04 January 2010

C++ Continues to Surprise

Someone was asking questions about const_cast<>() a few days ago. I was not quite sure how it would work because I try to use as little of the C++ language as possible and it possible to get  by in C++ without const_cast<>().  To find out exactly how it worked I tried it out with a test case. The following code gave the same output on g++ on Vista and OS X.


int i = 3;
const int* ptr = &i;
*const_cast<int*>(ptr) = 11;
if (&i == ptr && i != *ptr) {
     std::cout << "Cannot happen: &i=" << &i << " == ptr=" << ptr << " but i=" << i << " != *ptr=" << *ptr << std::endl;

}
The output in both cases was Cannot happen: &i=0x22fe6c == ptr=0x22fe6c but i=3 != *ptr=22

How can a single memory address hold two different values?

The disassembly was

push  %ebp
mov   %esp,%ebp
sub   $0x18,%esp
int i = 3;
movl  $0x3,0xfffffffc(%ebp)            (i in bp-4)
const int* ptr = &i;
lea   0xfffffffc(%ebp),%eax            (&i in eax)
mov   %eax,0xfffffff8(%ebp)            (ptr in bp-8)
*const_cast<int*>(ptr) = 11;
mov   0xfffffff8(%ebp),%eax            (ptr in eax)
movl  $0xb,(%eax)                      (*ptr set to 11)
if (&i == ptr && i != *ptr)
lea   0xfffffffc(%ebp),%eax            
cmp   0xfffffff8(%ebp),%eax
jne   0x403214 
mov   0xfffffff8(%ebp),%eax 
mov   (%eax),%eax
cmp   0xfffffffc(%ebp),%eax
je    0x403214

The disassembly matches the C++ code. i is stored at bp-4 and ptr is stored at bp-8 so the C++ code should work. The observed behaviour does not match the disassembly.

This cannot be right. I guess I found a bug in g++.

4 comments:

Andy said...

Interesting. What versions of g++ and the standard library are you using?

I compiled and ran the following:

#include <iostream>

int main(int argc, char** argv)
{
int i = 3;
const int* ptr = &i;
*const_cast<int*>(ptr) = 11;
if (&i == ptr && i != *ptr) {
std::cout << "Cannot happen: &i=" << &i << " == ptr=" << ptr << " but i=" << i << " != *ptr=" << *ptr << std::endl;

}
else
{
std::cout << "i = " << i << std::endl;
}
}

and got the output "i = 11".

This was on OSX 10.6.2, using g++ 4.2.1

Peter Williams said...

Andy

I am using g++ 4.0.1 on OS X 10.5.8

However I just built and ran this code from the command line and it worked correctly.

The bug I was seeing must have been in Eclipse CDT which I had used to build and run the program on OS X and Vista.

Vladislav Gubarev said...

user@ws:~/tmp/const_cast$ uname -a
Linux ws 2.6.28-13-generic #45-Ubuntu SMP Tue Jun 30 22:12:12 UTC 2009 x86_64 GNU/Linux

user@ws:~/tmp/const_cast$ g++ --version
g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

user@ws:~/tmp/const_cast$ cat main.cpp
#include <iostream>

int main()
{
int i = 3;
const int* ptr = &i;
*const_cast(ptr) = 11;
if (&i == ptr && i != *ptr)
std::cout << "Cannot happen: &i=" << &i << " == ptr=" << ptr << " but i=" << i << " != *ptr=" << *ptr << std::endl;
else
std::cout << "OK" << std::endl;

return 0;
}

user@ws:~/tmp/const_cast$ g++ main.cpp -o main
user@ws:~/tmp/const_cast$ ./main
OK

Peter Williams said...

Vlad

Thanks for double checking the code.

See my previous comment. The problem is in Eclipse CDT or gdb.

How is the image recognition work?

-Peter