C の揮発性修飾子

Mehvish Ashiq 2023年10月12日
  1. C の volatile 修飾子
  2. C プログラミングでの volatile 修飾子の使用
C の揮発性修飾子

今日のチュートリアルでは、C の volatile 修飾子について説明します。C プログラミングでこの修飾子をどこでどのように使用できるかを理解します。

C の volatile 修飾子

低レベルのプログラミングに取り組むまで、volatile 修飾子は使用しません。 ここで、低レベルのプログラミングとは、ハードウェアとやり取りすることになっている IO ポートと割り込みサービス ルーチン (ISR) を処理する必要があるコードを意味します。

ソース コードがなくても実行可能ファイルを実行できるように、コンパイラが C コードをマシン コードに変換することは誰もが知っています。

他のテクノロジと同様に、C プログラミング用のコンパイラもソース コードをマシン コードに変換します。 ここで、コンパイラは通常、結果 (出力) を最適化するのに苦労するため、最後に最小限のマシン コードを実行する必要があります。

この種の最適化により、コンパイラの観点から更新されていない変数にアクセスするための不要なマシン コードが削除されます。

コード例:

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

最適化コンパイラは、上記のコードの while ループで status という名前の変数が更新されていないことを確認します。 したがって、反復ごとにこの変数にアクセスする必要はありません。

ループはコンパイラによって無限ループ (while(1)) に変換されるため、status 変数を読み取るためのマシン コードは必要ありません。

コンパイラは、現在のプログラムでループ外の任意の時点でも status 変数を更新できることを知りません。 たとえば、周辺機器で IO 操作が発生した場合です。

実際には、プログラムは変更していませんが、繰り返しごとにコンパイラが status という名前の変数にアクセスできるようにする必要があります。 この種の状況を回避するために、そのような C プログラムのすべてのコンパイル最適化をオフにすることを提案する場合がありますが、次の理由により、それは解決策ではありません。

  • コンパイラの実装は、それぞれ異なります。
  • 1つの変数のためだけにすべてのコンパイラー最適化をオフにすると、問題が発生する可能性があります。これは、これらの最適化の一部が他のプログラムの部分で必要になる可能性があるためです。
  • コンパイラの最適化がオフになっているため、低レベルのアプリケーションが想定どおりに動作しません。 たとえば、実行の遅延。

ここで volatile 修飾子が必要です。 volatile キーワードは、(プログラマーとして) status の最適化が許可されていないことをコンパイラーに伝えるために使用する修飾子にすぎず、次のように使用されます。

volatile int status = 0;

この修飾子を使用する場合は、次の volatile のプロパティを考慮する必要があります。

  • メモリの割り当てを削除することはできません。
  • 変数はレジスタにキャッシュできません。
  • 割り当ての順序で値を変更することはできません。

C プログラミングでの volatile 修飾子の使用

以下のコードでは、スレッドが done という名前の変数を変更するまで "Waiting..." というメッセージを出力し、次に "Okay, let's move on" という別のメッセージを出力します。

コード例 (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");
}

このプログラムをコンパイルして実行します。

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

次の出力を見ると、通常は期待どおりに進んでいます。

出力:

Waiting...
Okay, Let's move on

ここで、同じソース コードに対してコンパイラの最適化をオンにします。

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

以下の出力が表示されるため、期待どおりに機能していません。

出力:

Waiting...

コンパイラは while ループを調べて、done 変数が while ループで更新されていないことに気付きました。 コンパイラは、別のスレッドが done という名前のグローバル変数を変更していることを認識していないため、コンパイラはコードを変更してプログラムを中断します。

ここで volatile 修飾子を使用します。 コードを更新し、done 変数を volatile にします。

コード例 (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");
}

最適化を行ってもコードは正常に動作します。 以下を参照してください。

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

出力:

Waiting...
Okay, Let's move on
著者: Mehvish Ashiq
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