Đumić Branislav
Đumić Branislav

Reputation: 387

Mismatch in C compiler behavior

I wrote the following piece of code in order to test could it modify the constant c trough a pointer a. I have tested it in Clang, VC and GCC compilers and observed that with both VC and GCC code works as I would expect, it prints 6 to the standard output, while when I compile it with Clang the value is not getting modified, and 5 is printed to the standard output.

#include <stdio.h>

int main(void) {
  const int c = 5;
  int* a  = (int*) &c;
  *a = 6;
  printf("%d", c);
  return 0;
}

I am wondering is there any well known explanation for this, or it has to do with internals of the compilers and other stuff that would be hard to analyze. Thanks everyone in advance!

Upvotes: 2

Views: 126

Answers (2)

Yes. It is called undefined behaviour. C11 6.7.3p6:

  1. 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.

With undefined behaviour explained as:

  1. undefined behavior

    behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

  2. NOTE: Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

(emphasis mine)


Compiling without optimization enabled is the same as buying a Ferrari and always driving on the first gear only. What happens if you use gcc and compile with -O3?

With -O3 my GCC produces code that is equivalent to

#include <stdio.h>

int main(void) {
    printf("%d", 5);
    return 0;
}

How about this program then:

#include <stdio.h>

int main(void) {
    const char *foo = "Hello world!";
    char *bar = (char*)foo;
    bar[0] = 'C';
    printf("%s\n", foo);
}

Using my GCC and -O3 it crashes. But if you use Clang, it will print Hello world!... and if you look at the assembly Clang figured out that it can be optimized to

 int main(void) {
     puts("Hello world!");
 }

Upvotes: 8

Eric Postpischil
Eric Postpischil

Reputation: 224596

Common possibilities with this code include:

  • The compiler sees that const int c = 5; means the only defined value of c is 5 and therefore generates code for printf("%d", c); that prints “5” without loading the value of c from memory or checking anything else about c.
  • The compiler puts c on the stack. Then int* a = (int*) &c; takes the address of c. For *a = 6;, the compiler generates code that writes 6 to the location that a points to, which is where c is stored. Due to how the stack is used, it is not practical to make it or parts of it read-only, so the processor does not generate any exception for this code that writes 6 to a location that is defined const in C. Then the printf("%d", c); fetches the value of c from memory, gets 6, and prints “6”.

None of this is defined by the C standard of course, but these behaviors arise as consequences of how compilers, hardware, and operating systems are designed.

You may be more likely to see the former behavior with high levels of optimization and the latter with optimization turned off, but it can vary.

Upvotes: 3

Related Questions