I had to share this:
I got hung up for 2 full days on the following trivial error involving the Conditional Operator.
It’s an easy correction, but I would like to know:
- Why did the buggy code compile?
- What was the bug doing?
- Why it was so damn hard to track down?
buggy code:
std::map<int, some_class> my_map;
int key_ctr = 0;
//...
std::map<int, some_class>::iterator it_next =
key_ctr == 0 ?
it_next = my_map.begin() // BUG!!!
:
it_next = --my_map.end(); // BUG!!!!
// .....
Clearly, I wrote the Conditional Operator incorrectly. Eveyrthing works totally fine when I finally found and corrected this bug:
correct code:
std::map<int, some_class> my_map;
int key_ctr = 0;
//...
std::map<int, some_class>::iterator it_next =
key_ctr == 0 ?
my_map.begin() // CORRECTED!
:
--my_map.end(); // CORRECTED!
My program was just hanging when it got near the buggy part – as if it were in an infinite loop. When I ran it with valgrind, I got stuff like
....
==24570== Warning: set address range perms: large range [0x1a7731000, 0x1c5f79000) (defined)
==24570== Warning: set address range perms: large range [0x1c5f79000, 0x1e47c1000) (defined)
==24570== Warning: set address range perms: large range [0x1e47c1000, 0x203009000) (defined)
==24570== Warning: set address range perms: large range [0x203009000, 0x221851000) (defined)
.....
==3733== More than 10000000 total errors detected. I'm not reporting any more.
Which was totally unhelpful and pointed me in the wrong director (I thought I was allocating too much on the heap, somehow).
Again,
- Why did the buggy code compile?
- What was the bug doing?
- Why it was so damn hard to track down?
Thanks kids.
1) The compiler only checks for syntax and a well-formed program. It’s up to you to spot the logical bugs.
2) It’s undefined behavior. And here’s why:
Actually, you can narrow it down to:
it doesn’t really matter what whatever is. What matters is that until the full statement executes (
;is reached),it_nextis uninitialized. That’s what thepart attempts to do. But first, it attempts to evaluate what’s on the right hand side. Which is
it_next = whatever. Which callsit_next.operator = (whatever). So you’re calling a member function on an un-initialized object. Which is undefined behavior. Ta-da!!!3) All undefined behavior is hard to track down. That’s why you should at least be aware of common situations.