views:

393

answers:

5

This is not academic code or a hypothetical quesiton. The original problem was converting code from HP11 to HP1123 Itanium. Basically it boils down to a compile error on HP1123 Itanium. It has me really scratching my head when reproducing it on Windows for study. I have stripped all but the most basic aspects... You may have to press control D to exit a console window if you run it as is:

#include "stdafx.h"
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    char blah[6];
    const int IAMCONST = 3;
    int *pTOCONST;
    pTOCONST = (int *)  &IAMCONST;
    (*pTOCONST) = 7;
    printf("IAMCONST %d \n",IAMCONST);
    printf("WHATISPOINTEDAT %d \n",(*pTOCONST));
    printf("Address of IAMCONST %x   pTOCONST %x\n",&IAMCONST, (pTOCONST));
    cin >> blah;    
    return 0;
}

Here is the output

IAMCONST 3
WHATISPOINTEDAT 7
Address of IAMCONST 35f9f0   pTOCONST 35f9f0

All I can say is what the heck? Is it undefined to do this? It is the most counterintuitive thing I have seen for such a simple example.

Update:

Indeed after searching for a while the Menu Debug >> Windows >> Disassembly had exactly the optimization that was described below.

    printf("IAMCONST %d \n",IAMCONST);
0024360E  mov         esi,esp 
00243610  push        3    
00243612  push        offset string "IAMCONST %d \n" (2458D0h) 
00243617  call        dword ptr [__imp__printf (248338h)] 
0024361D  add         esp,8 
00243620  cmp         esi,esp 
00243622  call        @ILT+325(__RTC_CheckEsp) (24114Ah)

Thank you all!

+6  A: 

The constant value IAMCONST is being inlined into the printf call.

What you're doing is at best wrong and in all likelihood is undefined by the C++ standard. My guess is that the C++ standard leaves the compiler free to inline a const primitive which is local to a function declaration. The reason being that the value should not be able to change.

Then again, it's C++ where should and can are very different words.

JaredPar
would running the precompiler be able to prove that?
ojblass
Nope. Looking at the (dis)assembly would, though.
ephemient
have you done that in visual studio 2008? I only recently bought it. I am a unix head learning my way around.
ojblass
just remove the 'const' keyword, as in ephemient's answer, that will be proof
ninesided
I can confirm that this is indeed Undefined Behavior. The compiler is allowed to put the const in read-only memory, which is why code like this was disallowed.
MSalters
+19  A: 

Looks like the compiler is optimizing

printf("IAMCONST %d \n",IAMCONST);

into

printf("IAMCONST %d \n",3);

since you said that IAMCONST is a const int.

But since you're taking the address of IAMCONST, it has to actually be located on the stack somewhere, and the constness can't be enforced, so the memory at that location (*pTOCONST) is mutable after all.

In short: you casted away the constness, don't do that. Poor, defenseless C...

Addendum

Using GCC for x86, with -O0 (no optimizations), the generated assembly

main:
    leal 4(%esp), %ecx
    andl $-16, %esp
    pushl -4(%ecx)
    pushl %ebp
    movl %esp, %ebp
    pushl %ecx
    subl $36, %esp
    movl $3, -12(%ebp)
    leal -12(%ebp), %eax
    movl %eax, -8(%ebp)
    movl -8(%ebp), %eax
    movl $7, (%eax)
    movl -12(%ebp), %eax
    movl %eax, 4(%esp)
    movl $.LC0, (%esp)
    call printf
    movl -8(%ebp), %eax
    movl (%eax), %eax
    movl %eax, 4(%esp)
    movl $.LC1, (%esp)
    call printf

copies from *(bp-12) on the stack to printf's arguments. However, using -O1 (as well as -Os, -O2, -O3, and other optimization levels),

main:
    leal 4(%esp), %ecx
    andl $-16, %esp
    pushl -4(%ecx)
    pushl %ebp
    movl %esp, %ebp
    pushl %ecx
    subl $20, %esp
    movl $3, 4(%esp)
    movl $.LC0, (%esp)
    call printf
    movl $7, 4(%esp)
    movl $.LC1, (%esp)
    call printf

you can clearly see that the constant 3 is used instead.

