664

Which one is better to use among the below statements in C?

static const int var = 5;

or

#define var 5

or

enum { var = 5 };
huysentruitw
  • 27,376
  • 9
  • 90
  • 133
Vijay
  • 65,327
  • 90
  • 227
  • 319
  • 50
    Interestingly, this is *almost* exactly the same question as http://stackoverflow.com/questions/1637332/static-const-vs-define. The only difference is that that question is about C++, and this one is about C. Since my answer was C++ specific, I say that makes them not identical, but others may disagree. – T.E.D. Nov 04 '09 at 15:09
  • 68
    Not identical, definitely. There is a whole lot of areas where C++ allows C syntax for compatibility reasons. In those cases, questions like "what is the best way to do X" will have different answers in C++. E.g. object initialization. – MSalters Nov 04 '09 at 15:33
  • 1
    Also: https://stackoverflow.com/questions/1944041/advantage-and-disadvantages-of-define-vs-constants – jamesdlin Aug 10 '17 at 02:07
  • 1
    How is this not opinion based? They each have a different purpose – Sam Hammamy Dec 28 '17 at 13:28

17 Answers17

795

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.

Deep
  • 5,772
  • 2
  • 26
  • 36
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 131
    fantastic list! One drawback with `enum` is that they're implemented as `int` ([C99] 6.7.2.2/3). A `#define` lets you specify unsigned and long with `U` and `L` suffixes, and `const` lets you give a type. `enum` can cause problems with usual type conversions. – Gauthier Mar 01 '11 at 09:33
  • 52
    (2) people ALWAYS complain about type safety. I never understand why not just use "#define var ((int)5)" and hurray you got type safety with a define. – Ingo Blackman Nov 26 '13 at 23:23
  • 5
    Besides what @Gauthier says, alternatives (1) and (2) can be used for non-integer constants as well. – Some programmer dude Jan 02 '14 at 09:41
  • 3
    What about memory usage in space contrived environments? Is only `#define` guaranteed to not consume any extra space? – RedX Feb 07 '14 at 14:46
  • 12
    @RedX: you would have to be in a very peculiar environment for space to a concern. That said, neither `enum` nor `#define` uses extra space, per se. The value will appear in the object code as part of the instructions rather than being allocated storage in the data segment or in heap or on the stack. You'll have some space allocated for the `static const int`, but the compiler might optimize it away if you don't take an address. – Jonathan Leffler Feb 07 '14 at 15:04
  • 6
    Yust been working on Arduino ... extremely small memory space (less than 2 k) ... option 2 is helping a lot. – Paul Feb 16 '14 at 13:10
  • 1
    @Paul: enums don't take more space than #defines, see "fshort-enums" with GCC. – Étienne Feb 16 '14 at 20:26
  • 1
    @Étienne thanks for the feedback: since you are in ARM processors... ;-) do you know if this also applies for the arduino avr-gcc compiler? – Paul Feb 17 '14 at 14:47
  • 1
    @Paul: I'm no expert but I would say yes, the fshort-enums option is also available for avr-gcc ;-) – Étienne Feb 17 '14 at 20:30
  • 1
    Enum also enables initialization like this:`enum { size=10 } ; char str[size] = {0};` – Philippe A. Feb 28 '14 at 17:39
  • 20
    Another 'vote' for `enum`s (and `static const`): they can't be changed. a `define` can be `#undefine`'d where an `enum` and `static const` are fixed to the value given. – Daan Timmer Jul 08 '14 at 09:46
  • 2
    another use of `#define` would be when one wants to embed major and minor software version numbers (since they don't change very often unlike build number) in a header file in their source code. – max Dec 21 '14 at 16:52
  • 1
    Are there any 16 bit compilers where static `const uint32_t var = 0xFFFF0000;` could result in var = 0x0000? What do the C99 and C89 specs say about the size of a constant? – Samuel Jan 06 '15 at 19:30
  • 3
    For the love of everything holy put your #define values in parentheses – QED Mar 27 '15 at 03:56
  • 22
    @QED: No, thank you. A simple constant is safe outside parentheses. Or, show me how a program that could legitimately be expected to compile would be altered by not having the 5 in parentheses. If it was the argument to a function-style macro, or if there were any operators in the expression, then you'd be correct to blame me had I not included the parentheses. But that isn't the case here. – Jonathan Leffler Mar 27 '15 at 03:59
  • 6
    The least type safe alternative could actually be 3). In this example, all 3 cases are _guaranteed_ by the standard to have type `int` and be `sizeof(int)` bytes long. An enum type variable, however, can be any type, including `char`, `signed int` and `unsigned int`. If you use enum _constants_, it is very tempting to also use enum _variables_, but because of a stupid design mistake in the C standard (6.7.2.2), these may not have the same type! In cases where the size of the variable matters, enums _variables_ should be avoided, because they aren't type safe. – Lundin Jun 02 '15 at 13:46
  • 3
    One disadvantage of using static const is that they cant be used to define *other* constant values. eg: `static const a = 5; static const b = a; //warning: use of const variable in a constant expression is non-standard in C` – Toby Oct 12 '15 at 16:24
  • What about using `constexpr` instead of `const` ? – Dan M. Mar 17 '16 at 15:15
  • 4
    @DanM.: In C (which is the topic of this question), using `constexpr` will yield a syntax error. In C++, you could (and probably should) use `constexpr` when appropriate. I'm not sure I could identify succinctly when `constexpr` should be used in preference to `const`, though. – Jonathan Leffler Mar 17 '16 at 15:48
  • 1
    @DanM. This seems to be a good Stack Overflow Q&A about [Difference between `constexpr` and `const`?](https://stackoverflow.com/questions/14116003/difference-between-constexpr-and-const) – Jonathan Leffler Mar 18 '16 at 04:37
  • (3) imports fine into Swift, while (2) doesn't for complex definitions. Adding a custom header that redefines (2) as (3) helps with creation of bindings. – Etan Oct 06 '16 at 20:35
  • 1
    The problem with enums in C is that they provide no type safety when passed as function parameters or assigned to a variable. No C compiler that I am aware of will produce a warning if you use a wrong enum type. – vgru Jul 13 '17 at 10:31
  • 4
    @Groo Not sure what you mean. In non-ancient versions of GCC/Clang, `-Wenum-conversion` (which may be enabled by default or with `-Wall`) will call out when enums are mixed in both assignments and function calls. –  Jul 02 '18 at 16:22
  • The last sentence in this otherwise useful answer is incorrect. You would very often avoid using a static const int in C++ in favor of an enum (and even static constexpr const int is not always superior). – einpoklum Aug 01 '21 at 20:50
285

Generally speaking:

static const

Because it respects scope and is type-safe.

The only caveat I could see: if you want the variable to be possibly defined on the command line. There is still an alternative:

#ifdef VAR // Very bad name, not long enough, too general, etc..
  static int const var = VAR;
#else
  static int const var = 5; // default value
#endif

Whenever possible, instead of macros / ellipsis, use a type-safe alternative.

If you really NEED to go with a macro (for example, you want __FILE__ or __LINE__), then you'd better name your macro VERY carefully: in its naming convention Boost recommends all upper-case, beginning by the name of the project (here BOOST_), while perusing the library you will notice this is (generally) followed by the name of the particular area (library) then with a meaningful name.

It generally makes for lengthy names :)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 2
    Agreed - also with #define there's a general danger of mangling code as the preprocessor is not aware of syntax. – NeilDurant Nov 04 '09 at 14:27
  • 10
    Its better to use #if than #ifdef , but otherwise I agree. +1. – Tim Post Nov 04 '09 at 15:32
  • See this : http://stackoverflow.com/questions/135069/ifdef-vs-if-which-is-better-safer regarding the difference. Actually, CCAN has implemented that as part of their style guidelines (one of Rusty's pet peeves). – Tim Post Nov 04 '09 at 15:34
  • 65
    This is standard C++ evangelism. The answer below is MUCH clearer in explaining what the options really are and mean. In particular: I just had a problem with "static const". Someone used it to define around 2000 "constants" in a header file. Then this header file was included in around 100 ".c" and ".cpp" files. => 8Mbytes for "consts". Great. Yes I know that you might use a linker to remove unreferenced consts, but then this still leaves you which the "consts" which ARE referenced. Running out of space what's all wrong with this answer. – Ingo Blackman Nov 26 '13 at 23:19
  • 2
    @IngoBlackman: With a good compiler, only those `static` whose address is taken should remain; and if the address is taken one could not have used a `#define` or `enum` (no address)... so I really fail to see what alternative could have been used. If you can do away with "compile time evaluation", you might be looking for `extern const` instead. – Matthieu M. Nov 27 '13 at 07:41
  • 15
    @Tim Post: `#if` might be preferable over `#ifdef` for boolean flags, but in this case it would make it impossible to define `var` as `0` from the command line. So in this case, `#ifdef` makes more sense, as long as `0` is a legal value for `var`. – Maarten Jan 05 '14 at 16:44
  • 1
    @Matthieu: I am looking for "#define CONST_VALUE ((int)5)". If I want a constant VALUE it is an advantage if I cannot take the address of "CONST_VALUE". If I want simple integer constant values I do not want their addresses. – Ingo Blackman Jan 25 '14 at 23:29
  • Btw, what about `static constexpr` ? Shouldn't it act exactly as static const without overhead? – Dan M. Mar 17 '16 at 15:13
  • 1
    @DanM.: In the case of an integer, there should be no difference. `constexpr` vs `const` is about *when* the value is evaluated. `constexpr` guarantees that the value will be evaluated at compile-time (thus no function executed before `main` starts), which also guarantees that said value will be available to the optimizer... however note this is a C question, and there is no `constexpr` in C. – Matthieu M. Mar 17 '16 at 15:35
  • This is a C question, not C++. And `const` on C means read-only, not constant. You can declare a const inside a block, take a pointer to it and modify its value. See [this example](http://pastebin.com/itxKn7kW) – volpato May 09 '16 at 20:52
  • @volpato: Well, at the time I answered, it was tagged C++ (quite a while now, eh?). Also, compiling with `gcc -std=c89 -pedantic` yields `warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]` and with `-O2` prints `"const really means constant"` so I am afraid that you are just a victim of a permissive compiler. – Matthieu M. May 10 '16 at 06:22
  • 1
    Note that in c99 inline functions, you can not reference static variables, so that's one scenario in which you must still use #define's. – Björn Lindqvist May 30 '17 at 14:54
  • Drop the static, use anonymous namespace. namespace { const int var = 123; } – Musky Jan 30 '19 at 10:21
  • @Musky: There is no namespace in C; and `static` is perfectly acceptable in C++. – Matthieu M. Jan 30 '19 at 12:03
  • Why doe this answer mention Boost when the question is about C? Does Boost have come C compatible libraries? – Pharap Aug 30 '19 at 22:08
  • 1
    @Pharap: Why not mention its *naming convention* for macros, when it shares the same preprocessor, its conventions have been established by experts and it has the largest collection of macros in a single (collection) of libraries? – Matthieu M. Aug 31 '19 at 11:16
  • 1
    @MatthieuM. The naming conventions of Boost aren't necessarily representative of the naming conventions used for C libraries because Boost is a collection of C++ libraries. An example of a well-known C-specific library would be more suitable. – Pharap Sep 02 '19 at 05:24
  • What if I want global const, available everywhere? – mohammadsdtmnd Apr 02 '20 at 14:54
113

In C, specifically? In C the correct answer is: use #define (or, if appropriate, enum)

While it is beneficial to have the scoping and typing properties of a const object, in reality const objects in C (as opposed to C++) are not true constants and therefore are usually useless in most practical cases.

So, in C the choice should be determined by how you plan to use your constant. For example, you can't use a const int object as a case label (while a macro will work). You can't use a const int object as a bit-field width (while a macro will work). In C89/90 you can't use a const object to specify an array size (while a macro will work). Even in C99 you can't use a const object to specify an array size when you need a non-VLA array.

If this is important for you then it will determine your choice. Most of the time, you'll have no choice but to use #define in C. And don't forget another alternative, that produces true constants in C - enum.

In C++ const objects are true constants, so in C++ it is almost always better to prefer the const variant (no need for explicit static in C++ though).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 7
    "you can't use a const int object as a case label (while a macro will work) " ---> Regarding this statement i tested a const int variable in C in switch- case it is working .... – john Feb 10 '12 at 06:24
  • 8
    @john: Well, you need to provide the code that you tested and name the specific compiler. Using `const int` objects in case-labels is illegal in all versions of C language. (Of course, your compiler is free to support it as a non-standard C++-like language extension.) – AnT stands with Russia Feb 10 '12 at 23:46
  • 12
    "... *and therefore are usually useless in most practical cases*." I disagree. They're perfectly useful as long as you don't need to use the name as a constant expression. The word "constant" in C means something that can be evaluated at compile time; `const` means read-only. `const int r = rand();` is perfectly legal. – Keith Thompson Feb 26 '14 at 16:51
  • In c++, it is better to use `constexpr` as compared to `const` specially with the `stl` containers like `array` or `bitset`. – Mayukh Sarkar Aug 02 '16 at 10:28
  • 1
    @john you must've tested in `switch()` statement, not in `case` one. I've just got caught on this one too ☺ – Hi-Angel May 17 '17 at 15:00
37

The difference between static const and #define is that the former uses the memory and the later does not use the memory for storage. Secondly, you cannot pass the address of an #define whereas you can pass the address of a static const. Actually it is depending on what circumstance we are under, we need to select one among these two. Both are at their best under different circumstances. Please don't assume that one is better than the other... :-)

If that would have been the case, Dennis Ritchie would have kept the best one alone... hahaha... :-)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
wrapperm
  • 1,266
  • 12
  • 18
  • 6
    +1 for mentioning memory, some embedded systems still don't have that much, though I would probably start off using static consts and only change to #defines if needed. – fluffyben Mar 07 '12 at 11:36
  • 3
    I just tested it. Indeed, const int uses additional memory compared to #define or enum. Since we program embedded systems, we can't afford the additional memory usage. So, we'll go back to using #define or enum. – Davide Andrea Mar 02 '15 at 17:51
  • 5
    Practically speaking it is not true (anymore) that a `const` does use memory. GCC (tested with 4.5.3 and a few newer versions) easily optimizes the `const int` into a direct literal in your code when using -O3. So if you do low RAM embedded development (e.g. AVR) you can safely use C consts if you use GCC or another compatible compiler. I have not tested it but expect Clang to do the same thing btw. – Raphael Dec 10 '15 at 18:43
