Earlier, you saw that you can use const to create symbolic constants:
const double PI = 3.14159;
That was something you could do with the #define directive, too, but const additionally lets
you create constant arrays, constant pointers, and pointers to constants.
Listing 10.4showed how to use the const keyword to protect an array:
#define MONTHS 12
...
const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
If the program code subsequently tries to alter the array, you’ll get a compile-time error
message:
days[9] = 44; /* compile error */
Pointers to constants can’t be used to change values. Consider the following code:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * pd = rates; // pd points to beginning of the array
The second line of code declares that the type double value to which pd points is a const .
That means you can’t use pd to change pointed-to values:
*pd = 29.89; // not allowed
pd[2] = 222.22; // not allowed
rates[0] = 99.99; // allowed because rates is not const
Whether you use pointer notation or array notation, you are not allowed to use pd to change
the value of pointed-to data. Note, however, that because rates was not declared as a constant,
you can still use rates to change values. Also, note that you can make pd point somewhere
else:
pd++; /* make pd point to rates[1] -- allowed */
A pointer-to-constant is normally used as a function parameter to indicate that the function
won’t use the pointer to change data. For example, the show_array() function from Listing
10.14could have been prototyped as
void show_array(const double *ar, int n);
There are some rules you should know about pointer assignments and const. First, it’s valid to
assign the address of either constant data or non-constant data to a pointer-to-constant:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
const double * pc = rates; // valid
pc = locked; // valid
pc = &rates[3]; // valid
However, only the addresses of non-constant data can be assigned to regular pointers:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
double * pnc = rates; // valid
pnc = locked; // not valid
pnc = &rates[3]; // valid
This is a reasonable rule. Otherwise, you could use the pointer to change data that was
supposed to be constant.
A practical consequence of these rules is that a function such as show_array() can accept
the names of regular arrays and of constant arrays as actual arguments, because either can be
assigned to a pointer-to-constant:
show_array(rates, 5); // valid
show_array(locked, 4); // valid
Therefore, using const in a function parameter definition not only protects data, it also allows
the function to work with arrays that have been declared const .
A function such as mult_array(), however, shouldn’t be passed the name of a constant array
as an argument:
mult_array(rates, 5, 1.2); // valid
mult_array(locked, 4, 1.2); // bad idea
What the C standard says is that an attempt to modify const data, such as locked, using a non- const identifier, such as the mult_array() formal argument ar, results in undefined behavior.
There are more possible uses of const. For example, you can declare and initialize a pointer so that it can’t be made to point elsewhere. The trick is the placement of the keyword const : double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; double * const pc = rates; // pc points to beginning of the array pc = &rates[2]; // not allowed to point elsewhere *pc = 92.99; // ok -- changes rates[0] Such a pointer can still be used to change values, but it can point only to the location originally assigned to it. Finally, you can use const twice to create a pointer that can neither change where it’s pointing nor change the value to which it points: double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; const double * const pc = rates; pc = &rates[2]; // not allowed *pc = 92.99; // not allowed