If you are using Visual Studio's CL.EXE, /Od disables optimization. This varies from compiler to compiler.

Be warned that the C specification allows the C compiler to assume that the target of any int * pointer never overlaps the memory location of a const int, so you really shouldn't be doing this at all if you want predictable behavior.

ephemient
you are right... only can select one answer... +1
ojblass
How do you tell it to not optimize. I have a property window that says "No whole program Optimization". I am not above correct answer selecting based on follow through!
ojblass
"whole program optimization" is something completely different, optimizing across compilation units at link time. All non-trivial compilers will perform this very basic optimization within a single compilation unit.
ephemient
Fine follow through... I don't change votes lightly.
ojblass
+4  A: 

You are lucky the compiler is doing the optimization. An alternative treatment would be to place the const integer into read-only memory, whereupon trying to modify the value would cause a core dump.

Jonathan Leffler
I am not sure about lucky. I think I would chose core dump over side effects of what I am seeing.
ojblass
Indeed, if it were written `static const int c`, it would likely have been placed into a read-only segment of memory. No such protection is possible for objects on the stack.
ephemient
@ephemient: you're right, of course...though in theory a compiler could decide that the automatic constant 'variable' was actually stored in read-only memory without violating the C standard, I doubt if any actually do allocate such variables like that.
Jonathan Leffler
@Jonathan, that is a common idiom in compilers for embedded systems to that will force the static const object into (or near) the code segment where it can be left in ROM and never copied to RAM like the initialized data segment must be. Saves nothing for a single int, but for a large table...
RBerteig
@RBerteig: thanks for the information about embedded compilers. Makes sense. (I note that if you had the same value as a local constant int in multiple files, then you'd probably get some savings; and arrays...!)
Jonathan Leffler
@ephemient : there's nothing in the C++ standard which says that non-static const ints have to be on the stack. They can still be put in ROM.
MSalters
@MSalters : Of course, the standard says nothing about stacks and heaps anyways :)
ephemient
+2  A: 

Writing to a const object through a cast that removes the const is undefined behavior - so at the point where you do this:

(*pTOCONST) = 7;

all bets are off.

From the C++ standard 7.1.5.1 (The cv-qualifiers):

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

Because of this, the compiler is free to assume that the value of IAMCONST will not change, so it can optimize away the access to the actual storage. In fact, if the address of the const object is never taken, the compiler may eliminate the storage for the object altogether.

Also note that (again in 7.1.5.1):

A variable of non-volatile const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions (5.19).

Which means IAMCONST can be used in compile-time constant expressions (ie., to provide a value for an enumeration or the size of an array). What would it even mean to change that at runtime?

Michael Burr
A: 

It doesn't matter if the compiler optimizes or not. You asked for trouble and you're lucky you got the trouble yourself instead of waiting for customers to report it to you.

"All I can say is what the heck? Is it undefined to do this? It is the most counterintuitive thing I have seen for such a simple example."

If you really believe that then you need to switch to a language you can understand, or change professions. For the sake of yourself and your customers, stop using C or C++ or C#.

const int IAMCONST = 3;

You said it.

int *pTOCONST;
pTOCONST = (int *)  &IAMCONST;

Guess why the compiler complained if you omitted your evil cast. The compiler might have been telling the truth before you lied to it.

"Does the evil cast get trumped by the evil compiler?"

No. The evil cast gets trumped by itself. Whether or not your compiler tried to tell you the truth, the compiler was not evil.

Windows programmer
Only one downvote so far? Come on people, don't you need more undefined code in control of phones and cars?
Windows programmer
I think he is right. The guy who wrote the code on HP11 years ago did a really bad thing. The thing that got my eyes crossed was that the address of the variable was correct but what it was pointing at wasnt what I think I thought I saw it point to. Either way its crap code.
ojblass
I am going to downvote it also, for telling the questioner to stop programming in C.
Zan Lynx
"for telling the questioner to stop programming in C" -- Even when the questioner said "I think he is right" in the comment just ahead of yours. But that's OK, Zan Lynx, maybe someday you get to drive a car controlled by randomly behaved programs, written by programmers who didn't understand the programming languages they were using.
Windows programmer