19

In C #define is much more popular. You can use those values for declaring array sizes for example:

#define MAXLEN 5

void foo(void) {
   int bar[MAXLEN];
}

ANSI C doesn't allow you to use static consts in this context as far as I know. In C++ you should avoid macros in these cases. You can write

const int maxlen = 5;

void foo() {
   int bar[maxlen];
}

and even leave out static because internal linkage is implied by const already [in C++ only].

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • 1
    What do you mean with "internal linkage"? I can have `const int MY_CONSTANT = 5;` in one file and access it with `extern const int MY_CONSTANT;` in another. I could not find any info in the standard (C99 at least) about `const` changing the default behaviour "6.2.2:5 If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external". – Gauthier Jan 19 '11 at 12:10
  • @Gauthier: Sorry, about that. I should have said "is implied by const already in the C++ language". This is specific to C++. – sellibitze Jan 21 '11 at 21:05
  • @sellibitze it's nice to see some arguments along the way instead of tons of *OPINION* If there would be bonus for true arguments, you got it! – Paul Feb 16 '14 at 13:07
  • 3
    As of C99, your second snippet is legal. `bar` is a VLA (variable length array); the compiler is likely to generate code as if its length were constant. – Keith Thompson Feb 26 '14 at 16:53
15

Another drawback of const in C is that you can't use the value in initializing another const.

