3

Working on creating my own programming language, which notably supports pointers and constants, I wonder how constants are stored in memories in languages such as C? I've read on StackOverflow that they were stored in read-only memories at runtime, but I don't understand how this could be possible since the following code compiles and executes well:

#include <stdio.h>

int main (int argc, char ** argv) {
  const int x = 1;

  int *y;
  y = &x;

  *y += 1;

  printf("x = %d\n", x); // Prints: 2
  printf("y = %d\n", *y); // Prints: 2

  return 0;
}

Here, I define a constant called x and make a pointer from it so I can modify its value. This means x cannot be stored in a read-only memory.

So, I really wonder how constants are stored at runtime?

ClementNerma
  • 1,079
  • 1
  • 11
  • 16
  • 4
    They're stored in RAM. On a PC, it doesn't store it in flash or anything like that. The `const` tells the compiler you intend not to change it. However, if you "cheat" and use a pointer you can change it, but I'll bet you saw a warning to that effect when you compiled. The warning you get with `gcc` is, *warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] y = &x;* So the `const` is ignored in that case. – lurker Apr 08 '18 at 14:50
  • 1
    You showed the source code for a program, but that proves nothing. Did you compile and run it? If so, and it ran, that would only prove the **specific implementation** you compiled and ran it on does not store `x` in read-only memory in **this instance** (and making certain assumptions about how the C model of computation was implemented). – Eric Postpischil Apr 08 '18 at 14:50
  • 1
    keyword `const` don't mean constant but read only... yeah ok it's miss leading. `warning: assigning to 'int *' from 'const int *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]` when I compile your code... standard doesn't not describe this kind of specification, compiler are free to do what they like. – Stargateur Apr 08 '18 at 14:51
  • Possible duplicate of [Where are constant variables stored in C?](https://stackoverflow.com/questions/1576489/where-are-constant-variables-stored-in-c) – Raviprakash Apr 08 '18 at 14:53
  • 1
    depends on the level of optimization. with -O0 gcc puts it on stack. – Serge Apr 08 '18 at 15:00
  • @Raviprakash not a duplicate, I opened this subject specifically because I didn't agree with the answer in the one you linked to. Here I tried to understand how constants could be stored in a read-only memory while we can still modify it. – ClementNerma Apr 08 '18 at 15:10
  • 1
    This is not a valid C program and it [does not compile well for any reasonable definition of "well"](https://ideone.com/fvUBds). – n. m. could be an AI Apr 08 '18 at 15:17
  • It could if he cheated a little more with `int *y = (void*)&x;` – David C. Rankin Apr 08 '18 at 15:25

4 Answers4

5

Generally, especially when optimization is turned on, a compiler will attempt to make constants as efficient as possible. For example, if you write x = 3*4 + 5, the compiler would reduce this to 17 at compile time rather than putting 3, 4, and 5 in the compiled program.

Small constants that are used directly are often encoded into immediate fields of instructions.

If the compiler cannot put a constant into the immediate field of an instruction, it will generally seek to store it in read-only memory.

However, the example you give makes that difficult for the compiler. The code in your example:

  • Defines a const object inside a routine (as opposed to at file scope).
  • Takes the address of the object.

If you merely defined a const object and never took its address, the compiler could store the constant in the read-only data section.

However, since you take the address of the object, there is a problem. Routines can be called recursively. (Your program does not call main recursively, but the compiler is designed to support recursive calls, so the issues discussed here apply to its design.) Whenever a routine is called recursively, new instances of the objects defined in it must be created (in the C model of computation). If the address of a const object were not taken, the compiler could optimize this by using the same read-only memory for all instances of the object—since their values never change, nobody could tell they were just one instance instead of multiple copies.

However, different instances of an object can be distinguished by their addresses. Since you take the address of the object, the compiler wants to create actual different instances of it. That is difficult to do in read-only memory. Programs do not normally maintain a stack for read-only memory, so the compiler does not have a convenient way to track the multiple instances that would have to be created for objects in read-only memory. (It would be difficult to maintain a stack for read-only memory. If what is on the stack can be different at different times, then the memory for that stack has to change. So, even if only read-only objects are on a stack, the stack itself cannot be read-only.)

So, in this case, the compiler places your const object on the regular stack.

Of course, that is not a behavior you may rely on. Attempting to change the value of an object that is defined const has behavior not defined by the C standard. Even though it appears to “work” in this case, it is possible that, in more complicated programs, the compiler might transform your program with various optimizations with the result that your program would fail when trying to modify a const object like this. For example, printf("%d", *y) could print “2” because the memory y points to has been changed to 2, while printf("%d", x) could print “1” because x is known (in the C model of computation) to be a constant 1.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
2

You have discovered a sad fact about undefined behavior in C.

"Compiles and executes well" does not (not not not) prove that the code is legal and correct!

If you cheat, and try to write to a const-qualified location, anything can happen: you can get an error message, it can seem to work, or quietly do something almost or completely unlike what you expected.

When you said

y = &x;

your compiler should have warned you, something along the lines of "warning: assigning to 'int *' from 'const int *' discards qualifiers". (That's exactly what my compiler said.)

So, yes, a compiler is perfectly within its rights to store unmodifiable constants in the text segment, or some other section of read-only memory. If that means programs like yours fail, that's perfectly fine.

But there's one more point. The "constant" you declared, const int x = 1, was a local variable. So there needs to be a new instance of it for each call to the function, meaning that it's likely to be stored on some kind of a stack. So that means the compiler probably isn't going to put x in read-only memory, because I've never heard of read-only memory on the stack.

If you make x a global variable, by declaring it outside of main(), or if you put static in front of it, it won't get stored on any stack, and it's much more likely to be stored in read-only memory. In fact, when I make either of those two changes to your code, not only do I get the same warning when I compile it, but when I run the program, it crashes with "Bus error".

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
1

AFAIK, how they are stored is an implementation detail depending on the compiler. However, it is generally stored in text section.

By the way, const just apply you to be warned by the compiler if the const variable is wanted to change.

  • Does that mean that the code I wrote could crash depending on the platform and/or compiler? – ClementNerma Apr 08 '18 at 14:55
  • 2
    @ClementNerma: Absolutely. – Eric Postpischil Apr 08 '18 at 14:56
  • 1
    @ClementNerma if you were writing this code on an embedded system, it's possible the assumption of the compiler (depending upon the tool) would assume that the value is in ROM. Your program could crash, or it may simply malfunction due to it not doing what you think you told it to do. – lurker Apr 08 '18 at 14:59
  • To clarify, the "text section" is where the executable code is. Using "text" that way is rooted in computer science a long time ago. – nicomp Apr 08 '18 at 15:07
0

A const object in C is a variable the compiler doesn't allow assignments to. As such, it can be applied the address operator to get its address, or can be used as if it where a variable (or even an lvalue ---of course, of type const) but you cannot modify it (at least with normal assignment procedures). The compiler is allowed (for static consts) to store them in read-only memory, but that's not imposed by the standard. This is the quick answer. For a more detailed, I refer you to the standard.

In the case you post, when you use the &x expression, you get a const int* value, that is automatically converted to a int * pointer (probably you are getting a warning for that and that warning is being ignored)

$ make pru
cc -O -pipe  pru.c  -o pru
pru.c:7:5: warning: assigning to 'int *' from 'const int *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
  y = &x;
    ^ ~~
1 warning generated.

For legacy code compatibility reasons, this is treated as a warning and not an error, so you are free to act in consequence (you haven't done this in this case :) )

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31