简体   繁体   中英

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.

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.

3 Why it was so damn hard to track down?

Because you didn't have compiler warnings turned on?

$ g++ -std=c++0x -pedantic -Wall -Werror -g    m.cc   -o m
cc1plus: warnings being treated as errors
m.cc:10: error: operation on ‘it_next’ may be undefined
m.cc: In function ‘void __static_initialization_and_destruction_0(int, int)’:
m.cc:6: error: operation on ‘it_next’ may be undefined
make: *** [m] Error 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM