
C言語のvolatile がよく分からない…。誰か詳しく教えて…。
C言語の入門書ではあまり詳しく説明されないのに、組み込みの現場では必須となるキーワードがあります。それがvolatileです。
「変数に付けるおまじない」くらいの説明で済まされることも多いのですが、実はvolatileを付け忘れるとプログラムが正しく動かなくなることがあります。
この記事では、volatileとは何かを整理した上で、「volatileを付けないと本当に動かなくなるのか?」を、ブラウザ上のArduinoシミュレーター(Wokwi)で実際に再現してみます。
- volatileの意味と、なぜ組み込みで必須なのか
- volatileを付け忘れるとプログラムがどう壊れるか(実演)
- どんな変数にvolatileを付けるべきか
先に結論
volatileは「この変数は、コンパイラの知らないところで値が変わる可能性があるので、最適化しないでそのまま毎回読み書きしてください」とコンパイラに伝えるキーワード- 割り込み処理の中で変更される変数にvolatileを付け忘れると、メインループがその変化に気づけず、プログラムが止まったように見えることがある
- 実際にWokwi上で、volatile無しだと動かず、volatileを付けると動くことを確認した
volatile無しの場合:ボタンを押してもLED点灯しない

volatile有りの場合:ボタンを押すとLED点灯する

volatileとは
コンパイラは、プログラムを速く小さくするために「最適化」を行います。
たとえば、以下のようなループがあったとします。
int flag = 0;
while (flag == 0) { /* flagが1になるのを待つ */}コンパイラはコード全体を見て、
このループの中ではflagを誰も変更していない。だったら毎回メモリから読み直す必要はない
と判断し、最初に1回だけ読んだ値(今回だと0)を使い回すようにコードを変換してしまうことがあります。
普通のプログラムならこの最適化で問題ありません。しかし、コンパイラの見えないところで値が変わる変数が相手だと、この最適化が裏目に出ます。
volatileを付けると、コンパイラは最適化を諦めて、その変数を毎回律儀にメモリから読み直すようになります。
volatile int flag = 0; /* 毎回メモリから読み直してくれる */なぜ組み込みで必須なのか
「コンパイラの見えないところで値が変わる変数」は、組み込みの世界には日常的に存在します。代表例は次の2つです。
- 割り込み処理の中で変更される変数:メインループの外側で、ボタン押下やタイマーをきっかけに突然実行される処理(割り込み)がフラグを書き換える
- ハードウェアのレジスタ:センサーや通信ポートの状態を表すメモリ領域は、プログラムが何もしなくてもハードウェア側が勝手に値を変える
どちらも「コードの流れだけを見ているコンパイラ」からは変更が見えないため、volatileで最適化を止めてあげる必要があります。
実演:volatile無しだと本当に動かないのか
ここからは、Wokwi(ブラウザ上のArduinoシミュレーター)で実際に確認してみます。Wokwiの基本的な使い方は以下の記事で紹介しています。

用意したプログラム
「ボタンを押したら割り込みでフラグを立て、メインループがフラグに気づいたらLEDを点灯する」だけのシンプルなプログラムです。
const int LED_PIN = 8;
const int BUTTON_PIN = 2;
int pressed = 0; /* まずはvolatile無しで試す */
void onButtonPress() {
pressed = 1; /* 割り込み処理:ボタンが押されたらフラグを立てる */
}
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, FALLING);
}
void loop() {
while (pressed == 0) {
/* フラグが立つのを待つ */
}
digitalWrite(LED_PIN, HIGH); /* ボタンに気づいたらLED点灯 */
}volatile無しの実行結果

ボタンを何度押しても、LEDは点灯しませんでした。
割り込み処理(onButtonPress)自体は動いてpressedを1にしているのですが、メインループ側はコンパイラの最適化によって「最初に読んだpressed = 0」を使い回しており、変化に気づけていません。
volatileを付けた実行結果
変数の宣言を1行変えるだけです。
volatile int pressed = 0; /* volatile有りとする */
今度はボタンを押した瞬間にLEDが点灯しました。変更点はvolatileを付けたことだけです。
注意:「たまたま動いてしまう」こともある
この再現は、コンパイラの最適化の挙動に依存します。最適化のレベルや書き方によっては、volatileを付け忘れていても「たまたま動いてしまう」ことがあります。
これがvolatileの怖いところで、開発中は動いていたのに、コンパイラや最適化設定が変わった途端に動かなくなる、という発見しにくい不具合の原因になります。
「たまたま動く」に頼らず、該当する変数には最初からvolatileを付けておくのが安全です。
どんな変数にvolatileを付けるべきか
代表例として、以下のような変数にはvolatileが必要なケースが多いです。
ただし、本当に必要かどうかはその都度確認が必要です。
- 割り込み処理(ISR)の中で読み書きされ、メインループでも使われる変数
- ハードウェアのレジスタをポインタ経由で読み書きする場合
なお、マルチスレッド環境の共有変数は、volatileではなく排他制御の仕組みを使うのが正解です。volatileは万能ではありません。
まとめ
- volatileは「コンパイラの見えないところで値が変わる変数」への最適化を止めるキーワード
- 割り込みで変更されるフラグにvolatileが無いと、メインループが変化に気づけず止まったように見える
- Wokwi上で、volatile無し→動かない、volatileあり→動く、を実際に確認できました
- 「たまたま動く」こともあるのが怖いところ。該当する変数には最初から付けておくのが安全
最後まで読んでいただき、ありがとうございました。




コメント