Qualifier in C (Const and Volatile)

Hi friends…… Today we are going to see Qualifier (Const and Volatile). This is the very important topic in c and embedded domain. In  an interview this would be the first question in C language. What is the volatile keyword? Why we are use Volatile? What is the difference between const and volatile? What is qualifier? etc. If you answered this question then they think you are familiar with C language. So before going to interview please go through this topic. I’m sure they will ask questions from qualifier. Let’s start.

Introduction

These are new in Standard C, although the idea of const has been borrowed from C++. Let us get one thing straight: the concepts of const and volatile are completely independent. A common misconception is to imagine that somehow const is the opposite of volatile and vice versa. They are unrelated and you should remember the fact. Since const declarations are the simpler, we’ll look at them first, but only after we have seen where both of these type qualifiers may be used. The complete list of relevant keywords is

  • char
  • long
  • float
  • volatile
  • short
  • signed
  • double
  • void
  • int
  • unsigned
  • const

In that list, const and volatile are type qualifiers, the rest are type specifiers. Various combinations of type specifiers are permitted:

  • char, signed char, unsigned char int, signed int, unsigned int
  • short int, signed short int, unsigned short int long int, signed long int, unsigned long int float
  • double
  • long double

A few points should be noted. All declarations to do with an int will be signed anyway, so signed is redundant in that context. If any other type specifier or qualifier is present, then the int part may be dropped, as that is the default.

The keywords const and volatile can be applied to any declaration, including those of structures, unions, enumerated types or typedef names. Applying them to a declaration is called qualifying the declaration—that’s why const and volatile are called type qualifiers, rather than type specifiers. Here are a few representative examples:

Don’t be put off. Some of them are deliberately complicated. What they mean will be explained later. Remember that they could also be further complicated by introducing storage class specifications as well! In fact, the truly spectacular

is a strong possibility in some real-time operating system kernels.

Const or Constant

The const keyword in a declaration establishes a variable whose value cannot be modified by assignment or by incrementing or decrementing. On an ANSI-compliant compiler, the code should produce an error message. You can, however, initialize a const variable.

Therefore, the following code is fine:

The preceding declaration makes no change a read-only variable. After it is initialized, it cannot be changed. You can use the const keyword to, for example, create an array of data that the program can’t alter:

Using const with Pointers and Parameter Declarations

Using the const keyword when declaring a simple variable and an array is pretty easy. Pointers are more complicated because you have to distinguish between making the pointer itself const and making the value that is pointed to const.

The declaration establishes that p points to a value that must remain constant. The value of p itself can be changed. For example, it can be set to point at another const value. Placing const after the type name and before the * means that the pointer can’t be used to change the pointed-to value. In short, a const anywhere to the left of the * makes the data constant, and a const to the right of the * makes the pointer itself constant.

In contrast, the declaration says that the pointer pr itself cannot have its value changed. It must always point to the same address, but the pointed-to value can change.

Finally, the declaration means both that ptr must always point to the same location and that the value stored at the location must not change.

One common use for this new keyword is declaring pointers that serve as formal function parameters. For example, suppose you have a function called display() that displays the contents of an array. To use it, you would pass the name of the array as an actual argument, but the name of an array is an address. That would enable the function to alter data in the calling function. But the following prototype prevents this from happening:

In a prototype and a function header, the parameter declaration const int array[] is the same as const int * array, so the declaration says that the data to which array points cannot be changed.

The ANSI C library follows this practice. If a pointer is used only to give a function access to values, the pointer is declared as a pointer to a const-qualified type. If the pointer is used to alter data in the calling function, the const keyword isn’t used. For example, the ANSI C declaration for strcat() is this:

Recall that strcat() adds a copy of the second string to the end of the first string. This modifies the first string, but leaves the second string unchanged. The declaration reflects this.

Difference between const char *p, char const *p and char *const p

This is also the important question that interviewer. And it is most confused topic in C.

  • const char * = Can’t change pointed characters using pointer, can change pointer
  • char const *p = Can’t change pointed characters using pointer, can change pointer
  • char * const p = Address cannot be changed, Must be initialize at declaration time
  • const char * const p = Both address and value cannot be changed, Initialized at declaration time

Trick or Tips:
Another thumb rule is to check where const is:

