Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

undefined behavior in conditional operator

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:

  1. Why did the buggy code compile?
  2. What was the bug doing?
  3. 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,

  1. Why did the buggy code compile?
  2. What was the bug doing?
  3. Why it was so damn hard to track down?

Thanks kids.

like image 408
cmo Avatar asked Dec 08 '22 20:12

cmo


1 Answers

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:


whatever_non_POD_type it_next = condition ? it_next = whatever1 : 
                                            it_next  = whatever2; 

Actually, you can narrow it down to:

It it_next = it_next = whatever;

it doesn't really matter what whatever is. What matters is that until the full statement executes (; is reached), it_next is uninitialized. That's what the

It it_next = ...

part attempts to do. But first, it attempts to evaluate what's on the right hand side. Which is it_next = whatever. Which calls it_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.

like image 86
Luchian Grigore Avatar answered Dec 25 '22 23:12

Luchian Grigore