※本記事は広告を含みます。
C言語文法解説シリーズ
本記事は「C言語文法解説シリーズ」の1つです。
C言語の文法を、組み込み開発の視点も交えて解説します。
C言語の volatile は、static・const・extern と並ぶ修飾子の1つです。
しかし、この volatile は初心者がつまずきやすいキーワードでもあります。
理由はシンプルで、
「なぜ必要なのか」が直感的に分かりにくいからです。
この記事では
- volatileとは何か
- 最適化で何が起きるのか
- どんなときに必要か
- volatileの限界
まで順番に解説します。
volatileとは?
volatile は
「この変数の値は勝手に変わるかもしれない」
とコンパイラに伝えるキーワードです。
つまり
コンパイラ最適化を防ぐ修飾子
です。
初心者さん勝手に変わるってどういうことですか?



プログラムの中で変更していなくても、外部要因で値が変わることがあるんだ



外から?



割り込みとか、ハードウェアレジスタだね。特に組み込みではよくあるんだ
なぜvolatileが必要?
例えば次のコード
while ( flag == 0 ) {
}これは
「flagが変わるまで待つ」
という意味に見えます。
しかしコンパイラはこう考えます。
- flagはループ内で変更されていない
- 値は変わらない
- 毎回読む必要はない
すると最適化されます。
最適化イメージ
if ( flag == 0 )
{
while(1)
{
}
}つまり
flagを1回しか読まない
ようになります。



え?毎回チェックしないんですか?



変わらないと判断されたらしないよ



でも外から変わるかもしれないですよね



そういう場合に必要なのがvolatile
割り込みで壊れる例
int flag = 0;
void interrupt(void)
{
flag = 1;
}
int main(void)
{
while(flag == 0)
{
}
}本来
割り込み
↓
flag = 1
↓
while抜ける
しかし最適化されると
flagを一度しか読まない
↓
無限ループ
になります。
volatileを付ける
volatile int flag = 0;こうすると
「値は変わる可能性あり」
とコンパイラに伝わります。
すると
while (flag == 0)のたびに
毎回メモリから読み直す
ようになります。
volatileが必要な代表例
① 割り込み共有変数
volatile int flag;② ハードウェアレジスタ
#define REG (*(volatile int*)0x40000000)③ DMAなど外部更新変数
でもvolatileは万能ではない
ここが重要です。
volatileは
✔ 最適化防止
✖ 原子性保証しない
✖ 順序保証しない
✖ 排他制御しない



急に難しくなりました…



1つずつ見ていこう
volatileは原子性を保証しない
原子性とは、処理が途中で割り込まれないことです。
例えば
volatile int count;
count++;この count++ は1つの処理に見えますが、実際には次の3ステップで実行されます。
① count を読む
② +1 する
③ count に書く
ここで問題になるのが割り込みです。
例えば次の状況を考えます。
初期値
count = 0メイン処理
count++割り込み処理
count++実行順
メイン: count読む → 0
割り込み: count読む → 0
割り込み: +1 → 1
割り込み: 書く → count=1
メイン: +1 → 1
メイン: 書く → count=1
本来は
0 → 1 → 2
になるはずですが、
メイン処理が古い値をもとに書き戻してしまうため
最終結果は
count = 1になります。
つまり
割り込み側の更新を上書きしてしまう
これが原子性が保証されていない状態です。
volatile は「毎回読み直す」だけなので、
この問題は防げません。



volatile付けてもダメ?



ダメ。途中割り込みは防がないからね
volatileは順序も保証しない
例えば
flag = 1;
data = 100;この順番で書いていても
最適化により
data = 100;
flag = 1;になる可能性があります。



順番変わるんですか?



コンパイラは順番を入れ替えることがある



volatileなら大丈夫?



volatileはその変数だけ。全体の順序は守らない
volatileは排他制御もしない
排他制御とは同時に触らせない仕組みです。
例えば
メインと割り込みが同時にアクセス
volatile int count;これでも同時更新される可能性があります。



じゃあvolatileは何を守るんですか?



毎回読み直すだけだよ
これがvolatileの役割です。
volatileの役割まとめ
volatileは
「最適化を防ぐ」
だけです。
やらないことは以下です。
- 原子性保証しない
- 排他制御しない
- 順序保証しない
まとめ
volatileは
コンパイラ最適化を防ぐ修飾子
です。
主な用途は以下です。
- 割り込み共有変数
- ハードウェアレジスタ
- 外部要因で変わる値
この記事が参考になった方へ
C言語の基本文法を一覧で整理しています。
技術に関するご相談・開発・自動化ツール作成・記事執筆などのご依頼も承っています。
小さなご相談からでもお気軽にご連絡ください。









コメント