6

I wrote some thing similar to this in my code

const int x=1;
int *ptr;
ptr = &x;
*ptr = 2;

Does this work on all compilers? Why doesn't the GCC compiler notice that we are changing a constant variable?

rra
  • 3,807
  • 18
  • 29
Adi
  • 729
  • 2
  • 6
  • 13
  • 1
    It does. You should get a message like `warning: assignment discards qualifiers from pointer target type`, referring to the line `ptr = &x;`. – Oliver Charlesworth Mar 22 '13 at 17:03
  • 1
    This has some info too: http://stackoverflow.com/questions/945640/constants-and-pointers-in-c?lq=1 – Memento Mori Mar 22 '13 at 17:04
  • 1
    Hypothetically, a compiler could put a `const int` into read-only memory. Then `*ptr = 2;` would crash. De facto, don't expect it to crash. Oh, and the compiler will complain about discarding the qualifier on `ptr = &x;` if you let it. – Daniel Fischer Mar 22 '13 at 17:06
  • My compiler declares an error `ptr = &x;` incompatible types 'int *' and 'const int * –  Mar 22 '13 at 17:29
  • I have ran this code on GCC compiler i got a warning and no run time error or compilation error. – Adi Mar 22 '13 at 17:35
  • 1
    @BillHicks And I once was stabbed with a knife but didn't die... does that prove that stabbings aren't fatal? – Nik Bougalis Mar 22 '13 at 17:36
  • In C, `const type var` says the variable won't be changed _here_, it could be changed _elsewhere_. I.e., you can do `int i; void f(const int); f(i);`. In the above, `var` is still a variable. In C++ it is a bona-fide constant (probably what @Armin is doing). – vonbrand Mar 22 '13 at 21:47
  • Have any of the answers below helped? – Neil Townsend Mar 22 '13 at 23:52

4 Answers4

10

const actually doesn't mean "constant". Something that's "constant" in C has a value that's determined at compile time; a literal 42 is an example. The const keyword really means read-only. Consider, for example:

const int r = rand();

The value of r is not determined until program execution time, but the const keyword means that you're not permitted to modify r after it's been initialized.

In your code:

const int x=1;
int *ptr;
ptr = &x;
*ptr = 2;

the assignment ptr = &x; is a constraint violation, meaning that a conforming compiler is required to complain about it; you can't legally assign a const int* (pointer to const int) value to a non-const int* object. If the compiler generates an executable (which it needn't do; it could just reject it), then the behavior is not defined by the C standard.

For example, the generated code might actually store the value 2 in x -- but then a later reference to x might yield the value 1, because the compiler knows that x can't have been modified after its initialization. And it knows that because you told it so, by defining x as const. If you lie to the compiler, the consequences can be arbitrarily bad.