before * => Can’t change pointed characters using pointer
after *    => Can’t change pointer stored address

Can we change the value of const variable by using pointer?

Yes. We can. But it is only applicable for constant local variable. We can’t modify the Constant global variable, because const global variable is stored into read only memory. Const local variable is stored into stack memory.

Code 1:

Output:

Code 2:

Output:

We will not get output.

Volatile

VOLATILE means: – UNSTABLE, UNPREDICTABLE…etc.

So, the basic meaning of volatile is we can’t predict what is going to happen next. The significance of volatile keyword in programming language is to inform/tell the compiler not to pre-predict/assume/believe/presume the value of the particular variable which has been declared as volatile.

Code optimization

Look at the code 1 and code 2, below. Suppose:-
Code 1:

code 2:

In code-1, compiler will optimize the code it will ignore the else part, because the variable “x” will never ever become other than 0.

In code-2, the compiler will never ever optimized the code (else part) because, by declaring x as volatile compiler comes to know that this variable can change at any point of time. So compiler does not ignore the else part.

Why/When do we need volatile?

In following case we need to use volatile variable.

  • Memory-mapped peripheral registers
  • Global variables modified by an interrupt service routine
  • Global variables within a multi-threaded application

If we do not use volatile qualifier the following problems may arise:

  • Code that works fine-until you turn optimization on
  • Code that works fine-as long as interrupts are disabled
  • Flaky hardware drivers
  • Tasks that work fine in isolation-yet crash when another task is enabled

The above code sets the value in var to 1. It then starts to poll that value in a loop until the value of var becomes 10.

An optimizing compiler will notice that no other code can possibly change the value stored in ‘var’, and therefore assume that it will remain equal to 0 at all times. The compiler will then replace the function body with an infinite loop, similar to this:

Declaration of volatile

Include the keyword volatile before or after the data type in the variable.

Pointer to a volatile variable

Above statements implicate ‘var’ is a pointer to a volatile integer.

Volatile pointers to non-volatile variables

Here var is a volatile pointer to a non-volatile variable/object. This type of pointer are very rarely used in embedded programming.

Volatile pointers to volatile variables

If we qualify a struct or union with a volatile qualifier, then the entire contents of the struct/union becomes volatile. We can also apply the volatile qualifier to the individual members of the struct/union.

Usages of volatile qualifier

1. Peripheral registers

Most embedded systems consist of a handful of peripherals devices. The value of the registers of these peripheral devices may change asynchronously. Lets say there is an 8-bit status register at address 0x1234 in any hypothetical device. What we need to do is to poll this status register until it becomes non-zero. The following code snippet is an incorrect implementation of this scenario/requirement:

Now no code in proximity attempts to change the value in the register whose address(0x1234) is kept in the ‘ptr’ pointer. A typical optimizing compiler(if optimization is turned ON) will optimize the above code as below:

What the assumes while optimizing the code is easy to interpret. It simply takes the value stored at the address location 0x1234(which is stored in ‘ptr’) into accumulator and it never updates this value as because apparently the value at the address 0x1234 never gets changed(by any nearby code). So, as the code suggests, the compiler replaces it with an infinite loop (comparing the initial zero value stored at the address 0x1234 with a constant ‘zero’). As the value stored at this address would initially be zero and it is never updated, this loop goes forever. The code beyond this point would never get executed and the system would go into a hanged state.

So what we essentially need to do here is to force the compiler to update the value stored at the address 0x1234 whenever it does the comparison operation. The volatile qualifier does the trick for us. Look at the code snippet below:

The assembly for the above code should be:

So now at every loop the actual value stored at the address 0x1234(which is stored in the ‘ptr’) is fetched from the peripheral memory and checked whether it’s zero or non-zero; as soon as the code finds the value to be non-zero the loop breaks. And that’s what we wanted.
Subtler problems tend to arise with registers that have special properties. For instance, a lot of peripherals contain registers that are cleared simply by reading them. Extra (or fewer) reads than you are intending can cause quite unexpected results in these cases.

2. ISR(Interrupt Service Routine)

Sometimes we check a global variable in the main code and the variable is only changed by the interrupt service routine. Lets say a serial port interrupt tests each received character to see if it is an ETX character (presumably signifying the end of a message). If the character is an ETX, the serial port ISR sets a particular variable, say ‘etx_rcvd’. And from the main code somewhere else this ‘etx_rcvd’ is checked in a loop and untill it becomes TRUE the code waits at this loop. Now lets check the code snippet below:

