-5

I realized, what if i define const int into the body of c++ function and then use the address arithmetic to change the value of the constant( its on the stack, isn't it? ). i got this code:

const int a = 10;
int b = 100;

int * c = &b;
c++;

cout << "&a: " << &a << endl;
cout << " c: " << c << endl;


*c = 100500;

cout << " a: " << a << endl;
cout << "*c: " << *c << endl;

and i got this output

&a: 0x7ffff63866a8
c: 0x7ffff63866a8

 a: 10
*c: 100500

So, addresses are the same, but values are different. Can someone explain me this result? Thanks! p.s. i tried on GCC, Clang and VS

  • 1
    changing const by anyways is Undefined behaviour in standards. – Grijesh Chauhan Sep 23 '13 at 17:27
  • You have just invoked undefined behaviour. – Oliver Charlesworth Sep 23 '13 at 17:27
  • The value of `c` post-increment is not defined behavior, neither in value nor dereference. Were `b` an array (even an array of one) the value would be defined, but the dereference would still not be (in the one-length case). – WhozCraig Sep 23 '13 at 17:28
  • Doesn't const just tell the compiler to try its best from letting you do something stupid (I.e. Modify something that you define as const)? The compiler has no way of knowing that the address you're telling to modify is const, an int or a float or a struct or whatever. This may also be UB, I think I remember reading somewhere that const can be placed in read-only memory at the discretion of the compiler. – George Mitchell Sep 23 '13 at 17:29
  • @WhozCraig: I think the value is valid; it's one-past-the-end-of-an-array (when a non-array behaves like an array of length 1). – Oliver Charlesworth Sep 23 '13 at 17:29
  • 1
    @OliCharlesworth it isn't. I had a rather interesting discussion about this about a year ago when I proposed sending a singleton through an iterator-required parameter list using `&c` and `&c+1` for the boundaries (effectively.. I used a pointer the same as the OP does here). I'd have to dust it off to find it, but the ultimately the standard was referenced to last+one as only valid for an array type. I was surprised, as you can imagine. – WhozCraig Sep 23 '13 at 17:32
  • @WhozCraig: I'd be interested if you could find that! C99 (I guess C++ could be defined differently) explicitly states (6.5.6 p7) "*For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.*" – Oliver Charlesworth Sep 23 '13 at 17:34
  • @WhozCraig: Indeed, the C++ standard has the same wording ([expr.add] p4 in whichever version I have here right now). – Oliver Charlesworth Sep 23 '13 at 17:35
  • @OliCharlesworth I'll have to do some digging. As I said it was a long time ago. But I'm most-definitely writing the section *you* just cited down. I was quite-thrased for that proposed solution, and rather than fighting it in elongated fashion (was token-at-best) I just licked my wounds and went quietly into that good night. I feel inspired now =P – WhozCraig Sep 23 '13 at 17:36
  • "My code has a bug. Why doesn't it do what I expect?" – David Schwartz Sep 23 '13 at 17:44
  • @OliCharlesworth I couldn't find the specific question, but apparently it comes up often, and opinions are like... well, everyone has one. [This question](http://stackoverflow.com/questions/3387021/alternate-way-of-computing-size-of-a-type-using-pointer-arithmetic), and [this question](http://stackoverflow.com/questions/3387557/size-of-struct-without-use-sizeof-keyword) were particularly interesting. Note the people *commenting* in *both* and the apparent conflicting self-opinions. After seeing your cite, however, I side with you on this. The value should be valid, the deref not (obviously). – WhozCraig Sep 23 '13 at 17:46
  • 1
    @OliCharlesworth ... but I'll keep digging. Since I dropped my answer from the original question after the shellacking I will only be able to find it by-comment, and we know what a joy *that* is. – WhozCraig Sep 23 '13 at 17:48
  • @WhozCraig: Yes, I've seen this come up before too ;) I get the impression that this was something that wasn't made explicit in earlier versions of the C or C++ standards, which might explain the differing opinions that people have. – Oliver Charlesworth Sep 23 '13 at 17:49

6 Answers6

5

Can someone explain me this result?

As with any attempt to access objects in invalid ways via invalid pointer arithmetic or other shenanigans: undefined behaviour.

What is happening here is that the compiler is assuming that the value of a won't change, and optimising it to be a compile-time constant. It's allowed to do that, since you've declared it const and thereby stated that it won't change.

its on the stack, isn't it?

Since you also take the address of it (when you print &a), it does get allocated an address on the stack; but there's no need for the program to read from that address to get the value, since it's already known to be 10.

Of course, this is all undefined, so the program would be just as valid if it ordered you a pizza instead.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Absolutely correct. Two key points: 1) a "const" value does *NOT* need to be stored on the stack: the compiler can do whatever it wants with it, 2) "undefined behavior" == "just as valid if it ordered you a pizza instead". Great response :) – paulsm4 Sep 23 '13 at 17:47
  • +1 for the "ordered you a pizza" ... I want mine with pepperoni! – Zac Howland Sep 23 '13 at 17:56
