Patterns: Shielding Inputs with const

One of the key worries I have heard from those ill-informed OOP programmers is that C cannot protect inputs you pass into functions. They use private fields and retrieval functions to ensure that the stored value is protected from unwanted modification.

However, this concern is addressed by C’s const keyword.

Taking a const value

There are times when we want to guarantee to all end users that our function will not modify particular values. Perhaps we are reading a stored key value in order to provide an access, but we want to ensure that nothing we do modifies that key.

If our function prototype and related documentation employ pass-by-reference (a common practice for large values) but do not employ the const keyword, the end user has no guarantee that the stored value will be the same after we’re done.

The difference between…

int func ( char * key)


int func( const char * const key )

…is that the first interface has full control of the key, while the second promises not to change either the pointer or the values at that pointer.

Creating a const value

Often enough, we want to create a value that never changes throughout operation. Perhaps it’s a static variable used to allocate arrays, or a static message we want to print out a number of times. In these cases, we use the const keyword to protect an initialized value from future changes. For example, we can create a constant integer value like this:

const int index = 256;

We can create a constant error message in the same way, but we usually make sure that we preserve both the pointer and the value stored therein:

const char * const error_message = "ERROR: Something done goofed\n";

Note: We have to assign the variable when we declare it, because it’s a constant value that we can never change again.

Keyword const rules

The const keyword prevents anyone from modifying a value. However, when dealing with pointers we actually have two values to preserve: the value of the pointer, and the value at that pointer.

The keyword const can be seen to preserve whatever comes right after it. That means that the statement…

const char *

…protects the pointer itself, while the statement…

char * const

…protects the value at that pointer. To preserve both, we use…

const char * const

Dangerous, but useful: Casting with const

We can actually protect our values by casting them to a const value. For example, if we know we don’t want a function to change something it can technically change (no const in the prototype), we can say something like this:

int func( char * key ) {}

char * value = "penny";

int i = func( (const char * const) value );

However, we can also cast away the const (which is where it gets dangerous). That means that the program can act as though the value is naturally unprotected. That looks something like this:

int func( const char * const key ) 
    (char *) key = (char *) calloc( 50, sizeof(char) );

Generally speaking, these actions are both considered unsafe. They can be extremely useful (for example, to free a const value inside of an allocated structure), but exercise extreme caution.

Lesson: The const keyword creates a contract in your code ensuring that the compiler protects the integrity of certain values.

Facebook Auto Publish Powered By :