Static Const VS #Define

static const vs #define

Personally, I loathe the preprocessor, so I'd always go with const.

The main advantage to a #define is that it requires no memory to store in your program, as it is really just replacing some text with a literal value. It also has the advantage that it has no type, so it can be used for any integer value without generating warnings.

Advantages of "const"s are that they can be scoped, and they can be used in situations where a pointer to an object needs to be passed.

I don't know exactly what you are getting at with the "static" part though. If you are declaring globally, I'd put it in an anonymous namespace instead of using static. For example

namespace {
unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}

static const vs #define vs enum

It depends on what you need the value for. You (and everyone else so far) omitted the third alternative:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Ignoring issues about the choice of name, then:

  • If you need to pass a pointer around, you must use (1).
  • Since (2) is apparently an option, you don't need to pass pointers around.
  • Both (1) and (3) have a symbol in the debugger's symbol table - that makes debugging easier. It is more likely that (2) will not have a symbol, leaving you wondering what it is.
  • (1) cannot be used as a dimension for arrays at global scope; both (2) and (3) can.
  • (1) cannot be used as a dimension for static arrays at function scope; both (2) and (3) can.
  • Under C99, all of these can be used for local arrays. Technically, using (1) would imply the use of a VLA (variable-length array), though the dimension referenced by 'var' would of course be fixed at size 5.
  • (1) cannot be used in places like switch statements; both (2) and (3) can.
  • (1) cannot be used to initialize static variables; both (2) and (3) can.
  • (2) can change code that you didn't want changed because it is used by the preprocessor; both (1) and (3) will not have unexpected side-effects like that.
  • You can detect whether (2) has been set in the preprocessor; neither (1) nor (3) allows that.

So, in most contexts, prefer the 'enum' over the alternatives. Otherwise, the first and last bullet points are likely to be the controlling factors — and you have to think harder if you need to satisfy both at once.

If you were asking about C++, then you'd use option (1) — the static const — every time.

Static const vs #define for efficiency in C

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.

static const vs. #define in c++ - differences in executable size

As seen in the comments, the typesafe operator| overloading for my enums seems to prevent VC++ from inlining the ORed value. I guess I'll keep using the #define version as I hate increasing the executable size if there's no benefits (no, this is not premature optimization) - after all, it doesn't increase readability, and since the combination of flags is already of my flagset enum type, I also don't lose any type-safety, I guess.

What is the difference between static const and const?

The difference is the linkage.

// At file scope
static const int a=5; // internal linkage
const int i=5; // external linkage

If the i object is not used outside the translation unit where it is defined, you should declare it with the static specifier.

This enables the compiler to (potentially) perform further optimizations and informs the reader that the object is not used outside its translation unit.

Difference between static const and #define in Objective-C

#define myInteger 5

is a preprocessor macro. The preprocessor will replace every occurrence of myInteger with 5 before the compiler is started. It's not a variable, it's just sort of an automatic find-and-replace mechanism.

static const NSInteger myInteger = 5;

This is a "real" variable that is constant (can't be changed after declaration). Static means that it will be a shared variable across multiple calls to that block.

Linux Kernel: Static Const vs #Define

It used to be that you couldn't do:

const size_t buffer_size = 1024;
unsigned char buffer[buffer_size];

in C, since buffer_size is not a "real" constant. Therefore you often see

#define BUFFER_SIZE 1024
unsigned char buffer[BUFFER_SIZE];

instead.

As of C99, you can do the former, but not in global scope. It won't work outside of a function (not even if made static). Since much code in the kernel deals with similiar constructs, that might be one reason for using the preprocessor instead.

Note: don't forget about sizeof, it's a very good tool when it comes to not repeating the size constant all over the place, regardless of how the constant was implemented.

constexpr vs. static const: Which one to prefer?

As long as we are talking about declaring compile-time constants of scalar integer or enum types, there's absolutely no difference between using const (static const in class scope) or constexpr.

Note that compilers are required to support static const int objects (declared with constant initializers) in constant expressions, meaning that they have no choice but to treat such objects as compile-time constants. Additionally, as long as such objects remain odr-unused, they require no definition, which further demonstrates that they won't be used as run-time values.

Also, rules of constant initialization prevent local static const int objects from being initialized dynamically, meaning that there's no performance penalty for declaring such objects locally. Moreover, immunity of integral static objects to ordering problems of static initialization is a very important feature of the language.

constexpr is an extension and generalization of the concept that was originally implemented in C++ through const with a constant initializer. For integer types constexpr does not offer anything extra over what const already did. constexpr simply performs an early check of the "constness" of initializer. However, one might say that constexpr is a feature designed specifically for that purpose so it fits better stylistically.



Related Topics



Leave a reply



Submit