static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;

// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND 
                                     * NUMBER_OF_HANDS;

Even this does not work with a const since the compiler does not see it as a constant:

static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!

I'd be happy to use typed const in these cases, otherwise...

Gauthier
  • 40,309
  • 11
  • 63
  • 97
  • 5
    A little late to the game, but this question came up in another question. Chasing down why your `static uint8_t const ARRAY_SIZE = 16;` all of a sudden no longer compiles can be a bit challenging, particularly when the `#define ARRAY_SIZE 256` is buried ten layers deep in a tangled web of headers. That all caps name `ARRAY_SIZE` is asking for trouble. Reserve ALL_CAPS for macros, and never define a macro that is not in ALL_CAPS form. – David Hammen Sep 03 '11 at 02:35
  • @David: sound advice, which I will follow. – Gauthier Sep 05 '11 at 07:02
  • It's due to 'static' storage, not 'const' qualifier – tstanisl Sep 03 '21 at 19:36
  • @tstanisl well yes, since these statics need initialization, which I think happens at compile time. I was just referring to this way to define "constants". Strangely enough, my first code example now works, with gcc 9.3.0, even with `--stc=c89`. The second one doesn't, the compiler complains about "variably modified array"... since ARRAY_SIZE is a (read-only) variable, not a constant. – Gauthier Sep 05 '21 at 08:44
  • @Gauthier, the first example works with all C standard (c89,c99,c11,c18) with GCC and CLANG in pedantic mode. Interesting. It looks that `static` variables can be used to initialize other `static` variables. – tstanisl Sep 05 '21 at 19:51
  • @tstanisl It sure looks like it. I think I know what esoteric embedded compiler I was using at the time (over 10 years ago!), but won't accuse them without double checking ;) – Gauthier Sep 14 '21 at 11:41
