Combining C's volatile and const keywords

February 24, 2012


How to use C's const keyword
The const keyword is can be used to modify parameters, as well as in variable declarations. Here we are only interested in the use of const as a type qualifier in variable declarations, as in:
uint16_t const max_temp_in_c = 1000;
This declaration creates a 16-bit unsigned integer value of 1,000 with a scoped name of max_temp_in_c. In C, this variable will exist in memory at run time, but will typically be located, by the linker, in a non-volatile memory area such as ROM or flash. Any reference to the const variable will result in a read from that location. (In C++, a const integer may no longer exist as an addressable location in run-time memory.)

Any attempt the code makes to write to a const variable directly (i.e., by its name) will result in a compile-time error. To the extent that the const variable is located in ROM or flash, an indirect write (i.e., via a pointer to its address) will also be thwarted--though at run time, obviously.

Another use of const is to mark a hardware register as read-only. For example:
uint8_t const * p_latch_reg = 0x10000000;
Declaring the pointer this way, any attempt to write to that physical memory address via the pointer (e.g., *p_latch_reg = 0xFF;) should result in a compile-time error.

[See my Coding Standard Rule #2: Use const whenever possible for best practice recommendations on the use of const by itself.]

How to use const and volatile together
Though the essence of the volatile ("ever-changing") and const ("read-only") keywords may seem at first glance opposed, they are in fact orthogonal. Thus sometimes it makes sense to use both keywords in the declaration of a single variable or pointer. Typical use scenarios for both involve either pointers to memory-mapped hardware registers or variables located in shared memory areas.

(#1) Constant addresses of hardware registers
The following declaration uses both const and volatile in the frequently useful scenario of declaring a constant pointer to a volatile hardware register.
uint8_t volatile * const p_led_reg = (uint8_t *) 0x80000;
The proper way to read a complex declaration like this is from the name of the variable back to the left, as in:

"p_led_reg IS A constant pointer TO A volatile 8-bit unsigned integer."

Reading it that way, we can see that the keyword const modifies only the pointer (i.e., the fixed address 80000h), which should obviously not change at run time. Whereas the keyword volatile modifies only the type of integer. This style of declaration is actually quite useful and is a much safer version of the volatile-only declaration of p_led_reg that appears earlier in this article.

In this example, adding const means that the common typo of a missed pointer dereference (i.e., '*') can be caught at compile time. That is, the mistaken code
p_led_reg = LED1_ON; 
won't overwrite the address with the non-80000h value of LED1_ON. The compiler error leads us to correct this to:
*p_led_reg = LED1_ON;
which is almost certainly what we meant to write in the first place.

(#2) Read-only shared-memory buffer
Another use for a combination of const and volatile is where you have two or more processors communicating via a shared memory area and you're coding the side of this communications that will only be reading from a shared memory buffer. In this case you could declare variables such as:
int const volatile comm_flag;

uint8_t const volatile comm_buffer[BUFFER_SIZE];
Of course, you'd usually want to instruct the linker to place these global variables at the correct addresses in the shared memory area or to declare the above as pointers to specific physical memory addresses. In the case of pointers, the use of const and volatile may become even more complex, as in the next category.
< Previous
Page 2 of 3
Next >

Loading comments...

Parts Search Datasheets.com

KNOWLEDGE CENTER