Actually, the worst thing that can happen is that the program behaves as you expect it to; that means you have a bug that's very difficult to detect. (But the diagnostic you should have gotten will have been a large clue.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Standard reference: C11 6.5.4/3 – M.M Aug 08 '16 at 04:51
  • [This compiler](https://www.onlinegdb.com/online_c++_compiler) allows such a statement: `int *ptr = (int*)&x;` but not `int *ptr = &x;`. Additionally, `printf("%d %d",x,*ptr);` outputs the result `1 2` while `printf("%p %p",&x, ptr);` outputs the same address for both `&x` and `ptr` (something like 0x7ffe6a1aa47c). Is that counted as mistake/ bug or does it serve a purpose? Is that behavior language-related or compiler-related? – Xfce4 Jan 07 '22 at 00:12
  • 1
    @Xfce4 I'd count `int *ptr = (int*)&x;` as a mistake by the programmer. I'd expect most C compilers to accept it without complaint (no diagnostic is required). The cast means that the conversion is not a constraint violation, but it does have undefined behavior. Given `const int x = 1;`, the compiler may legitimately assume that `x` continues to have the value `1`, and need not generate code to read it again. If you go behind the compiler's back to write `2` to that memory location, you've lied to the compiler and it will get its revenge. – Keith Thompson Jan 07 '22 at 00:13
  • Thank you for your answer :) I was about to ask how it could be possible that the same address yields two different results for `x` and `*ptr`, but trying `printf("%d %d %d\n",x,*(&x),*ptr);` gave the output `1 2 2`. So it looks like `x` is different than `*(&x)`, which implies -to me- that the values and addresses are stored, linked and/or called in a 'not-straightforward' way. – Xfce4 Jan 07 '22 at 00:26
  • 1
    @Xfce4 The values and addresses are stored straightforwardly. What's not straightforward is that the compiler knows that `x` was defined with `const`, so it assumes its value cannot change, so it replaces the reference to `x` with the equivalent of a literal `1`. This is a perfectly valid optimization. It gives unexpected results here because the programmer cheated. – Keith Thompson Jan 07 '22 at 19:47
  • @KeithThompson Oh I was assuming that the values `1 2 2` were decided at runtime. So `x` was replaced by `1` at "compile-time" for optimization? It makes sense now. As a side question, is it possible for the compiler to replace `*(&x)` and `*ptr` in a similar way for optimization or are pointers exempt from that, i.e. the corresponding values to the pointers must be calculated/ obtained at runtime? – Xfce4 Jan 08 '22 at 03:13
  • @Xfce4 Pointer objects are in most ways like any other objects. If a pointer object is defined with `const`, the compiler can assume that its value is unchanged. If the program cheats and modifies the object's value, the behavior is undefined. (So don't do that.) – Keith Thompson Jan 08 '22 at 22:17
  • @KeithThompson I meant if it would be possible for the compiler to determine that a value pointed by the pointer won't be changed, and as a result a statement like `*(&x)` would be replaced by a constant value at "compile-time" rather than runtime. – Xfce4 Jan 08 '22 at 22:48
  • @Xfce4 To be clear, pointers point to objects, not to values. If a compiler can determine that the object pointed to by a pointer is defined with `const`, then it can assume the value is unchanged. Given `const int x = 42;`, a compiler can replace `*&x` by a constant `42`. (Whether it actually does so depends on how clever the compiler is.) – Keith Thompson Jan 09 '22 at 00:25
  • @KeithThompson Thank you. Is it possible for a compiler to do the same when `const` keyword is not used, but the pointed object's value does not change at all? – Xfce4 Jan 09 '22 at 00:37
  • @Xfce4 Sure. A compiler can replace `{ int x = 42; printf("%d\n", x); }` by `puts("42")` -- as long as it can prove that the value of `x` is unchanged (given the assumption that the code has defined behavior). An implementation is required to produce the specified behavior. The standard doesn't care how it does so. – Keith Thompson Jan 09 '22 at 00:43
  • @KeithThompson Can more complicated expressions like `*(&x)` be replaced by the compiler at compile time, provided that they will not change even though `const` keyword is not used. Can compiler detect that? This is the ultimate question I was trying to ask. – Xfce4 Jan 09 '22 at 00:51
  • 1
    @Xfce4 I thought I answered that. The implementation's obligation is to produce the defined behavior of the program. Any code it generates is just a means to that end. (And if the program has undefined behavior, all bets are off.) – Keith Thompson Jan 09 '22 at 03:16
4

Online C 2011 draft:

6.7.3 Type qualifiers

...
6 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.133)
133) This applies to those objects that behave as if they were defined with qualified types, even if they are never actually defined as objects in the program (such as an object at a memory-mapped input/output address).

Emphasis added.

Since the behavior is left undefined, the compiler is not required to issue a diagnostic, nor is it required to halt translation. This would be difficult to catch in the general case; suppose you had a function like

void foo( int *p ) { *p = ...; }

defined in it's own separate translation unit. During translation, the compiler has no way of knowing if p could be pointing to a const-qualified object or not. If your call is something like

const int x;
foo( &x );

you may get a warning like parameter 1 of 'foo' discards qualifiers or something similarly illuminating.

Also note that the const qualifier doesn't necessarily mean that the associated variable will be stored in read-only memory, so it's possible the above code would "work" (update the value in x) in that you'd successfully update x by doing an end-run around the const semantics. But then you might as well just not declare x to be const.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • It would be good to also mention that OP's code is a constraint violation, before we get so far as 6.7.3/6. (the reference is C11 6.5.4/3) – M.M Aug 08 '16 at 04:51
-1

Bad programmer. No Moon Pie!

If you need to modify a const, copy it to a non-const variable and then work with it. It's const for a reason. Trying to "sneak" around a const can cause serious runtime issues. i.e. the optimizer has likely used the value inline, etc.

const int x=1;
int non_const_x = x;
non_const_x = 2;
K Scott Piel
  • 4,320
  • 14
  • 19
-1

There is a good discussion of this here:Does the evil cast get trumped by the evil compiler?

I would expect gcc to compile this because:

  • ptr is allowed to point to x, otherwise reading it would be impossible, although as the comment below says it's not exactly brilliant code and the compiler should complain. Warning options will (I guess) affect whether or not it's actually warned about.
  • when you write to x, the compiler no longer "knows" that it is writing to a const, all this is in the coders hands in C. However, it does really know, so it may well warn you, depending on the warning options you've selected.

whether it works or not, however, will depend on how the code is compiled, the way const is implemented for the compile options selected and the target CPU and architecture. It may work. Or it may crash. Or you may write to a "random" bit of memory and cause (or not) some freaky effect. It's not a good coding strategy, but that wasn't your question :-)

Community
  • 1
  • 1
Neil Townsend
  • 6,024
  • 5
  • 35
  • 52
  • 4
    `ptr` **isn't** allowed to point to `x`; the compiler should (and does) complain. You would need `const int *ptr` for the assignment to be valid. – Oliver Charlesworth Mar 22 '13 at 17:17