volatile 型修飾子
C の型システムにおける個々の型には、それぞれいくつかの「修飾」されたバージョンがあり、これは const、volatile、およびオブジェクト型へのポインタの場合には restrict の 1 つ、2 つ、または 3 つすべてに対応する修飾子です。このページでは、volatile 修飾子の効果について説明します。
volatile 修飾された型の左辺値式を通じて行われるすべてのアクセス(読み取りと書き込みの両方)は、最適化の目的で観測可能な副作用と見なされ、抽象マシンの規則に従って厳密に評価されます(つまり、すべての書き込みは、次のシーケンスポイントの前にいつか完了します)。これは、単一のスレッド実行内では、volatile アクセスは、volatile アクセスからシーケンスポイントで区切られた別の観測可能な副作用と比較して、最適化されたり並べ替えられたりしないことを意味します。
非 volatile 値を volatile 型にキャストしても効果はありません。非 volatile オブジェクトを volatile セマンティクスを使用してアクセスするには、そのアドレスを volatile へのポインタにキャストしてから、そのポインタを通じてアクセスを行う必要があります。
volatile 修飾された型のオブジェクトに対して、非 volatile 左辺値を通じて読み取りまたは書き込みを行おうとすると、未定義の動作となります。
volatile int n = 1; // object of volatile-qualified type int* p = (int*)&n; int val = *p; // undefined behavior
volatile 修飾された構造体または共用体型のメンバは、それが属する型の修飾子を取得します(. 演算子または -> 演算子を使用してアクセスする場合)。
struct s { int i; const int ci; } s; // the type of s.i is int, the type of s.ci is const int volatile struct s vs; // the types of vs.i and vs.ci are volatile int and const volatile int
|
配列型が volatile 型修飾子で宣言されている場合( |
(C23まで) |
|
配列型とその要素型は、常に同一の volatile 修飾がされていると見なされます。 |
(C23以降) |
typedef int A[2][3]; volatile A a = {{4, 5, 6}, {7, 8, 9}}; // array of array of volatile int int* pi = a[0]; // Error: a[0] has type volatile int* void *unqual_ptr = a; // OK until C23; error since C23 // Notes: clang applies the rule in C++/C23 even in C89-C17 modes
関数型が 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: adds qualifiers (int to volatile int) p = vp; // Error: discards qualifiers (volatile int to int) p = (int*)vp; // OK: cast
T へのポインタから volatile T へのポインタへのポインタは互換性がないことに注意してください。2 つの型が互換性を持つためには、それらの修飾子は同一でなければなりません。
char *p = 0; volatile char **vpp = &p; // Error: char* and volatile char* are not compatible types char * volatile *pvp = &p; // OK, adds qualifiers (char* to char*volatile)
目次 |
[編集] 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 変数からの読み取り、または同期されていない 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++ ドキュメント (cv (
const および volatile) 型修飾子) |