Differences with C
Constants were introduced in early
versions of C++ while the Standard C specification was still being finished.
Although the C committee then decided to include const in C, somehow
it came to mean for them
“an ordinary variable that cannot be changed.” In C, a const
always occupies storage and its name is global. The C compiler cannot treat a
const as a compile-time constant. In C, if you say
const int bufsize = 100;
char buf[bufsize];
you will get an error, even though it
seems like a rational thing to do. Because bufsize occupies storage
somewhere, the C compiler cannot know the value at compile time. You can
optionally say
const int bufsize;
in C, but not in C++, and the C compiler
accepts it as a declaration indicating there is storage allocated elsewhere.
Because C defaults to external linkage
for consts, this makes
sense. C++ defaults to internal linkage
for consts so if you want
to accomplish the same thing in C++, you must explicitly change the linkage to
external using extern:
extern const int bufsize; // Declaration only
This line also works in
C.
In C++, a const doesn’t
necessarily create storage. In C a const always creates
storage. Whether or not storage is reserved for a
const in C++ depends on how it is used. In general, if a const is
used simply to replace a name with a value (just as you would use a
#define), then storage doesn’t have to be created for the
const. If no storage is created (this depends on the complexity of the
data type and the sophistication of the compiler), the values may be folded into
the code for greater efficiency after type checking, not before, as with
#define. If, however, you take an address of a
const (even unknowingly,
by passing it to a function that takes a reference argument) or you define it as
extern, then storage is created for the const.
In C++, a const that is outside
all functions has file scope
(i.e., it is invisible outside the file). That is, it defaults to internal
linkage. This is very different from all other identifiers in C++ (and from
const in C!) that default to external linkage. Thus, if you declare a
const of the same name in two different files and you don’t take
the address or define that name as extern, the ideal C++ compiler
won’t allocate storage for the const, but simply fold it into the
code. Because const has implied file scope, you
can put it in C++ header files with no conflicts at link time.
Since a const in C++ defaults to
internal linkage, you
can’t just define a const in one file and reference it as an
extern in another file. To give a const external
linkage so it can be referenced
from another file, you must explicitly define it as
extern,
like this:
extern const int x = 1;
Notice that by giving it an initializer
and saying it is extern, you force storage to be created for the
const (although the compiler still has the option of doing constant
folding here). The initialization establishes this as a definition, not a
declaration. The declaration:
extern const int x;
in C++ means that the definition exists
elsewhere (again, this is not necessarily true in C). You can now see why C++
requires a const definition to have an initializer: the initializer
distinguishes a declaration from a
definition (in C it’s always a definition, so no
initializer is necessary). With an extern
const declaration, the compiler cannot do constant folding because it
doesn’t know the value.
The C approach to const is not
very useful, and if you want to use a named value inside a constant expression
(one that must be evaluated at compile time), C almost
forces you to use #define in the
preprocessor.