Deep Analysis of Const Qualifier in C
The keyword const
indicates a variable that is read-only (i.e., cannot be changed at run-time). It does not indicate a compile-time constant. Therefore, all of the usual attributes of variables apply; specifically, it is allocated addressable storage space.
Unlike with #define
, your constant is not necessarily inlined by the compiler. Rather, the compiler will create a symbol corresponding to your const
declaration in the object file so that it can be accessed from other code files—remember that const
objects have external linkage by default in C (although some compilers will still inline the constant value within the file where it is defined).
The reason the code snippet that you posted "works" is because the unary operator &
can be applied to any lvalue, which includes a const
object. Though the behavior here is undefined, I suspect that your compiler is detecting this usage and ensuring that your const
declaration is given address space, and therefore not inlining it, even within the file it is declared.
EDIT: Also see: http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html
Let's look at what is meant when const is used. It's really quite simple: const means that something is not modifiable, so a data object that is declared with const as a part of its type specification must not be assigned to in any way during the run of a program. It is very likely that the definition of the object will contain an initializer (otherwise, since you can't assign to it, how would it ever get a value?), but this is not always the case. For example, if you were accessing a hardware port at a fixed memory address and promised only to read from it, then it would be declared to be const but not initialized.
Taking the address of a data object of a type which isn't const and putting it into a pointer to the const-qualified version of the same type is both safe and explicitly permitted; you will be able to use the pointer to inspect the object, but not modify it. Putting the address of a const type into a pointer to the unqualified type is much more dangerous and consequently prohibited (although you can get around this by using a cast). For example...
Modifying your code to print the value:
#include <stdio.h>
main()
{
const int j=20;
int *p;
p=&j;
(*p)++;
printf("%d\n", j);
return 0 ;
}
The above code, when compiled with gcc 4.3.2 at -O1
optimisation or above, results in the output 20
rather than 21
. This shows that it hasn't really "worked" - it just appeared to work.
A const
qualifier isn't a request to have the variable placed in a particular kind of memory - it's a promise from you to the compiler, that you won't modify that variable by any means. The compiler can rely on your promise to optimise the code that's produced - and if you break your promise, it won't necessarily break in an obvious way, it may just produce strange results.
Per the C standard (n1256 draft):
6.7.3 Type qualifiers
...
3 The properties associated with qualified types are meaningful only for expressions that are lvalues.114)
...
5 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.115)
...
114) The implementation may place aconst
object that is notvolatile
in a read-only region of storage. Moreover, the implementation need not allocate storage for such an object if its address is never used.
115) This applies to those objects that behave as if they were defined with qualified types, even if they are never actually defined as objects in the program (such as an object at a memory-mapped input/output address).
In short, a const
-qualified object may be stored in a different area from non-const
-qualified objects, but not necessarily.
The const
qualifier is an instruction to the compiler to reject code that attempts to modify that object directly; attempts to modify the object indirectly (as you do in the second code snippet) results in undefined behavior, meaning any result is possible.