Constant Joy (3)

Constant Joy (3)

The const keyword in C is a useful tool with different meanings in different contexts. In my first two articles, I looked at const applied to global variables and local variables and now I will consider the use of const to define read-only pointers.

Consider the definition of a pointer pReadOnly and its use of const.

1
int const *pReadOnly;

The variable pReadOnly is not constant, it can be changed. And the integer to which it points may or may not be constant, we cannot tell from the type of pReadOnly. We only know, from the type, that the value pointed to by pReadOnly cannot be changed using pReadOnly.

More modern languages than C might use a readonly keyword for this significantly different meaning. One use-case for a read-only pointer is for a memory-mapped register that supports reads but not writes. Consider the definition of a macro to access a 32-bit timer.

1
#define TIMER0 (*(uint32 volatile const *)0x12345678uL)

When the timer is running, writes to this address will be ignored. Without the const, we might accidentally write some incorrect code to time a function.

1
2
3
4
5
SuspendAllInterrupts();
TIMER0 = 0uL;
function();
functionDuration = TIMER0;
ResumeAllInterrupts();

This code will not work because the attempt to set TIMER0 to zero will do nothing and we will get nonsense results in functionDuration. Thanks to the const keyword the compiler will identify the write to TIMER0 as an error. Then we correct the code to use only timer reads.

1
2
3
4
5
6
SuspendAllInterrupts();
timerStart = TIMER0;
function();
/* MISRA justification: also underflow produces correct result */
functionDuration = TIMER0 - timerStart;
ResumeAllInterrupts();

Being able to make such a correction using compile-time errors, rather than having to debug run-time errors, is particularly beneficial with embedded development. Debugging cross-compiled, embedded code tends to be more time-consuming than in native platform development.

The other major use-case for a read-only pointer is when defining an API. Consider a function type signature published in a header file:

1
status_t TrasnmitMessage( msgId_t msgId, void const *pData );

By declaring pData as a read-only pointer, the API promises that TransmitMessage will not write to the pointed-to data. This not only helps the user of the API to know that this memory will not be changed by TransmitMessage, it can also be used by the compiler to optimize better. If the compiler has cached some or all of that pointed-to data in registers, the compiler does not need to flush those registers at the call to TransmitMessage. If you have ever wondered why memcpy has the type signature that it does, this is the reason.

In the next, and final, article on the const keyword in C, I will provide more examples and look at arguments for and against the use of const in industrial scale embedded software projects.