The Volatile Qualifier in C

Mehvish Ashiq Oct 12, 2023
  1. the volatile Qualifier in C
  2. Use of the volatile Qualifier in C Programming
The Volatile Qualifier in C

Today’s tutorial teaches about the volatile qualifier in C. We will understand how and where we can use this qualifier in C programming.

the volatile Qualifier in C

We don’t use the volatile qualifier until we work on low-level programming. Here, low-level programming means the code that has to deal with the IO ports and Interrupt Service Routines (ISRs) that are supposed to interact with the hardware.

We all know that the compiler transforms the C code into the Machine code so that the executable file can be run without the availability of the source code.

Like other technologies, the compiler for C programming also converts the source code to machine code. Here, the compiler typically struggles to optimize the result (output) so that the minimum machine code needs to be run at the end.

This kind of optimization removes the unnecessary machine code for accessing the variable that is not updating from the compiler’s perspective.

Example Code:

int main() {
  int status = 0;
  while (status == 0) {
  }
}

The optimizing compiler will observe that the variable named status is not updated in a while loop in the code above. So, it is not needed to access this variable on each iteration.

The loop would be converted to an infinite loop (while(1)) by the compiler so that the machine code to read the status variable is not required.

The compiler does not know that the status variable can be updated in the current program at any point outside of the loop as well. For instance, when an IO operation occurs on the peripheral device.

Practically, we want the compiler to get access to a variable named status on every iteration, although the program is not changing it. Now, you may have a suggestion to turn-off the all compile optimizations for such C programs to avoid this sort of situation, but that is not the solution for the following reasons.

  • The implementation of the compilers varies from one to another.
  • Turning off all the compiler optimizations just because of one variable may cause issues because some of these optimizations may be needed at some other program’s portion.
  • Due to turning off the compiler optimizations, the low-level application can not work as it was supposed to. For instance, delayed execution.

This is where we need the volatile qualifier. The volatile keyword is nothing but a qualifier that we (as a programmer) use to tell the compiler that no optimizations are permitted for the status and is used as follows.

volatile int status = 0;

The one using this qualifier must consider the following properties of volatile.

  • It cannot remove the memory assignment.
  • The variables can’t be cached in the register.
  • The value can’t be changed in the assignment’s order.

Use of the volatile Qualifier in C Programming

In the code below, we print a message that says "Waiting..." until the thread changes the variable named done and then print another message saying "Okay, let's move on".

Example Code (without volatile):

#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>

bool done = false;

void *tfunc() {
  sleep(1);
  done = true;
  return NULL;
}

int main() {
  pthread_t t1;
  pthread_create(&t1, NULL, tfunc, NULL);
  printf("Waiting...\n");
  while (!done) {
  }
  printf("Okay, Let's move on");
}

Compile and run this program.

PS C:\Users\DelftStack\Desktop\C> gcc volatile.c -o volatile -lpthread
PS C:\Users\DelftStack\Desktop\C> ./volatile

If we look at the following output, things are normally going as expected.

Output:

Waiting...
Okay, Let's move on

Now, we turn on the compiler optimization for the same source code.

PS C:\Users\DelftStack\Desktop\C> gcc -O3 volatile.c -o volatile -lpthread
PS C:\Users\DelftStack\Desktop\C> ./volatile

It is not working as expected because it shows the output below.

Output:

Waiting...

The compiler looked at the while loop and noticed that the done variable is never updated in the while loop. Because the compiler does not realize that another thread modifies the global variable named done, and that’s the reason, the compiler changes the code for us and breaks the program.

This is where we use the volatile qualifier. We update the code and make the done variable volatile.

Example Code (with volatile):

#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>

volatile bool done = false;

void *tfunc() {
  sleep(1);
  done = true;
  return NULL;
}

int main() {
  pthread_t t1;
  pthread_create(&t1, NULL, tfunc, NULL);
  printf("Waiting...\n");
  while (!done) {
  }
  printf("Okay, Let's move on");
}

The code works fine even with the optimizations. See the following.

PS C:\Users\DelftStack\Desktop\C> gcc -O3 volatile.c -o volatile -lpthread
PS C:\Users\DelftStack\Desktop\C> ./volatile

Output:

Waiting...
Okay, Let's move on
Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook