views:

931

answers:

8

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?

+8  A: 

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..
+62  A: 

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
+1 for correct answer among a sea of wrong ones from C++ coders who don't know C.
R..
@Bart is there any reason to do the same thing for a character constant?
C. Ross
@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
You can not say something like const int size 4; and then later do char str[size]?
fahad
@fahad: Not in C89. In C99 you can do so for a variable of automatic storage duration, but it is technically a VLA.
caf
@caf:whats VLA?
fahad
@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
fahad
@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
In C++0x you can use `constexpr` to allow `const` variables to specify the size of an array.
Exception
@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
+3  A: 

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
R..
JeremyP
Lots of C programmers use `strchr` to search for a character instead of `strtof` because `strtof` was not added until C99.
R..
+6  A: 

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
So `const` operates more like java's `final`, allowing only one assignment?
C. Ross
@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
+8  A: 

No, they're different.

"const" is just a qualifier, that says to variable(!) that it cannot be changed in runtime. But all other attributes of variable persist: it has allocated storage, and this storage may be addressed. So any code do not just use it as literal, but refers to it by accessing to specified memory location (only in case of statc it can be simply optimized), and convertiong it to suitable data type in runtime. And as it have allocated storage, if you add it to header and include in several c sources, you'll get linkage error of multiple symbol definition until you mark them as extern. And in this case compiler shoudn't optimize code against it's actual value (until global optimisation is on).

"#define" simply substitutes a name with its value. Futhermore, "#define"d constant may be used in preprocessor: #ifdef to have conditional compilation upon its value, stringize # to have string with its value. And as compiler may analyse its value it may optimize code which depents on it and make required conversions in compile time.

For this example code:

#define SCALE 1

...

scaled_x = x * SCALE;

While it have SCALE defined to 1 compiler simply removes multiplications as it knows that x * 1 == x, but if it have (extern) const, it will heed to compile const fetch and actual multiplication because it cannot realize what will be constant as it will be known only on linking stage. (Extern is needed to use constant from several source files).

More closer equivalent of defining is using enumerations:

enum dummy_enum{
   constant_value = 10010
};

But it restricted to integer values and have'nt advantages of define, so it is not widely used.

Const is useful when need to import onstant value from some library where it compiled in. Or if it used with pointers. Or it is array of constant values accessed through varable index value. Until one need this const have no advantages over define.

Vovanium
+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..
+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
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
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..
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
+2  A: 

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
+2  A: 

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
@CashCow: 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
+1  A: 

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.

Nick T
Regarding your "conversely", this would be highly implementation-specific. A good implementation would inline all `const` variables and optimize them out if their addresses are never taken, just like how it inlines functions.
R..
I've seen several implementations that use/abuse that in embedded systems across a couple platforms (AVR, HCS08). They're defined as `const` but with some compiler directives that cause them to be stored at a defined memory address. They seem to work just like a `* const`
Nick T
@R..: Things get really nasty in C at the embedded level. You can't assume much about C compilers.
Matt Joiner
@Nick: Indeed that's a huge abuse. The proper implementation is `#define myregister (*(volatile const uint32_t *)0xdecafbad)` or similar.
R..