9

In Item2 on page 16, (Prefer consts, enums, and inlines to #defines), Scott says:

Also, though good compilers won't set aside storage for const objects of integer types...

I don't understand this. If I define a const object, eg

const int myval = 5;

then surely the compiler must set aside some memory (of int size) to store the value 5?

Or is const data stored in some special way?

This is more a question of computer storage I suppose. Basically, how does the computer store const objects so that no storage is set aside?

1
  • 3
    You should provide a clear title. for ex storage of const object the source of your question has little value. Commented Apr 2, 2012 at 10:38

6 Answers 6

7

Also, though good compilers won't set aside storage for const objects of integer types...

A slightly more correct statement would be that compilers wouldn't set aside data memory for const objects of integer type: they would trade it for program memory. There is no difference between the two under Von Neumann architecture, but in other architectures, such as Harvard, the distinction is rather important.

To fully understand what's going on, you need to recall how assembly language loads data for processing. There are two fundamental ways to load the data - read a memory from a specific location (so called direct addressing mode), or set a constant specified as part of the instruction itself (so called immediate addressing mode). When compiler sees a const int x = 5 declaration followed by int a = x+x, it has two options:

  • Put 5 in a data memory, as if it were a variable, and generate direct load instructions; treat writes to x as errors
  • Each time the x is referenced, generate an immediate load instruction of the value 5

In the first case you will see a read from x into the accumulator register, an addition of the value at the location of x to accumulator, and a store into the location of a. In the second case you will see a load immediate of five, an add immediate of five, followed by a store into the location of a. Some compilers may figure out that you are adding a constant to itself, optimize a = x+x into a = 10, and generate a single instruction that stores ten at the location of a.

1
  • 1
    +1 for mentioning constant folding as well
    – jk.
    Commented Apr 2, 2012 at 10:40
10

Not necessarily. It can also decide to just use the raw value 5 instead of myval in the compiled code.

The difference between #define MYVAL 5 and const int myval = 5 is that in the former case, the compiler has no choice whatsoever, as the preprocessor already replaced all mentions of MYVAL in the source code with 5 by the time the compiler gets to see the source code. In the latter case though, there is a choice. For a non-optimized debug build, the compiler may explicitly allocate a const int, so in the debugger you will be able to see the constant myval, instead of just the raw value 5.

3
  • I understand the benefit of the const rather than define. But even with the const example, the value 5 must be stored in the executable somewhere?
    – user619818
    Commented Apr 1, 2012 at 20:39
  • 3
    @user619818, it will be stored within the code as an instruction parameter, not in the data segment as a regular variable / constant. Commented Apr 1, 2012 at 22:02
  • @user619818: The instruction will always have a parameter. So if separate storage is allocated, there is the value and the address of it in immediate argument of the instruction. On CPUs with fixed instruction size (like ARM) it gets even worse, because small constants like 5 fit into the 32-bit instruction itself, but address often does not and causes extra load address instruction to be emitted.
    – Jan Hudec
    Commented Apr 2, 2012 at 12:29
3

What the quote says is not quite correct.

A good compiler won't set aside storage for static const variables. If the const variable isn't static and is in file-scope, it must set aside storage because the variable could be referenced from another compilation unit. With link-time optimizations, the linker might be able to eliminate the storage and rewrite instructions that reference the variable if it can prove that the program doesn't generate a pointer to that variable.

A much better reason for using const int instead of #define is that debuggers don't "see" macros, so you can't inspect a #defined'd value in the debugger.

3

I'll steal the first sentence from Péter Török's answer but elaborate differently: Not necessarily. It can also decide to just use the raw value 5 instead of myval in the compiled code.

Treating myval like a regular variable by allocating space for it in memory can have performance implications that range from miniscule to severe depending on the architecture and how it handles memory.

Working that way, a compiler would emit an instruction that says something along the lines of "load register R with whatever is at the memory location for myval". The location of myval as an operand of the instruction, so it comes right out of the same block of data as the instruction itself. On modern CPUs, this value will be readily available on-chip because of instruction prefetch. With the address in hand, the CPU still has to get the value out of memory. That might go quickly if the location is nearby in cache or not so quickly if it isn't. Not only does the CPU have to go off-chip to get the value, doing so might force it to bump other, more useful data out of the cache that will have to be brought back in later. When the program is running under an OS that virtualizes memory, access to that location might cause a page fault, resulting in the program being put to sleep until the required page is brought into RAM via peripheral (e.g., disk) I/O, the program restarted via a context switch and the cache mechanism does whatever it's going to do with it.

By hard-wiring the constant value into the object code, the compiler would emit an instruction like "load register R with the value 5." Like the memory address described above, the 5 would be an operand to the instruction and available in the same way (i.e., prefetched). That's where the similarity ends, because the CPU now has everything it needs to put the 5 into register R and get on with its business. Since addresses and registers are usually the same size, there's no difference in the number of bytes the instruction occupies and the actual execution takes place with zero chance of the cache misses and page faults that can occur when you go fish something out of memory.

The compiler could, as Péter pointed out, allocate space and a symbol for myval in debug builds. There wouldn't be any harm in doing this and still hard-wiring its value, since the value remains the same no matter what and the symbol is really just there for us humans to use in debugging.

Note that this only applies to values that can be held in registers, because registers are integers by nature. Other constants will end up in memory.

1

The compiler will substitute the number five wherever the variable 'myval' is used.

0

The compiler may consider const values as immediate operands. Immediate operands do not require data storage. The compiler can treat:

int foo = myVal;

the same as

int foo = 5;

The value 5 is not stored in data memory, but rather as part of the instruction sequence.

The compiler must reserve data storage in cases where the address of a the value may be taken. Even in that case, the compiler will still use immediate operations when the value of myVal is used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.