11

If you can get away with it, static const has a lot of advantages. It obeys the normal scope principles, is visible in a debugger, and generally obeys the rules that variables obey.

However, at least in the original C standard, it isn't actually a constant. If you use #define var 5, you can write int foo[var]; as a declaration, but you can't do that (except as a compiler extension" with static const int var = 5;. This is not the case in C++, where the static const version can be used anywhere the #define version can, and I believe this is also the case with C99.

However, never name a #define constant with a lowercase name. It will override any possible use of that name until the end of the translation unit. Macro constants should be in what is effectively their own namespace, which is traditionally all capital letters, perhaps with a prefix.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
  • 6
    Unfortunately, this is not the case with C99. `const` in C99 is still not a real constant. You can declare array size with a `const` in C99, but only because C99 supports Variable Length Arrays. For this reason, it will only work where VLAs are allowed. For example, even in C99, you still can't use a `const` to declare size of a member array in a `struct`. – AnT stands with Russia Nov 06 '09 at 00:25
  • 1
    While it is correct that C99 won't let you do that, GCC (tested with 4.5.3) will perfectly let you initialize arrays with a `const int` size as if it were a C++ const or a macro. Whether you want to depend on this deviation of GCC from the standard is of course your choice, I'd personally go with it unless you can really forsee using another compiler than GCC or Clang, the latter has the same feature here (tested with Clang 3.7). – Raphael Dec 10 '15 at 18:46
9

It is ALWAYS preferable to use const, instead of #define. That's because const is treated by the compiler and #define by the preprocessor. It is like #define itself is not part of the code (roughly speaking).

Example:

#define PI 3.1416

The symbolic name PI may never be seen by compilers; it may be removed by the preprocessor before the source code even gets to a compiler. As a result, the name PI may not get entered into the symbol table. This can be confusing if you get an error during compilation involving the use of the constant, because the error message may refer to 3.1416, not PI. If PI were defined in a header file you didn’t write, you’d have no idea where that 3.1416 came from.

This problem can also crop up in a symbolic debugger, because, again, the name you’re programming with may not be in the symbol table.

Solution:

const double PI = 3.1416; //or static const...
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
suren
  • 7,817
  • 1
  • 30
  • 51
8

#define var 5 will cause you trouble if you have things like mystruct.var.

For example,

struct mystruct {
    int var;
};

#define var 5

int main() {
    struct mystruct foo;
    foo.var = 1;
    return 0;
}

The preprocessor will replace it and the code won't compile. For this reason, traditional coding style suggest all constant #defines uses capital letters to avoid conflict.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Non-maskable Interrupt
  • 3,841
  • 1
  • 19
  • 26
6

I wrote quick test program to demonstrate one difference:

#include <stdio.h>

enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};

