volatile(型修飾子)とは

ぼらたいる

C言語の型修飾子の一つで、その変数の値がプログラムの見えないところ(周辺回路、割り込みハンドラなど)で変わったり、読み書きそのものに意味があったりすることをコンパイラに伝える。volatile を付けたオブジェクトへのアクセスは、省いたりまとめたりせず、書いた回数と順序のとおりに行われる。メモリマップドI/Oのレジスタや、割り込みハンドラと共有するフラグに使う。操作を不可分にしたり、ほかのコアとの順序を保証したりする働きはない。

「volatile(型修飾子)」は、6分野の7問の練習問題に出てきます(C言語の基礎, OSとハードウェア, マルチコアと同期など)。

この用語の分野: C言語の基礎

付けないと何が起こるか

while (flag == 0) { } のようなループで、flag を変えるのが割り込みハンドラだけだと、コンパイラはループの中で flag は変わらないと判断し、1回だけ読んで無限ループにすることがあります。周辺回路の状態レジスタを待つループでも同じことが起こり、同じレジスタに続けて書く処理が1回にまとめられることもあります。volatile を付けると、コードに書いたとおりの回数だけ実際に読み書きされます。

宣言の読み方

volatile uint32_t *p は「volatile な uint32_t を指すポインタ」で、*p へのアクセスが省かれなくなります。volatile uint32_t *const p とすると、ポインタ自体は変えられない(常に同じレジスタを指す)という意味が加わります。#define REG (*(volatile uint32_t *)0x40020014) のように、番地をポインタに変換して参照するマクロもよく使われます。

volatile では足りないこと

volatile は、読み出し・変更・書き込みの操作を不可分にはしないので、*reg |= mask; の途中で割り込みが入って別のビットの変更が消えることがあります。8ビットのマイコンでの32ビットの変数のように、1回で読み書きできない大きさの値は、途中で割り込まれると一部だけ新しい値が読めます。スレッドの間の同期には、C11 の _Atomic やロックのような、不可分性と順序を保証するしくみを使います。

volatile(型修飾子)の練習問題に挑戦

C言語で、スレッド間で受け渡すフラグ変数にvolatileを付けるだけでは、同期として不十分である理由として正しいものはどれか。

  1. Avolatileを付けた変数は、ほかのスレッドから読めなくなるから
  2. Bvolatileはコンパイラの最適化を抑えるだけで、操作の不可分性やほかのコアから見える順番を保証しないから
  3. Cvolatileを付けると、変数がディスクに保存されて遅くなるから
答えと解説を見る

正解:B. volatileはコンパイラの最適化を抑えるだけで、操作の不可分性やほかのコアから見える順番を保証しないから

volatileは、変数の読み書きを省いたりまとめたりしないようコンパイラに指示するもので、もともとはハードウェアのレジスタの操作のためのものです。CPUがメモリ操作の順番を入れ替えることは防げず、counter++のような操作も不可分になりません。C11やC++11以降は、_Atomicやstd::atomicを使うと、不可分性とメモリの順番の両方が保証されます。

よくある質問

const と volatile は同時に付けられますか?

付けられます。const volatile は「プログラムからは書き換えないが、外の要因で変わるので毎回読み直す」という意味で、読み出し専用の状態レジスタなどに使います。

volatile を付けると遅くなりますか?

その変数に関しては、レジスタに値を置いておく最適化ができなくなるので、アクセスが増えて遅くなることがあります。そのため、すべての変数に付けるのではなく、周辺回路のレジスタや割り込みハンドラと共有する変数など、必要なものにだけ付けます。

解説の中の「volatile(型修飾子)」

「volatile(型修飾子)」が出てくる問題(7問)

  1. 上のように装置の状態レジスタをメモリマップドI/Oで読むとき、volatileを付け、その領域をキャッシュしない設定にする理由として正しいものはどれか。
  2. C言語で、スレッド間で受け渡すフラグ変数にvolatileを付けるだけでは、同期として不十分である理由として正しいものはどれか。
  3. マイコンなどで、周辺機器のレジスタをメモリと同じアドレス空間に割り当て、通常のメモリ読み書き命令で操作する方式はどれか。
  4. 上のプログラムを最適化を有効にしてコンパイルすると、タイマ割り込みが起きてもled_on()が呼ばれないことがある。原因と対策の組み合わせとして正しいものはどれか。
  5. 組込みのプログラムで、32ビットの周辺レジスタの値を入れる変数を、unsigned int ではなく stdint.h の uint32_t で宣言することが多い。主な理由として正しいものはどれか。
  6. マイコンの出力レジスタを、上のように宣言して使っている。この宣言についての説明として、正しいものはどれか。
  7. 上のような小さなループで計算の速さを測ったところ、Nを大きくしても時間がほとんど0だった。最も考えられる原因はどれか。

C言語の基礎の参考書を探す PR

Amazonの検索結果へのリンクです。Amazonのアソシエイトとして、半導体教室は適格販売により収入を得ています。

関連する用語

変数 メモリマップドI/O マイコン(マイクロコントローラ) 割り込みハンドラ UART 割り込み ポインタ マイクロベンチマーク アライメントとパディング 関数ポインタ

用語集の一覧へ