Namespaces
Variants

volatile type qualifier

From cppreference.net

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型修飾子で宣言された場合( typedef の使用を通じて)、配列型自体はvolatile修飾されないが、その要素型は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 の使用を通じて)、その動作は未定義です。

関数宣言において、キーワード 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: 修飾子を追加 (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の用途

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) static volatile 型の sig_atomic_t オブジェクトは、 signal ハンドラとの通信に使用されます。
3) volatile 関数内でローカルな volatile 変数は、 setjmp マクロの呼び出しを含む関数内において、 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++ ドキュメント for cv ( const and volatile ) 型修飾子