Compilers are allowed to optimize -and will try to optimize with the right flags- your code as much as possible. This includes eliminating your const int
s. Here's why, and how that works:
The as-if rule
[...] Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.6
[...]
6) This provision is sometimes called the “as-if” rule, because an implementation is free to disregard any requirement of this document as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program. [...]
- intro.abstract §1
Or in plain words: if your code's behavior is the unchanged, the compiler can do anything it wants with it to make it faster.
Also see: What exactly is the "as-if" rule?
Compiler optimizations for your example
const int a = 1;
const int b = 2;
const int c = 3;
const int d = 4;
func(a, b, c, d);
Each of the four variables declares an object, and objects occupy memory.
Intuition would tells us that the stack needs to be 4 * sizeof(int)
bytes larger to store all of them.
However, we're not using any of these objects' addresses, and we're just calling a function with them, so the following code:
void func(int a, int b, int c, int d);
void foo() {
const int a = 1;
const int b = 2;
const int c = 3;
const int d = 4;
func(a, b, c, d);
}
compiles to: (see CE example using GCC 13 with -O2
)
foo():
mov ecx, 4
mov edx, 3
mov esi, 2
mov edi, 1
jmp func(int, int, int, int)
Note: with fewer or no optimizations (e.g. -O0
), you would get much different assembly, with all four int
s residing on the stack.
We get the same assembly as if we had written:
func(1, 2, 3, 4);
So it's as if we never declared these objects and never occupied memory, but simply used temporary objects 1
, 2
, 3
, 4
that have no address.
This optimization is allowed, because the observable behaviour is still the same: We're still calling func
with the same arguments (passed by register, because of our calling conventions).
In general, I recommend using Compiler Explorer to see what kind assembly the compiler generates.
It can help you understand many optimizations (or missed optimizations).