volatile type qualifier
C言語の
type system
における各々の型には、その型のいくつかの
qualified
版が存在し、
const
、
volatile
、およびオブジェクト型へのポインタについては
restrict
修飾子のうち1つ、2つ、または全て3つに対応しています。このページでは
volatile
修飾子の効果について説明します。
最適化の目的において、volatile修飾型の左辺値式を通じて行われるすべてのアクセス(読み取りと書き込みの両方)は観測可能な副作用と見なされ、抽象機械の規則に厳密に従って評価されます(つまり、すべての書き込みは次のシーケンスポイント前のある時点で完了します)。これは、単一の実行スレッド内では、volatileアクセスは、そのvolatileアクセスから シーケンスポイント によって分離された別の可視的な副作用に対して、最適化によって除去されたり順序変更されたりすることができないことを意味します。
非volatile値のvolatile型へのキャストは効果を持ちません。volatileセマンティクスを使用して非volatileオブジェクトにアクセスするには、そのアドレスをvolatileへのポインタにキャストし、そのポインタを通じてアクセスを行う必要があります。
volatile修飾された型を持つオブジェクトへの非volatile左辺値を通じた読み取りまたは書き込みの試みは、未定義動作を引き起こします:
volatile int n = 1; // volatile修飾型のオブジェクト int* p = (int*)&n; int val = *p; // 未定義動作
volatile修飾された構造体または共用体型のメンバーは、それが属する型の修飾を継承します(
.
演算子または
->
演算子を使用してアクセスされる場合の両方):
struct s { int i; const int ci; } s; // s.iの型はint、s.ciの型はconst int volatile struct s vs; // vs.iとvs.ciの型はそれぞれvolatile intとconst volatile int
|
配列型がvolatile型修飾子で宣言された場合(
|
(C23まで) |
|
配列型とその要素型は常に同一のvolatile修飾を持つものと見なされる。 |
(C23以降) |
typedef int A[2][3]; volatile A a = {{4, 5, 6}, {7, 8, 9}}; // volatile intの配列の配列 int* pi = a[0]; // エラー: a[0]はvolatile int*型 void *unqual_ptr = a; // C23まではOK; C23以降はエラー // 注記: clangはC++/C23のルールをC89-C17モードでも適用する
関数の型がvolatile型修飾子付きで宣言された場合(
typedef
の使用を通じて)、その動作は未定義です。
|
関数宣言において、キーワード
以下の2つの宣言は同じ関数を宣言します: void f(double x[volatile], const double y[volatile]); void f(double * volatile x, const double * volatile y); |
(C99以降) |
非volatile型へのポインタは、同じまたは 互換性のある 型のvolatile修飾版へのポインタに暗黙的に変換できます。逆方向の変換にはキャスト式が必要です。
int* p = 0; volatile int* vp = p; // OK: 修飾子を追加 (int から volatile int へ) p = vp; // エラー: 修飾子を破棄 (volatile int から int へ) p = (int*)vp; // OK: キャスト
T
へのポインターへのポインターは、
volatile T
へのポインターへのポインターには変換できないことに注意してください。2つの型が互換性を持つためには、その修飾子が完全に同一でなければなりません:
char *p = 0; volatile char **vpp = &p; // エラー: char*とvolatile char*は互換性のある型ではありません char * volatile *pvp = &p; // OK, 修飾子を追加(char*からchar*volatileへ)
目次 |
volatileの用途
static
volatile
オブジェクトはメモリマップドI/Oポートをモデル化し、
static
const
volatile
オブジェクトはリアルタイムクロックなどのメモリマップド入力ポートをモデル化します:
volatile short *ttyport = (volatile short*)TTYPORT_ADDR; for(int i = 0; i < N; ++i) *ttyport = a[i]; // *ttyport is an lvalue of type volatile short
volatile
関数内でローカルな
volatile
変数は、
setjmp
マクロの呼び出しを含む関数内において、
longjmp
が戻った後も値を保持することが保証されている唯一のローカル変数です。
volatile変数はスレッド間通信には適さないことに注意してください。これらはアトミック性、同期、またはメモリ順序を提供しません。同期なしに別のスレッドによって変更されるvolatile変数からの読み取り、または2つの非同期スレッドからの同時変更は、データ競合による未定義動作となります。
キーワード
例
volatileを使用して最適化を無効化する使用例を示します
#include <stdio.h> #include <time.h> int main(void) { clock_t t = clock(); double d = 0.0; for (int n = 0; n < 10000; ++n) for (int m = 0; m < 10000; ++m) d += d * n * m; // reads from and writes to a non-volatile printf("Modified a non-volatile variable 100m times. " "Time used: %.2f seconds\n", (double)(clock() - t)/CLOCKS_PER_SEC); t = clock(); volatile double vd = 0.0; for (int n = 0; n < 10000; ++n) for (int m = 0; m < 10000; ++m) { double prod = vd * n * m; // reads from a volatile vd += prod; // reads from and writes to a volatile } printf("Modified a volatile variable 100m times. " "Time used: %.2f seconds\n", (double)(clock() - t)/CLOCKS_PER_SEC); }
出力例:
Modified a non-volatile variable 100m times. Time used: 0.00 seconds Modified a volatile variable 100m times. Time used: 0.79 seconds
参考文献
- C17規格 (ISO/IEC 9899:2018):
-
- 6.7.3 型修飾子 (p: 87-90)
- C11規格 (ISO/IEC 9899:2011):
-
- 6.7.3 型修飾子 (p: 121-123)
- C99規格 (ISO/IEC 9899:1999):
-
- 6.7.3 型修飾子 (p: 108-110)
- C89/C90標準 (ISO/IEC 9899:1990):
-
- 6.5.3 型修飾子
関連項目
|
C++ ドキュメント
for
cv (
const
and
volatile
) 型修飾子
|