#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32

int main(int argc, char *argv[]) {

   printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);

   return(0);
}

This compiles with these errors and warnings:

main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
      ^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
      ^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
        ^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
        ^

Note that enum gives an error when define gives a warning.

Be Kind To New Users
  • 9,672
  • 13
  • 78
  • 125
5

The definition

const int const_value = 5;

does not always define a constant value. Some compilers (for example tcc 0.9.26) just allocate memory identified with the name "const_value". Using the identifier "const_value" you can not modify this memory. But you still could modify the memory using another identifier:

const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.

This means the definition

#define CONST_VALUE 5

is the only way to define a constant value which can not be modified by any means.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user2229691
  • 1,099
  • 12
  • 10
  • 10
    Modifying a constant value using a pointer is undefined behavior. If you're willing to go there, `#define` can also be modified, by editing the machine code. – ugoren Jun 12 '13 at 13:57
  • You're partly right. I tested the code with Visual Studio 2012 and it prints `5`. But one can't modify `#define` because it's a preprocessor macro. It doesn't exist in the binary program. If one wanted to modify all places where `CONST_VALUE` was used, one had to do it one by one. – user2229691 Oct 19 '13 at 07:28
  • 3
    @ugoren: Suppose you write `#define CONST 5`, then `if (CONST == 5) { do_this(); } else { do_that(); }`, and the compiler eliminates the `else` branch. How do you propose to edit the machine code to change `CONST` to 6? – Keith Thompson Feb 26 '14 at 16:47
  • @KeithThompson, I never said it can be done easily and reliably. Just that `#define` isn't bullet-proof. – ugoren Feb 26 '14 at 18:12
  • 3
    @ugoren: My point is that "editing the machine code" is not a sensible way to duplicate the effect of changing the value of a `#define`. The only real way to do that is to edit the source code and recompile. – Keith Thompson Feb 26 '14 at 19:04
