Reputation: 3804
I was recently wondering what the difference between #define
and static const
is in C and why two methods exist to do the same things. I found some people that had similar questions here:
Lots of people talk about best practice and convention as well as give practical reasons for using one over the other, such as the need to pass a pointer to a constant, which I can do with a static const
but not with a #define
. However I have yet to find anyone talk about a comparison of the efficiency of the two.
From what I understand about the C preprocessor, if I have a statement like this:
#define CONSTANT 6
I create a constant value that can be used like this
char[CONSTANT]
which will actually be replaced with this statement char[6]
prior to actually being compiled.
This to me seems like it would be more efficient than using a
static const constant = 6;
because this would create a variable called constant that would live on the stack which I assume would come with some more baggage than a #define
. Assuming I need a constant in a situation where I could choose to use either a preprocessor #define
or a static const
statement with no obvious reasons to choose one over the other, which is more efficient? And how exactly would I go about testing this myself?
Upvotes: 27
Views: 20146
Reputation: 1577
Consider the following 2 test files
Test1.c: Uses static const foo.
// Test1.c uses static const..
#include <stdio.h>
static const foo = 6;
int main() {
printf("%d", foo);
return 0;
}
Test2.c: uses macro.
// Test2.c uses macro..
#include <stdio.h>
#define foo 6
int main() {
printf("%d", foo);
return 0;
}
and the corresponding assembly equivalences when using gcc -O0
(default) are follows,
Assembly for Test1.c:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 20 sub rsp,0x20
8: e8 00 00 00 00 call d <main+0xd>
d: b8 06 00 00 00 mov eax,0x6
12: 89 c2 mov edx,eax
14: 48 8d 0d 04 00 00 00 lea rcx,[rip+0x4] # 1f <main+0x1f>
1b: e8 00 00 00 00 call 20 <main+0x20>
20: b8 00 00 00 00 mov eax,0x0
25: 48 83 c4 20 add rsp,0x20
29: 5d pop rbp
2a: c3 ret
2b: 90 nop
Assembly for Test2.c:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 20 sub rsp,0x20
8: e8 00 00 00 00 call d <main+0xd>
d: ba 06 00 00 00 mov edx,0x6
12: 48 8d 0d 00 00 00 00 lea rcx,[rip+0x0] # 19 <main+0x19>
19: e8 00 00 00 00 call 1e <main+0x1e>
1e: b8 00 00 00 00 mov eax,0x0
23: 48 83 c4 20 add rsp,0x20
27: 5d pop rbp
28: c3 ret
29: 90 nop
In both the cases, it is not using external memory. But the difference is that, #define
replaces foo
by the value, static const
is an instruction so it increments the instruction pointer to the next instruction and it uses 1 additional register to store the value.
By this, we can say that macro is better than static constants but the difference is minimum.
EDIT: When using -O3
compilation option (i.e at optimization on) both the test1.c and test2.c evaluates the same.
0000000000000000 <main>:
0: 48 83 ec 28 sub rsp,0x28
4: e8 00 00 00 00 call 9 <main+0x9>
9: 48 8d 0d 00 00 00 00 lea rcx,[rip+0x0] # 10 <main+0x10>
10: ba 06 00 00 00 mov edx,0x6
15: e8 00 00 00 00 call 1a <main+0x1a>
1a: 31 c0 xor eax,eax
1c: 48 83 c4 28 add rsp,0x28
20: c3 ret
21: 90 nop
So, gcc
treats both static const
and #define
as the same when it optimize.
Upvotes: 40
Reputation: 158469
The quick way to test simple optimization questions is to use godbolt.
For your specific issue a modern optimizing compiler should be able to produce the same code for both cases and will in fact just optimize them away to a constant. We can see this with the following program (see it live):
#include <stdio.h>
#define CONSTANT 6
static const int constant = 6;
void func()
{
printf( "%d\n", constant ) ;
printf( "%d\n", CONSTANT ) ;
}
in both cases both accessing reduce to the following:
movl $6, %esi #,
Upvotes: 8
Reputation: 20
You've totally changed your question. Here's my answer to your new question:
Because we are talking about C, and assuming you are declaring the array on the stack, the answer is actually very interesting. In this case, it is not possible for there to be any difference between the two. The "6" is not actually used at runtime! Because you are only using it to size an array on the stack, the compiler simply uses this to calculate how much stack space is required for the variable.
Suppose you have a 32 bit address space, and your local function contains this 6-byte array (myArray), and an unsigned 32 bit integer (myInt). The compiler creates the following instructions for entering this function:
- Write the 4 byte return address to the stack
- Move the stack pointer forward by 10 bytes
While executing the function, the runtime doesn't know the names or sizes of any variables. If your code says
myInt = 5;
myArray[myInt] = 25;
then the compiler will have generated these instructions:
- write 00000000 00000000 00000000 00000101 starting at address (StackPointer - 4)
- write 00001101 starting at (StackPointer - 10 + (value at Stackpointer - 4))
So you see, the value "6" is not used at runtime. In fact, you can write to index 6, 7, 8, whatever you want. The run-time won't know that you're overflowing the end of the array. (but depending how you write the code, the compiler may catch the error at compile time)
I glossed over some details there (no doubt some that I'm not even aware of) but that's the gist of it. (I welcome your comments)
Defining the 6 as a "const" may actually cause the value to be stored into 4 bytes of useless space, but that won't affect the execution. Obviosuly it will get optimized away because it is never used.
But, having said all that, never worry about saving a byte of space. Code maintainability is way more important. The risk of introducing a single tiny bug, or making your code a tiny bit less readable, these risks are a trillion trillion times more expensive than the the cost of an extra few bytes or an extra processor cycle. Use constants and enums to take advantage of all the benefits listed here
Upvotes: -2
Reputation: 123448
static const
variables are not (at least should not be) created on the stack; space for them is set aside when the program is loaded, so there should not be a runtime penalty associated with their creation.
There may be a runtime penalty associated with their initialization. although the version of gcc I'm using initializes the constant at compile time; I don't know how common that behavior is. If there is such a runtime penalty, it only occurs once at program startup.
Beyond that, any runtime performance difference between a static const
-qualified object and a literal1 (which is what a macro will eventually expand to) should be negligible to non-existent, depending on the type of the literal and the operation involved.
Stupid example (gcc version 4.1.2 20070115 (SUSE Linux)
):
#include <stdio.h>
#define FOO_MACRO 5
static const int foo_const = 5;
int main( void )
{
printf( "sizeof FOO_MACRO = %zu\n", sizeof FOO_MACRO );
printf( "sizeof foo_const = %zu\n", sizeof foo_const );
printf( " &foo_const = %p\n", ( void * ) &foo_const );
printf( "FOO_MACRO = %d\n", FOO_MACRO );
printf( "foo_const = %d\n", foo_const );
return 0;
}
Output:
sizeof FOO_MACRO = 4
sizeof foo_const = 4
&foo_const = 0x400660
FOO_MACRO = 5
foo_const = 5
Address of foo_const
is in the .rodata
section of the binary:
[fbgo448@n9dvap997]~/prototypes/static: objdump -s -j .rodata static
static: file format elf64-x86-64
Contents of section .rodata:
40065c 01000200 05000000 73697a65 6f662046 ........sizeof F
^^^^^^^^
40066c 4f4f5f4d 4143524f 203d2025 7a750a00 OO_MACRO = %zu..
40067c 73697a65 6f662066 6f6f5f63 6f6e7374 sizeof foo_const
40068c 203d2025 7a750a00 20202020 20202666 = %zu.. &f
40069c 6f6f5f63 6f6e7374 203d2025 700a0046 oo_const = %p..F
4006ac 4f4f5f4d 4143524f 203d2025 640a0066 OO_MACRO = %d..f
4006bc 6f6f5f63 6f6e7374 203d2025 640a00 oo_const = %d..
Note that the object is already initialized to 5, so there's no runtime initialization penalty.
In the printf
statements, the instruction to load the value of foo_const
into %esi
requires one more byte than the one to load the literal value 0x5
, and the instruction has to effectively dereference the %rip
register:
400538: be 05 00 00 00 mov $0x5,%esi
^^^^^^^^^^^^^^
40053d: bf ab 06 40 00 mov $0x4006ab,%edi
400542: b8 00 00 00 00 mov $0x0,%eax
400547: e8 e4 fe ff ff callq 400430 <printf@plt>
40054c: 8b 35 0e 01 00 00 mov 270(%rip),%esi # 400660 <foo_const>
^^^^^^^^^^^^^^^^^
400552: bf bb 06 40 00 mov $0x4006bb,%edi
400557: b8 00 00 00 00 mov $0x0,%eax
40055c: e8 cf fe ff ff callq 400430 <printf@plt>
Will this translate into a measurable runtime performance difference? Maybe, under the right cirucmstances. If you're doing something CPU-bound several hundred thousand times in a tight loop, then yes, using a macro (that resolves to a literal) over a static const
variable may be measurably faster. If this is something that happens once over the lifetime of the program, then the difference is too small to measure and there's no compelling reason to use the macro over the static const
variable.
As always, correctness and maintainability matter more than performance2. You're less likely to make a mistake using a static const
instead of a macro. Consider the following scenario:
#define FOO 1+2
...
x = FOO * 3;
What answer would you expect, and what answer would you get? Compare that with
static const int foo = 1+2;
...
x = foo * 3;
Yes, you could fix the macro case by using parentheses - (1 + 2)
. The point is, this scenario isn't an issue if you use the static const
object. It's one less way to shoot yourself in the foot.
Upvotes: 0
Reputation: 104698
If the constant's definition is visible to the translation, the compiler is certainly capable of utilizing that as an optimization.
this would create a variable called constant that would live on the stack which I assume would come with some more baggage than a #define.
It could "live" in multiple places. A compiler can certainly substitute the constant where referenced, without requiring static or stack storage.
Assuming I need a constant in a situation where I could choose to use either a preprocessor #define or a static const statement with no obvious reasons to choose one over the other, which is more efficient?
It depends on the compiler and architecture. I get the impression that some people believe #define
has a big advantage. It doesn't. The obvious case is a complex evaluation or function call (say sin(4.8)
. Consider a constant used inside a loop. A properly scoped constant could be evaluated once. A define could evaluate on each iteration.
And how exactly would I go about testing this myself?
Read the assembly produced by each compiler you use, and measure.
If you want a rule of thumb, I would say "Use a constant, unless #define
provides you a measurable improvement in the scenario".
There was a good writeup in the GCC docs about this. Maybe somebody remembers where exactly it was.
Upvotes: 4