3

Your program has undefined behavior written all over it...

The assumption that incrementing the address of b will get you to a is bogus, it could or it could not. You are then using what is called in the standard unsafely derived pointer to modify a const object (a) which is also undefined behavior. Anything can happen.

What really happens (in your implementation, explanation of your results but you cannot depend on this as this is undefined behavior) is that you forced the allocation of a in the stack by means of taking its address, and you got a pointer into it. You modified that value, and the address in memory is updated. But, in the expression: cout << " a: " << a << endl; the compiler knows that a is a constant, and thus its value can only be 10, so it transformed the code into cout << " a: " << 10 << endl; to avoid having to go to memory to obtain the value.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
2

its on the stack, isn't it?

No, your expectations are wrong. C++ has no notion of stack whatsoever, much less of how different automatic variables are stored in memory relative to each other. What you are trying to do is plain Undefined Behaviour.

In your very case, the compilers optimize a away, because they are allowed to by the standard, and the results you're getting don't have to make any sense since it's UB anyway.

syam
  • 14,701
  • 3
  • 41
  • 65
2
const int a = 10;
int b = 100;

OK.

int * c = &b;

OK, but silly and my bug-o-meter is starting to twiddle.

c++;

Bug-o-meter now in the yellow zone.

cout << "&a: " << &a << endl;

OK

*c = 100500;

Bug-o-meter pegged. Undefined Behavior invoked. World explodes, you get fired.

c++ moves the pointer to the next int in memory. The pointer math is OK, but all you can use c for at this point is to compare the pointer to another pointer. You can't dereference the pointer in any way. But that's what you do when you try to assign through it.

What happens next is irrelevant and, honestly, misleading. You think that c now points to the same memory as b, and maybe it does. But it's only through Undefined Behavior that this happened. You might also think that the value of *c is what you expect it to be, but this result is false. Maybe it is, maybe it isn't. You shattered the vial when you opened the box -- the cat is dead. And so is your program.

And by the way, if what you're trying to do is find a way to cheat the compiler so that you can change a const, don't -- it is strictly forbidden to change a const by any means.

There is a const_cast in C++, but that is also not a mechanism that you can use to change a const.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
0

The C++ compiler will simply assume that you will never try to change the value of a const variable.

This doesn't mean that if you do you will get an error... just that the compiler authors can ignore what is going to happen and anything that happens will be classified as "your fault".

6502
  • 112,025
  • 15
  • 165
  • 265
0

SSCC:

#include <stdio.h>

int main ()
{
  const int a = 10;
  int b = 100;
  int *c = &b;
  printf ("a=%d, b=%d, *c=%d; &a=%p, &b=%p, c=%p\n",
    a, b, *c, (void *)&a, (void *)&b, (void *)c);

  c++;  // "c" now invalid
  printf ("a=%d, b=%d, *c=%d; &a=%p, &b=%p, c=%p\n",
    a, b, *c, (void *)&a, (void *)&b, (void *)c);

  *c = 100500;  // Undefined behavior!
  printf ("a=%d, b=%d, *c=%d; &a=%p, &b=%p, c=%p\n",
    a, b, *c, (void *)&a, (void *)&b, (void *)c);

  return 0;
}

EXAMPLE OUTPUT:

a=10, b=100, *c=100; &a=0028FF18, &b=0028FF14, c=0028FF14
a=10, b=100, *c=10; &a=0028FF18, &b=0028FF14, c=0028FF18
a=10, b=100, *c=100500; &a=0028FF18, &b=0028FF14, c=0028FF18

CASE 2 - WE DON'T TRY TO TAKE ADDRESSOF CONST A:

#include <stdio.h>

int main ()
{
  const int a = 10;
  int b = 100;
  int *c = &b;
  printf ("a=%d, b=%d, *c=%d; &b=%p, c=%p\n",
    a, b, *c, (void *)&b, (void *)c);

  c++;  // "c" now invalid
  printf ("a=%d, b=%d, *c=%d; &b=%p, c=%p\n",
    a, b, *c, (void *)&b, (void *)c);

  *c = 100500;  // Undefined behavior!
  printf ("a=%d, b=%d, *c=%d; &b=%p, c=%p\n",
    a, b, *c, (void *)&b, (void *)c);

  return 0;
}

SAMPLE OUTPUT

a=10, b=100, *c=100; &b=0028FF14, c=0028FF14
a=10, b=100, *c=2686744; &b=0028FF14, c=0028FF18
a=10, b=100, *c=0; &b=0028FF14, c=00018894
paulsm4
  • 114,292
  • 17
  • 138
  • 190