107

In many programs a #define serves the same purpose as a constant. For example.

#define FIELD_WIDTH 10
const int fieldWidth = 10;

I commonly see the first form preferred over the other, relying on the pre-processor to handle what is basically an application decision. Is there a reason for this tradition?

Thom Wiggers
  • 6,938
  • 1
  • 39
  • 65
C. Ross
  • 31,137
  • 42
  • 147
  • 238
  • 4
    Note that for integer constants, you can also use an enum like in ``enum {FIELD_WIDTH = 10};´´which can be useful if you need a compile-time constant in a function scope. – Jochen Walter Oct 26 '10 at 14:16
  • 2
    You can #define at function scope too although you have to remember to #undef it at the end of the function – CashCow Oct 26 '10 at 14:58
  • 3
    @CashCow: That´s right, but then you´re undefining a possible #define at file scope (which may have been defined in an include file you have not written yourself). – Jochen Walter Oct 26 '10 at 15:18
  • 6
    @Jochen: When there is a collision with another macro definition, then your #define has already overwritten the previous definition and you've ignored a compiler warning, so you can as well #undef it and produce a compiler error when the macro is used further down in your code, to remind you that you should change the macro name for your function scope macro. – Secure Oct 26 '10 at 15:49
  • 1
    @Secure: Sure, but then you have to handle the name clash. The advantage of local scopes is that they avoid it. Of course, using a local enum is not bullet-proof either: If a global #define rewrites FIELD_WIDTH to something else, say 20, the enum would be rewritten to `\`enum {20 = 10};\`` If the global and the local definition of FIELD_WIDTH are enums, though, both definitions do coexist (the local definition shadows the global one). (See http://www.eetimes.com/discussion/programming-pointers/4023879/Enumeration-Constants-vs-Constant-Objects for further information on this topic. – Jochen Walter Oct 26 '10 at 19:59
  • @Jochen Walter: How hard can it be to handle a macro name clash in function scope? You get a compiler error message with it, search it in the source and see what is done there. Enums may be easier here, sure, but if I need or want to use macros for any of the reasons given in this thread, there is no choice. If I use x where y can happen, then I have to handle y. At least I get a compiler error with macros. How do I handle a silent shadowing when it is the thing I **don't** want? BTW, your link is about C++, this topic is about C. – Secure Oct 27 '10 at 05:56
  • @Secure: I'm not saying that this is the most important question in software engineering. If you prefer #defines to enums, stick with them. I switched from #defines to enums because I see no point in using the preprocessor for compile-time integer constants when I can have them in plain C (with scoping, type checking, regular C syntax, proper indentation). For non-integer constants, of course, you have no choice. Shadowing is not a major concern of mine. We have the same with local variables and I never heard this to be a big impediment to C programming. – Jochen Walter Oct 27 '10 at 20:29
  • @Secure: The link in my previous comment was wrong. It should read http://www.eetimes.com/discussion/programming-pointers/4023858/Symbolic-Constant. Thank you for pointing this mistake out to me. – Jochen Walter Oct 27 '10 at 20:29

9 Answers9

179

There is a very solid reason for this: const in C does not mean something is constant. It just means a variable is read-only.