5

Although the question was about integers, it's worth noting that #define and enums are useless if you need a constant structure or string. These are both usually passed to functions as pointers. (With strings it's required; with structures it's much more efficient.)

As for integers, if you're in an embedded environment with very limited memory, you might need to worry about where the constant is stored and how accesses to it are compiled. The compiler might add two consts at run time, but add two #defines at compile time. A #define constant may be converted into one or more MOV [immediate] instructions, which means the constant is effectively stored in program memory. A const constant will usually be stored in a separate section in data memory such as .const or .rodata. In systems with a Harvard architecture, there could be differences in performance and memory usage, although they'd likely be small. They might matter for hard-core optimization of inner loops.

Adam Haun
  • 359
  • 7
  • 13
  • Re *".const section in data memory."*: That seems to be system-specific. What system? What assembler? – Peter Mortensen Aug 13 '23 at 19:19
  • I've seen it in TI's compiler and GCC, and it seems to be common on other platforms as well. I've changed the sentence to specify that this is an example, not a universal rule. – Adam Haun Aug 14 '23 at 15:41
3

Don't think there's an answer for "which is always best" but, as Matthieu said

static const

is type safe. My biggest pet peeve with #define, though, is when debugging in Visual Studio you cannot watch the variable. It gives an error that the symbol cannot be found.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Afcrowe
  • 129
  • 4
  • 1
    "you cannot watch the variable" Right, it isn't a variable. It doesn't change, why do you need to watch it? You can find everywhere it's used simply by searching for the label. Why would you need (or even want) to watch a #define? – Marshall Eubanks Jul 23 '18 at 18:44
2

Incidentally, an alternative to #define, which provides proper scoping but behaves like a "real" constant, is "enum". For example:

enum {number_ten = 10};

In many cases, it's useful to define enumerated types and create variables of those types; if that is done, debuggers may be able to display variables according to their enumeration name.

