名前空間
変種
操作

volatile 型修飾子

From cppreference.com
< c‎ | language

C の型システムにおける個々の型には、それぞれいくつかの「修飾」されたバージョンがあり、これは constvolatile、およびオブジェクト型へのポインタの場合には 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 型修飾子で宣言されている場合(typedef を使用して)、配列型自体は volatile 修飾されませんが、その要素型は 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 を使用して)、動作は未定義です。

関数宣言において、キーワード volatile は、関数パラメータの配列型を宣言するために使用される角括弧の内側に現れることがあります。これは、配列型が変換されるポインタ型を修飾します。

次の 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 の用途

1) `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
2) `sig_atomic_t` 型の `static` `volatile` オブジェクトは、シグナルハンドラとの通信に使用されます。
3) `setjmp` マクロが呼び出されている関数内にローカルな `volatile` 変数は、`longjmp` が返った後も値を保持することが保証されている唯一のローカル変数です。
4) さらに、volatile 変数は、特定の最適化形式を無効にするために使用できます。たとえば、デッドストア除去やマイクロベンチマークのための定数畳み込みを無効にするために使用できます。

volatile 変数はスレッド間の通信には適していないことに注意してください。それらはアトミック性、同期、またはメモリ順序を提供しません。同期なしで別のスレッドによって変更されている volatile 変数からの読み取り、または同期されていない 2 つのスレッドからの同時変更は、データ競合による未定義の動作となります。

[編集] キーワード

volatile

[編集]

最適化を無効にするための 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) 型修飾子)
English 日本語 中文(简体) 中文(繁體)