Der Volatile Qualifier in C

Mehvish Ashiq 12 Oktober 2023
  1. der volatile Qualifier in C
  2. Verwendung des Qualifiers volatile in der C-Programmierung
Der Volatile Qualifier in C

Das heutige Tutorial behandelt den volatile Qualifier in C. Wir werden verstehen, wie und wo wir diesen Qualifier in der C-Programmierung verwenden können.

der volatile Qualifier in C

Wir verwenden den flüchtigen Qualifizierer erst, wenn wir an der Low-Level-Programmierung arbeiten. Low-Level-Programmierung bedeutet hier den Code, der mit den IO-Ports und Interrupt Service Routines (ISRs) umgehen muss, die mit der Hardware interagieren sollen.

Wir alle wissen, dass der Compiler den C-Code in den Maschinencode umwandelt, sodass die ausführbare Datei ohne die Verfügbarkeit des Quellcodes ausgeführt werden kann.

Wie andere Technologien wandelt auch der Compiler für die C-Programmierung den Quellcode in Maschinencode um. Hier tut sich der Compiler typischerweise schwer, das Ergebnis (Ausgabe) so zu optimieren, dass am Ende nur noch minimaler Maschinencode ausgeführt werden muss.

Diese Art der Optimierung entfernt den unnötigen Maschinencode für den Zugriff auf die Variable, die aus Sicht des Compilers nicht aktualisiert wird.

Beispielcode:

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

Der optimierende Compiler wird feststellen, dass die Variable mit dem Namen status nicht in einer while-Schleife im obigen Code aktualisiert wird. Es ist also nicht erforderlich, bei jeder Iteration auf diese Variable zuzugreifen.

Die Schleife würde vom Compiler in eine Endlosschleife (while(1)) umgewandelt, so dass der Maschinencode zum Lesen der status-Variablen nicht benötigt wird.

Der Compiler weiß nicht, dass die Variable status auch im laufenden Programm an jeder Stelle ausserhalb der Schleife aktualisiert werden kann. Zum Beispiel, wenn eine IO-Operation auf dem Peripheriegerät auftritt.

Praktisch möchten wir, dass der Compiler bei jeder Iteration Zugriff auf eine Variable namens status erhält, obwohl das Programm sie nicht ändert. Jetzt haben Sie vielleicht den Vorschlag, alle Kompilierungsoptimierungen für solche C-Programme auszuschalten, um diese Art von Situation zu vermeiden, aber das ist aus den folgenden Gründen nicht die Lösung.

  • Die Implementierung der Compiler variiert von einem zum anderen.
  • Das Deaktivieren aller Compiler-Optimierungen nur wegen einer Variablen kann zu Problemen führen, da einige dieser Optimierungen möglicherweise in einem Teil eines anderen Programms benötigt werden.
  • Aufgrund der Deaktivierung der Compiler-Optimierungen kann die Low-Level-Anwendung nicht so funktionieren, wie sie sollte. Zum Beispiel verzögerte Ausführung.

Hier brauchen wir den Qualifier volatile. Das Schlüsselwort volatile ist nichts anderes als ein Qualifier, mit dem wir (als Programmierer) dem Compiler mitteilen, dass für den status keine Optimierungen erlaubt sind und wird wie folgt verwendet.

volatile int status = 0;

Wer diesen Qualifier verwendet, muss die folgenden Eigenschaften von volatile berücksichtigen.

  • Es kann die Speicherbelegung nicht aufheben.
  • Die Variablen können nicht im Register zwischengespeichert werden.
  • Der Wert kann in der Reihenfolge der Zuweisung nicht geändert werden.

Verwendung des Qualifiers volatile in der C-Programmierung

Im folgenden Code drucken wir eine Nachricht mit der Aufschrift Warten..., bis der Thread die Variable mit dem Namen done ändert, und geben dann eine weitere Nachricht mit der Aufschrift "Okay, let's move on" aus.

Beispielcode (ohne 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");
}

Kompilieren Sie dieses Programm und führen Sie es aus.

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

Wenn wir uns die folgende Ausgabe ansehen, laufen die Dinge normalerweise wie erwartet.

Ausgang:

Waiting...
Okay, Let's move on

Jetzt schalten wir die Compiler-Optimierung für denselben Quellcode ein.

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

Es funktioniert nicht wie erwartet, da es die folgende Ausgabe zeigt.

Ausgang:

Waiting...

Der Compiler hat sich die while-Schleife angesehen und festgestellt, dass die done-Variable in der while-Schleife nie aktualisiert wird. Weil der Compiler nicht merkt, dass ein anderer Thread die globale Variable namens done modifiziert, und das ist der Grund, ändert der Compiler den Code für uns und bricht das Programm ab.

Hier verwenden wir den Qualifier volatile. Wir aktualisieren den Code und machen die done-Variable volatile.

Beispielcode (mit 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");
}

Der Code funktioniert auch mit den Optimierungen einwandfrei. Siehe Folgendes.

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

Ausgang:

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