This code may work with optimization turned off. But almost all the optimizing compiler would optimize this code to something which is not intended here. Because the compiler doesn’t even have any hint that etx_rcvd can be changed outside the code somewhere( as we saw within the serial port ISR). So the compiler assumes the expression !ext_rcvd would always be true and would replace the code with infinite loop. Consequently the system would never be able to exit the while loop. All the code after the while loop may even be removed by the optimizer or never be reached by the program. Some compiler may throw a warning, or some may not, depends completely on the particular compiler.
The solution is to declare the variable etx_rcvd to be volatile. Then all of your problems (well, some of them anyway) will disappear.

3. Multi-threaded applications

Often tasks/threads involved in a multi-threaded application communicate via a shared memory location i.e. through a global variable. Well, a compiler does not have any idea about preemptive scheduling or to say, context switching or whatsoever. So this is sort of same problem as we discussed in the case of an interrupt service routine changing the peripheral memory register. Embedded Systems Programmer has to take care that all shared global variables in an multi threaded environment be declared volatile. For example:

This code will likely fail once the compiler’s optimizer is enabled. Declaring ‘cntr’ to be volatile is the proper way to solve the problem. Some compilers allow you to implicitly declare all variables as volatile. Resist this temptation, since it is essentially a substitute for thought. It also leads to potentially less efficient code.

Can you have constant volatile variable? You can have a constant pointer to a volatile variable but not a constant volatile variable. Consider the following two blocks of a program, where second block is the same as first but with volatile keyword. Gray text between lines of C code means i386/AMD64 assembler compiled from this code.

In first block variable ‘flag’ could be cached by compiler into a CPU register, because it does not have volatile qualifier. Because no one will change value at a register, program will hang in an infinite loop (yes, all code below this block is unreachable code, and compiler such as Microsoft Visual C++ knows about it). Also this loop was optimized in equivalent program with the same infinite loop, but without involving variable initialization and fetching. ‘jmp label’ means the same as ‘goto label’ in C code.

Second block have volatile qualifier and have more complex assembler output (initializing ‘flag’ with ‘mov’ Instruction, in a loop fetching this flag into CPU register ‘eax’ with a ‘mov’ instruction, comparing fetched value with zero with ‘test’ instruction, and returning to the beginning of the loop if ‘flag’ was not equal to zero. ‘jne’ means ’goto if not equal’). This is all because volatile keyword prohibits compiler to cache variable value into CPU register, and it is fetched in all loop iterations. Such code is not always is an infinite loop, because another thread in the same program potentially could change value of variable ‘flag’ and first thread will exit the loop.

It is important to understand that volatile keyword is just a directive for compiler and it works only at a compile-time. For example, the fact of using interlocked operation differs from just a compiler option, since special assembler commands are produced. Thus, interlocked instructions are most like to hardware directives, and they work at a run-time.

Can a variable be both Volatile and Const?

This also the important interview question. 

  • Const means the program cannot modify the value
  • Volatile means the value may be arbitrarily modified outside the program.

The two are separate and not mutually exclusive. Use them together, for instance, in the case of reading a hardware status register. const prevents the value from being stomped on before compilation, while volatile tells the compiler that this value can be changed at any time external to the program.
So it means that the program cannot modify the variable’s value, but the value can be modified from the outside, thus no optimisations will be performed on the variable.
This,

Will thus satisfy both requirements and prevent an optimizing compiler from incorrectly optimizing the code, that it would do if only “const” were used.

Conclusion

1. A volatile variable can be changed by the background routine of pre-processor. This background routine may be interrupt signals by microprocessor, threads, real times clocks etc.
2. In simple word, we can say a value volatile variable which has stored in the memory can be by any external sources.
3. Whenever compiler encounter any reference of volatile variable is always load the value of variable from memory so that if any external source has modified the value in the memory compiler will get its updated value.
4. Working principle of volatile variable is opposite to the register variable in c. Hence volatile variables take more execution time than non-volatile variables.

 

Download our new Android app. You can learn all Embedded Tutorials from your Android Phone easily.

Click Here to Download App!