In places where the compiler requires a true constant (such as for array sizes for non-VLA arrays), using a const variable, such as fieldWidth is just not possible.

Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41
  • 49
    +1 for correct answer among a sea of wrong ones from C++ coders who don't know C. – R.. GitHub STOP HELPING ICE Oct 26 '10 at 13:59
  • @Bart is there any reason to do the same thing for a character constant? – C. Ross Oct 26 '10 at 14:04
  • 3
    @C. Ross: Consistency. All manifest constants are usually defined with `#define`s. `const` is only used to indicate a read-only (access path to a) variable. I know, `const` in C is just plain broken :-) – Bart van Ingen Schenau Oct 26 '10 at 14:16
  • 2
    You can not say something like const int size 4; and then later do char str[size]? –  Oct 27 '10 at 11:29
  • 3
    @fahad: Not in C89. In C99 you can do so for a variable of automatic storage duration, but it is technically a VLA. – caf Oct 27 '10 at 12:01
  • 2
    @fahad: A VLA is a 'Variable Length Array'. It is an array whose size is not known until runtime and can only be used for automatic variables (i.e. can only be allocated on the stack). – Bart van Ingen Schenau Oct 27 '10 at 12:44
  • @caf & @Bart:thats something interesting,you can use stack at run time without the help of heap.. –  Oct 27 '10 at 13:43
  • @fahad: You can, but be warned that you will not get a reliable error if your array is too large for the remaining stack space. That lands you firmly in the land of undefined behaviour. – Bart van Ingen Schenau Oct 27 '10 at 14:19
  • In C++0x you can use `constexpr` to allow `const` variables to specify the size of an array. – Siqi Lin Oct 28 '10 at 01:11
  • 6
    @Exception: C and C++ are very different languages in their treatment of `const`. In C++, `const` can be used for proper constants, in C it can not. – Bart van Ingen Schenau Oct 28 '10 at 09:54
  • 2
    Not only the `const` cannot be used in non-VLA array sizes, it cannot be used for creating other `const` either. For example: `const uint8_t SECONDS_PER_MINUTE = 60U; const uint16_t SECONDS_PER_HOUR = 60U * SECONDS_PER_MINUTE;`. I miss a way to have typed constants (`const` is not constant, as @Bart wrote). – Gauthier Mar 01 '11 at 08:45
  • Well this is embarrassing, I'm surprised it took me this long to realize this. I just compiled with `gcc -pedantic` and realized that gnu89 uses variable length arrays with const. I don't know if using VLA for arrays that could be defined at compile time matters or not (e.g. for optimization). I'm finally being convinced that C and C++ are separate languages. Or at least it's important to know where they differ. – Z boson Apr 24 '15 at 08:57
  • `const int arsize = 10; int myar[arsize];` Compiled with gcc. Am I misinterpreting "_In places where the compiler requires a true constant (such as for array sizes for non-VLA arrays), using a const variable, such as fieldWidth is just not possible._"? `error: variable-sized object may not be initialized int myar[arsize] = {0,1};` compiles but isn't allowed to be initialized! – Minh Tran Dec 26 '16 at 12:28
  • 1
    @MinhTran: Note that the error message states "variable-sized object". This means that your array is a variable-length array (VLA). – Bart van Ingen Schenau Dec 26 '16 at 12:56
24

They're different.

const is just a qualifier, which says that a variable cannot be changed at runtime. But all other features of the variable persist: it has allocated storage, and this storage may be addressed. So code does not just treat it as a literal, but refers to the variable by accessing the specified memory location (except if it is static const, then it can be optimized away), and loading its value at runtime. And as a const variable has allocated storage, if you add it to a header and include it in several C sources, you'll get a "multiple symbol definition" linkage error unless you mark it as extern. And in this case the compiler can't optimize code against its actual value (unless global optimization is on).

#define simply substitutes a name with its value. Furthermore, a #define'd constant may be used in the preprocessor: you can use it with #ifdef to do conditional compilation based on its value, or use the stringizing operator # to get a string with its value. And as the compiler knows its value at compile time it may optimize code based on that value.

For example:

#define SCALE 1

...

scaled_x = x * SCALE;

When SCALE is defined as 1 the compiler can eliminate the multiplication as it knows that x * 1 == x, but if SCALE is an (extern) const, it will need to generate code to fetch the value and perform the multiplication because the value will not be known until the linking stage. (extern is needed to use the constant from several source files.)

A closer equivalent to using #define is using enumerations:

enum dummy_enum {
   constant_value = 10010
};

But this is restricted to integer values and doesn't have advantages of #define, so it is not widely used.

const is useful when you need to import a constant value from some library where it was compiled in. Or if it is used with pointers. Or if it is an array of constant values accessed through a variable index value. Otherwise, const has no advantages over #define.

Boann
  • 48,794
  • 16
  • 117
  • 146