One important caveat with doing that, however: in C++, enumerated types have limited compatibility with integers. For example, by default, one cannot perform arithmetic upon them. I find that to be a curious default behavior for enums; while it would have been nice to have a "strict enum" type, given the desire to have C++ generally compatible with C, I would think the default behavior of an "enum" type should be interchangeable with integers.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
supercat
  • 77,689
  • 9
  • 166
  • 211
  • 1
    In C, enumeration constants are always of type `int`, so the "enum hack" can't be used with other integer types. (The enumeration *type* is compatible with some implementation-defined integer type, not necessarily `int`, but in this case the type is anonymous so that doesn't matter.) – Keith Thompson Feb 26 '14 at 16:49
  • @KeithThompson: Since I wrote the above, I've read that MISRA-C will squawk if a compiler assigns a type other than `int` to an enumeration-typed variable (which compilers are allowed to do) and one tries to assign to such a variable a member of its own enumeration. I wish standards committees would add portable ways of declaring integer types with specified semantics. *ANY* platform, regardless of `char` size, should be able to e.g. declare a type which will wrap mod 65536, even if the compiler has to add lots of `AND R0,#0xFFFF` or equivalent instructions. – supercat Feb 26 '14 at 17:09
  • You can use `uint16_t`, though of course that's not an enumeration type. It would be nice to let the user specify the integer type used to represent a given enumeration type, but you can achieve much of the same effect with a `typedef` for `uint16_t` and a series of `#define`s for individual values. – Keith Thompson Feb 26 '14 at 17:12
  • 1
    @KeithThompson: I understand that for historical reasons, we're stuck with the fact that some platforms will evaluate `2U < -1L` as true and others as false, and we're now stuck with the fact that a some platforms will implement a comparison between `uint32_t` and `int32_t` as signed and some as unsigned, but that doesn't mean the Committee couldn't define an upwardly-compatible successor to C that includes types whose semantics would be consistent on all compilers. – supercat Feb 26 '14 at 17:16
  • @KeithThompson Thankfully C23 has now added the ability to explicitly specify the base type of an enum, for example `enum : unsigned char { DEL = 127 };`. It has also borrowed `constexpr` from C++, for example `constexpr unsigned char DEL = 127;` – Simon Kissane Jul 07 '23 at 06:39
  • (Just saw this answer because someone replied to my comment.) Ordinary C++ `enum`s are very similar to C `enum`s; an `enum` type is compatible with some integer type. C++ adds `enum class`, which lets you define an enumerated type that isn't compatible with any integer type. – Keith Thompson Jul 08 '23 at 05:38
  • @KeithThompson: When I used C++ ages ago, I had to define arithmetic and bitwise operators for enumerations, because the compiler complained that the operators weren't defined for enum types otherwise. – supercat Jul 08 '23 at 18:07
  • A slight error on my part: a C++ enum is not compatible with any integer type (a C enum is), but a C++ enum type has the same representation as its integral "underlying type", and enum expressions can be used in arithmetic expressions. This has been the case at least since C++98. `enum class` was introduced in C++11,but ordinary enums were unchanged. Is it possible you were using a buggy or non-conforming compiler? In any case, your answer is incorrect for current C++ (at least since 1998) and for any C++ compiler anyone is likely to use. – Keith Thompson Jul 08 '23 at 21:24
1

A simple difference:

At pre-processing time, the constant is replaced with its value. So you could not apply the dereference operator to a define, but you can apply the dereference operator to a variable.

As you would suppose, define is faster that static const.

For example, having:

#define mymax 100

you can not do printf("address of constant is %p",&mymax);.

But having

const int mymax_var=100

you can do printf("address of constant is %p",&mymax_var);.

To be more clear, the define is replaced by its value at the pre-processing stage, so we do not have any variable stored in the program. We have just the code from the text segment of the program where the define was used.

However, for static const we have a variable that is allocated somewhere. For gcc, static const are allocated in the text segment of the program.

Above, I wanted to tell about the reference operator so replace dereference with reference.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 1
    Your answer is very wrong. This is about C, your answer relates to C++, which has very different semantics for the `const` qualifier. C does not have symbolica constants other than _enum-constants_. A `const int` is a variable. You also confuse language and specific implementations. There is no requirement where to place the object. And it is not even true for gcc: typically it places `const` qualified variables in the `.rodata` section. But that depends on the target platform. And you mean the address-of operator `&`. – too honest for this site Mar 05 '17 at 07:13
0

We looked at the produced assembler code on the MBF16X... Both variants result in the same code for arithmetic operations (ADD Immediate, for example).

So const int is preferred for the type check while #define is old style. Maybe it is compiler-specific. So check your produced assembler code.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
guest
  • 9
  • 1
-2

I am not sure if I am right, but in my opinion calling #defined value is much faster than calling any other normally declared variable (or const value).

It's because when program is running and it needs to use some normally declared variable, it needs to jump to exact place in memory to get that variable.

In opposite, when it uses #defined value, the program doesn't need to jump to any allocated memory; it just takes the value. If #define myValue 7 and the program calling myValue, it behaves exactly the same as when it just calls 7.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
pajczur
  • 235
  • 3
  • 10
  • 1
    can you please quantify the statement that it is faster? Have you profiled this in some way? at what point it it faster - compile time? linking? at runtime? On what systems and compilers? – braindigitalis Aug 03 '22 at 09:25
  • Actually as I told on beginning "I am not sure if I am right but in my opinion...". I'd rather expect any verification from more experienced users. – pajczur Aug 29 '22 at 21:29