Why consting is good
In Perl, we have the use constant
pragma to define unchanging values. The Readonly module extends this to allow arrays and hashes to be non-modifiable as well.
In C, we have const
numbers and pointers, and using them wherever possible lets us put safety checks in our code, and the compiler will watch over our shoulders.
const
numbers
The easiest way to use the const
qualifier is by flagging numbers that are set at the top of a block. For example:
int max_elements;
max_elements = nusers * ELEMENTS_PER_USER;
...
array[max_elements++] = n;
/* but you really meant array[max_elements] = n++; */
Adding a const
qualifier means you can't accidentally modify max_elements
.
const int max_elements = nusers * ELEMENTS_PER_USER;
const
pointers
If a pointer is qualified as const, then its contents cannot be modified. This lets the compiler protect you from doing naughty things to yourself.
Here are two examples for functions you're familiar with:
int strlen( const char *str );
void memset( char *ptr, char value, int length );
In the case of strlen
, the caller is guaranteed that any string passed in won't be modified. How terrible it would be if it was possible for strlen
to modify what gets passed in!
The const on strlen
's parameter also lets the compiler know that strlen
can't be initializing what's passed in. For example:
char buffer[ MAX_LEN ];
int n = strlen( buffer );
The compiler knows that buffer
hasn't been initialized, and that strlen
can't be initializing it, so the call to strlen
is on an uninitialized value.
Without the const, the compiler assumes that the contents of any pointer are getting initialized or modified.
const
arrays
Consting arrays makes all the values in the array non-modifiable.
const int days_per_month[] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
You don't want to be able to do days_per_month[1] = 4;
, right? (We'll ignore that about 25% of the time you want days_per_month[1]
to be 29.)
Mixing consts
Combining const
s on a pointer and its contents can get confusing. It's important to know on which side of the asterisk that the const
lies.
To the left of the asterisk, the characters are constant. To the right of the asterisk, the pointer is constant.
Note the difference between a pointer to constant characters:
/* Pointer to constant characters */
const char *str = "Don't change me.";
str++; /* legal, now points at "o" */
*str = "x"; /* not legal */
and a constant pointer to characters:
/* Constant pointer to characters */
char * const str = buffer;
str++; /* not legal */
*str = 'x'; /* buffer[0] is now 'x' */
Note the difference between which side of the asterisk that the const
is on.
You can also combine the two, with a constant pointer to constant characters:
const char * const str = "Don't change me";
or even an array of constant pointers to constant characters:
const char * const days[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
If you see a declaration you don't understand, use cdecl
. It's standard in many C compiler suites, and is freely available around the net.
$ cdecl
Type `help' or `?' for help
cdecl> explain const char * str;
declare str as pointer to const char
cdecl> explain char * const str;
declare str as const pointer to char