Vovanium
  • 3,798
  • 17
  • 23
  • +1 this answer is the most complete, discussing the uses and limits of `enum` too, and the few cases where a `const`-qualified variable is actually what you want. – R.. GitHub STOP HELPING ICE Oct 26 '10 at 15:17
  • 1
    +1 for mentioning `enum` as an alternative - that should be used a lot more often. One major advantage is that the symbol is available to the debugger. – Jonathan Leffler Oct 26 '10 at 17:02
  • 1
    I'd rather think that it is debugger disadvantage to be not able of using defines, rather than advandage of enum to be usable in debugger. – Vovanium Oct 26 '10 at 18:56
  • 3
    And the advantage of `#define` over `enum` is that you can `#ifdef` to test its presence or use its value in `#if`, which makes it a lot more useful for controlling build-time choices or offering features which might be present on some platforms but not others. – R.. GitHub STOP HELPING ICE Oct 26 '10 at 19:27
  • R..: Yes! Of course! The only exception I know (not counting obvious optimisations) is GCC builtins which give control over compilation using constant expressions, like __builtin_choose_expr. – Vovanium Oct 26 '10 at 19:49
  • 1
    Newer versions of gdb / gcc *do* support the use of `#define` constants in the debugger (I think the `-gdb3` flag must be passed to gcc at compilation time to enable this). – psmears Dec 03 '10 at 14:21
  • 2
    The part about optimization is not true for any compiler doing link-time optimization. – Étienne Aug 30 '14 at 15:37
14

The reason is that most of the time, you want a constant, not a const-qualified variable. The two are not remotely the same in the C language. For example, variables are not valid as part of initializers for static-storage-duration objects, as non-vla array dimensions (for example the size of an array in a structure, or any array pre-C99).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
7

Expanding on R's answer a little bit: fieldWidth is not a constant expression; it's a const-qualified variable. Its value is not established until run-time, so it cannot be used where a compile-time constant expression is required (such as in an array declaration, or a case label in a switch statement, etc.).

Compare with the macro FIELD_WIDTH, which after preprocessing expands to the constant expression 10; this value is known at compile time, so it can be used for array dimensions, case labels, etc.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • 2
    So `const` operates more like java's `final`, allowing only one assignment? – C. Ross Oct 26 '10 at 14:50
  • @C. Ross: for all practical purposes, yes, although I think there may be some non-trivial differences in semantics (I don't use `const` all that much in C code, and I'm still at the bottom of the Java learning curve). – John Bode Oct 26 '10 at 15:11
6

To add to R.'s and Bart's answer: there is only one way to define symbolic compile time constants in C: enumeration type constants. The standard imposes that these are of type int. I personally would write your example as

enum { fieldWidth = 10 };

But I guess that taste differs much among C programmers about that.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
4

Although a const int will not always be appropriate, an enum will usually work as a substitute for the #define if you are defining something to be an integral value. This is actually my preference in such a case.

enum { FIELD_WIDTH = 16384 };
char buf[FIELD_WIDTH];

In C++ this is a huge advantage as you can scope your enum in a class or namespace, whereas you cannot scope a #define.

In C you don't have namespaces and cannot scope an enum inside a struct, and am not even sure you get the type-safety, so I cannot actually see any major advantage, although maybe some C programmer there will point it out to me.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • In C++, is there any 'nice' way to define an enum that will behave as an "int", including working with arithmetic operators? I know it's possible to define operator overloads, and such things can be done in a macro so as not to be too totally evil, but I'm unaware of any simple language keyword for something like that. Does one exist that I'm unaware of? If not, it seems an odd omission from the language, since C++ is supposed to allow easy adaptation of C programs. – supercat Oct 26 '10 at 15:36
2

According to K&R (2nd edition, page 211) the "const and volatile properties are new with the ANSI standard". This may imply that really old ANSI code did not have these keywords at all and it really is just a matter of tradition. Moreover, it says that a compiler should detect attempts to change const variables but other than that it may ignore these qualifiers. I think it means that some compilers may not optimize code containing const variable to be represented as intermediate value in machine code (like #define does) and this might cost in additional time for accessing far memory and affect performance.

Shlomi Loubaton
  • 733
  • 6
  • 9
1

The best way to define numeric constants in C is using enum. Read the corresponding chapter of K&R's The C Programming Language, page 39.

esoriano
  • 477
  • 6
  • 6
1

Some C compilers will store all const variables in the binary, which if preparing a large list of coefficients can use up a tremendous amount of space in the embedded world.

Conversely: using const allows flashing over an existing program to alter specific parameters.

Community
  • 1
  • 1
Nick T
  • 25,754
  • 12
  • 83
  • 121