volatileとは?C言語で最適化を防ぐ理由と使いどころをわかりやすく解説

目次

C言語文法解説シリーズ

本記事は「C言語文法解説シリーズ」の1つです。
C言語の文法を、組み込み開発の視点も交えて解説します。

C言語の volatile は、staticconstextern と並ぶ修飾子の1つです。
しかし、この volatile は初心者がつまずきやすいキーワードでもあります。

理由はシンプルで、

「なぜ必要なのか」が直感的に分かりにくいからです。

この記事では

  • volatileとは何か
  • 最適化で何が起きるのか
  • どんなときに必要か
  • volatileの限界

まで順番に解説します。


volatileとは?

volatile

「この変数の値は勝手に変わるかもしれない」

とコンパイラに伝えるキーワードです。

つまり

コンパイラ最適化を防ぐ修飾子

です。

初心者さん

勝手に変わるってどういうことですか?

エンジニアくん

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

初心者さん

外から?

エンジニアくん

割り込みとか、ハードウェアレジスタだね。特に組み込みではよくあるんだ


なぜvolatileが必要?

例えば次のコード

main.c
while ( flag == 0 ) {
}

これは

「flagが変わるまで待つ」

という意味に見えます。

しかしコンパイラはこう考えます。

  • flagはループ内で変更されていない
  • 値は変わらない
  • 毎回読む必要はない

すると最適化されます。

最適化イメージ

C
if ( flag == 0 )
{
    while(1)
    {
    }
}

つまり

flagを1回しか読まない

ようになります。

初心者さん

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

エンジニアくん

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

初心者さん

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

エンジニアくん

そういう場合に必要なのがvolatile


割り込みで壊れる例

C
int flag = 0;

void interrupt(void)
{
    flag = 1;
}

int main(void)
{
    while(flag == 0)
    {
    }
}

本来

割り込み

flag = 1

while抜ける

しかし最適化されると

flagを一度しか読まない

無限ループ

になります。


volatileを付ける

C
volatile int flag = 0;

こうすると

「値は変わる可能性あり」

とコンパイラに伝わります。

すると

C
while (flag == 0)

のたびに

毎回メモリから読み直す

ようになります。


volatileが必要な代表例

① 割り込み共有変数

C
volatile int flag;

② ハードウェアレジスタ

C
#define REG (*(volatile int*)0x40000000)

③ DMAなど外部更新変数


でもvolatileは万能ではない

ここが重要です。

volatileは

✔ 最適化防止
✖ 原子性保証しない
✖ 順序保証しない
✖ 排他制御しない

初心者さん

急に難しくなりました…

エンジニアくん

1つずつ見ていこう


volatileは原子性を保証しない

原子性とは、処理が途中で割り込まれないことです。

例えば

C
volatile int count;

count++;

この count++ は1つの処理に見えますが、実際には次の3ステップで実行されます。

① count を読む
② +1 する
③ count に書く

ここで問題になるのが割り込みです。

例えば次の状況を考えます。

初期値

C
count = 0

メイン処理

C
count++

割り込み処理

C
count++

実行順

メイン: count読む → 0
割り込み: count読む → 0
割り込み: +1 → 1
割り込み: 書く → count=1

メイン: +1 → 1
メイン: 書く → count=1

本来は

0 → 1 → 2

になるはずですが、

メイン処理が古い値をもとに書き戻してしまうため
最終結果は

C
count = 1

になります。

つまり

割り込み側の更新を上書きしてしまう

これが原子性が保証されていない状態です。

volatile は「毎回読み直す」だけなので、
この問題は防げません。

初心者さん

volatile付けてもダメ?

エンジニアくん

ダメ。途中割り込みは防がないからね


volatileは順序も保証しない

例えば

C
flag = 1;
data = 100;

この順番で書いていても

最適化により

C
data = 100;
flag = 1;

になる可能性があります。

初心者さん

順番変わるんですか?

エンジニアくん

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

初心者さん

volatileなら大丈夫?

エンジニアくん

volatileはその変数だけ。全体の順序は守らない


volatileは排他制御もしない

排他制御とは同時に触らせない仕組みです。

例えば

メインと割り込みが同時にアクセス

C
volatile int count;

これでも同時更新される可能性があります。

初心者さん

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

エンジニアくん

毎回読み直すだけだよ

これがvolatileの役割です。


volatileの役割まとめ

volatileは

「最適化を防ぐ」

だけです。

やらないことは以下です。

  • 原子性保証しない
  • 排他制御しない
  • 順序保証しない

まとめ

volatileは

コンパイラ最適化を防ぐ修飾子

です。

主な用途は以下です。

  • 割り込み共有変数
  • ハードウェアレジスタ
  • 外部要因で変わる値

この記事が参考になった方へ

技術に関するご相談・開発・自動化ツール作成・記事執筆などのご依頼も承っています。

小さなご相談からでもお気軽にご連絡ください。

お問い合わせはこちら

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする


目次