2
const int a = 100;
int *p = &a;
*p = 99;
printf("value %d", a);

Above code compiles and I am able to update its value via pointer but code below didn't output anything.

static const int a = 100;
int *p = &a;
*p = 99;
printf("value %d", a);

Can anyone explain this thing.

Ans Hafeez
  • 31
  • 4
  • I run the code on CentOs8 + gcc. it failed to compile. invalid conversion from ‘const int*’ to ‘int*. then I add the parameter -fpermissive, it succeed with a waning. and it will crash when running. – Paul Yang Feb 26 '21 at 16:59

5 Answers5

4

Both code snippets are invalid C. During initialization/assignment, there's a requirement that "the type pointed to by the left has all the qualifiers of the type pointed to by the right" (c17 6.5.16.1). This means we can't assign a const int* to an int*.

"Above code compiles" Well, it doesn't - it does not compile cleanly. See What must a C compiler do when it finds an error?.

I would recommend you to block invalid C from compiling without errors by using (on gcc, clang, icc) -std=c11 -pedantic-errors.

Since the code is invalid C, it's undefined behavior and why it has a certain behavior is anyone's guess. Speculating about why you get one particular output from one case of undefined behavior to another isn't very meaningful. What is undefined behavior and how does it work? Instead focus on writing valid C code without bugs.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 2
    Nevertheless, the actual behavior of a compiler is rather curious and may warrant a useful explanation. :-) – oakad Feb 22 '21 at 08:56
  • 1
    @oakad That's the very nature of UB. You can dissect a certain use case sure, but then we need the exact compiler and OS. And there's not likely anything meaningful to learn from it. – Lundin Feb 22 '21 at 08:58
4

There are several things going on here:

  1. const does not mean "Put this variable in read-only memory or otherwise guarantee that any attempt to modify it will definitively result in an error message."
  2. What const does mean is "I promise not to try to modify this variable." (But you broke that promise in both code fragments.)
  3. Attempting to modify a const-qualified variable (i.e., breaking your promise) yields undefined behavior, which means that anything can happen, meaning that it might do what you want, or it might give you an error, or it might do what you don't want, or it might do something totally different.
  4. Compilers don't always complain about const violations. (Though a good compiler should really have complained about the ones here.)
  5. Some compilers are selective in their complaints. Sometimes you have to ask the compiler to warn about iffy things you've done.
  6. Some programmers are careless about ignoring warnings. Did your compiler give you any warnings when you compiled this?
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
3

The compiler should complain in both cases when you store the address of a const int into p, a pointer to modifiable int.

In the first snippet, a is defined as a local variable with automatic storage: although you define it as const, the processor does not prevent storing a value into it via a pointer. The behavior is undefined, but consistent with your expectations (a is assigned the value 99 and this value is printed, but the compiler could have assumed that the value of a cannot be changed, hence could have passed 100 directly to printf without reading the value of a).

In the second snippet, a is a global variable only accessible from within the current scope, but the compiler can place it in a read-only location, causing undefined behavior when you attempt to modify its value via the pointer. The program may terminate before evaluating the printf() statement. This is consistent with your observations.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

Briefly, modifying const-qualified static objects causes a trap and modifying a const-qualified automatic object does not because programs are able to place static objects in protected memory but automatic objects must be kept in writeable memory.

In common C implementations, a const-qualified static object is placed in a section of the program data that is marked read-only after it is loaded into memory. Attempting to modify this memory causes the processor to execute a trap, which results in the operating system terminating execution of the program.

In contrast, an object with automatic storage duration (one defined inside a function without static or other storage duration) cannot easily be put in a read-only program section. This is because automatic objects need to be allocated, initialized, and released during program execution, as the functions they are defined in are called and returned. So even though the object may be defined as const for the purposes of the C code, the program needs to be able to modify the memory actually used for it.

To achieve this, common C implementations put automatic objects on the hardware stack, and no attempt is made to mark the memory read-only. Then, if the program mistakenly attempts to modify a const-qualified automatic object, the hardware does not prevent it.

The C standard requires that the compiler issue a diagnostic message for the statement int *p = &a;, since it attempts to initialize a pointer to non-const with the address of a const-qualified type. When you ignore that message and execute the program anyway, the behavior is not defined by the C standard.

Also see this answer for explanation of why the program may behave as though a is not changed even after *p = 99; executes without trapping.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Traps only exist on certain systems, typically the kinds with high-end MMUs and virtual memory. On many forms of embedded systems, attempting to modify `.rodata` through the physical address will literally not do a thing, since it's stored in flash memory and you can't write to it unless you do so through a flash driver. – Lundin Feb 22 '21 at 14:15
  • @Lundin: Hence the qualification “In common C implementations.” The answer does not seek to explain everything everywhere in the universe; it answers OP’s request for explanation of the behavior they observed. – Eric Postpischil Feb 22 '21 at 14:44
0
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).
C 2011 Online Draft

If you declare a as const, you're making a promise to the compiler that the value of a should not change during its lifetime; if you try to assign a new value to a directly the compiler should at least issue a diagnostic. However, by trying to change a through a non-const pointer p you're breaking that promise, but you're doing it in such a way that the compiler can't necessarily detect it.

The resulting behavior is undefined - neither the compiler nor the runtime environment are required to handle the situation in any particular way. The code may work as expected, it may crash outright, it may appear to do nothing, it may corrupt other data. const-ness may be handled in different ways depending on the compiler, the platform, and the code.

The use of static changes how a is stored, and the interaction of static and const is likely what's leading to the different behavior. The static version of a is likely being stored in a different memory segment which may be read-only.

John Bode
  • 119,563
  • 19
  • 122
  • 198