[I snipped some gcc diagnostics from a previous poster here]
Okay, this is really weird. here are the 2 programs I compiled:
It is not all that weird, really
[program 1 snipped, might as well go with the all-"const"-qualified version]
and (2)
int main(int argc, char **argv) {
const char *const a[] = {"A"}; //constant pointer AND constand data
const char *const b[] = {"B"};
const char *const *z[] = {a,b};
return 0;
}
-----
I compiled both with GCC 3.3.2 like
$gcc -Wall -std=c99 -pedantic const_test_1.c
$gcc -Wall -std=c99 -pedantic const_test_2.c
Only reply I got was:
const_test_1.c: In function "main":
const_test_1.c:4: warning: unused variable `z'
(the same for the other file)
Try with -std=c89 instead of "-std=c99" (or, equivalently but the
only option available in older versions of gcc, "-ansi -pedantic");
the result should differ. Here is why.
First, "const" in C never means "constant". Its meaning is much
more like "read-only variable". In particular, this means that:
static const int size = 10;
static int a[size];
is *always* an error, in both C89 and C99, because arrays with
"static duration" (not quite the same thing as the "static" keyword)
must have integer constant expressions for any specified sizes.
(But -- while this is off-topic -- we might note that this works
fine in C++, where "const" *does* mean "constant" whenever possible.)
In C, a read-only variable is still a variable, and in some situations
is even *supposed* to change -- just not by being written-on in
the C code you are writing. For instance, the Standard specifically
allows one to write:
extern const volatile int tick_counter;
and arrange for one's system to "hook up" this variable to some
sort of constantly-running system clock (CPU clock, external timer,
or whatever). If this tick counter "ticks" at a known speed,
the clock() function might even be as simple as:
/* NB: the name "tick_counter" is not in the implementor's space
so if this were a real implementation we might want to use some
other variable name. */
clock_t clock(void) { return tick_counter; }
The "const" here means that you, the programmer, cannot change it,
but not that it is constant; and the "volatile" means that something
outside the C code changes it, so that the compiler cannot simply
read the variable once and then "remember" its value forever.
(Without the "volatile", the compiler is still supposed to treat
this as a read-only variable -- not a constant -- but *is* allowed
to assume it never changes as well. If tick_counter is never
explicitly initialized, it is implicitly initially zero, so a
compiler could assume that it is *always* zero and just "return 0"
-- not a very good clock() function.)
With that out of the way, consider the local variables again,
and compare against this code:
int main(void) {
int one = 1, two = 2;
int a[2] = { 1, 2 }; /* works in C89 and C99 */
int b[2] = { one, two }; /* works in C99 but not C89 */
return 0;
}
Here, "one" and "two" are clearly ordinary variables -- they are not
even "const"-qualified read-only variables. The initializers for
"b" are thus not at all constants, but C99 accepts this, just as
both C89 and C99 would accept:
int three = one + two;
The difference here is that C99 has extended the set of values
allowed in "aggregate initializers" -- the set of values in the
braces that initialize a[] and b[]. In C89, all aggregate initializers
had to be constant-expressions; in C99, they may be fully-general
expressions as long as they are initializing "automatic duration"
variables (meaning, pretty much, "ordinary local variables").
This means we can even write:
int f(int zog) {
int arr[4] = { zog, 0, 0, zog + 3 };
... more code ...
}
-- but only in C99, not in C89.
Finally, although it is not used in the original poster's problem,
there is one more significant change in C99 that relates back to
"const" meaning read-only rather than constant. C99 adds a new
type of array called a "variable length array" or VLA. Instead of
making the array "arr" have size 4, we can now do this:
int f(int zog) {
int arr[zog];
...
}
This array has all the usual attributes, plus one more -- its size
is determined at runtime. The expression:
sizeof arr
actually multiplies the value of "zog" by sizeof(int) at runtime.
VLAs have some interesting pitfalls -- the draft C99 standard I
keep handy notes that "unusual" control flow into a block (e.g.,
via a "goto") may not be allocated properly. (More precisely,
it says that "the behavior is undefined" if you jump into a block
that declares a VLA, rather than sequentially executing into it.
There are a couple of different "likely" compilation techniques
that give somewhat different but always bizarre results having to
do with not allocating space at the right point. These include
losing local variables and return addresses when control flows
back out of the block -- your code could crash in such a way that
even a good debugger cannot figure out what happened.)
In any case, VLAs can obscure the fact that "const" variables
are still variables. Compare these three functions:
int f1(int zog) { int arr[zog]; ... }
int f2(int n) { const int zog = n; int arr[zog]; ... }
int f3(void) { const int zog = 12; int arr[zog]; ... }
In f1(), it is obvious that "zog" is a variable. In f2(), "zog"
is *still* a variable, despite being "const"-qualified -- and in
f3(), "zog" is again still a variable, despite being const-qualified
and "obviously" always 12.
In all three functions, "arr" is a VLA, and "sizeof arr" is supposed
to be computed at runtime. In f3(), a C99 compiler is allowed to
"cheat": clearly you promised that zog is always exactly 12 -- by
making it 12 initially, promising never to change it, and not saying
that it might be changed "by magic" ("volatile") -- so the compiler
can replace each "zog" by 12, but only after emitting any required
diagnostics. (In this case, there are no required diagnostics.)
On the other hand, if we change "int arr[zog]" to "static int
arr[zog]", or if we are using a C89 compiler, VLAs are no longer
allowed. In this case, all three variants (f1, f2, f3) require
diagnostics (or more precisely, a C compiler has to emit "at least
one" diagnostic for the translation unit -- producing a single
warning, even though there are at least three errors, suffices to
meet the "letter of the law" as it were).
In summary:
- "const" never means "constant", but rather merely "read-only".
- Variables, even if read-only, are not constant expressions
and cannot be used where constant expressions are required.
- If a C compiler can prove that a read-only variable is always
some particular value, it can substitute in that value, but
only after emitting any required diagnostic.
- C89 requires constant expressions in quite a few more places
than does C99, including aggregate initializers and array sizes.
In particular, C99 removes the silly restriction in C89, where
(as a local variable):
int a = b;
is OK, but:
int a[1] = { b };
is not; and C99 adds variable length arrays.
Thus, if you have a C99 compiler, it may provide a better illusion
of const-qualified variables "acting like" constants, simply because
it allows variables in places C89 did not. But const-qualified
variables are still not constants, and sometimes only a "#define"
will do. (C's actual constants -- members of an "enum" type --
only give you integral constant expressions, i.e., not floating
point, and in practice may not be wide enough, e.g., you probably
cannot get a "long long" constant.)