7

C ++では、次volatileのように扱われます。修飾子をconst必要としない関数に揮発性データへのポインターを渡すと、コンパイルエラーがトリガーされます。volatile

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;
    foo(baz); // error: invalid conversion from ‘volatile int*’ to ‘int*’
}

なぜ危険なのですか?const修飾子を削除すると、const正確性が損なわれる可能性があることは明らかです。volatileでも「正しさ」というものはありますか?揮発性データへのポインタを不揮発性データへのポインタとして渡すと、どのように問題が発生するのか理解できません。

編集私が最初に使用した理由を皆さんが知っているようにvolatile、Mac OS XのOSAtomic関数ファミリーの多く(アトミックインクリメント、デクリメント、加算、減算、コンペアアンドスワップなど)はvolatile引数を取ります。

4

5 に答える 5

10

volatile修飾子は、コンパイラがC標準の「抽象マシン」で指定されているとおりに揮発性データ項目の各読み取り/書き込みを実際に実行するように注意する必要があることを意味するためです。

修飾子が取り除かれるvolatileと、プログラムが制御のプログラムフローのシングルスレッドの視点に関する限り、アクセスが発生したかのように動作する限り、データアクセスを最適化できます。言い換えると、コンパイラは不揮発性のデータをコンパイラのように扱うことができ、コンパイラだけがデータ項目を表示および変更できます(これはほとんどの場合に当てはまります)。

このvolatileキーワードは、他の何か(ハードウェアまたは別の実行スレッド)がそのデータ項目を変更または表示できることをコンパイラーに通知するため、コンパイラーはアクセスを最適化することを許可されていません。

揮発性データへのポインターを、警告なしに不揮発性ポインターを取得した関数に渡すことができた場合、関数は、発生する可能性のあるデータの変更を認識しない可能性があります。それを気にしない場合は、(foo()データの処理方法に応じて)優れたポータブルな回避策をコーディングできる可能性があります。

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;

    int tmp = *baz;
    foo(&tmp);
    *baz = tmp;
}
于 2010-08-04T23:52:46.480 に答える
5

コンパイラーは、非揮発性変数へのアクセスを最適化できるだけでなく、プログラムの順次実行に影響がない限り、予測的/推測的にそれらを更新できます。

揮発性変数へのスプリアス書き込みが設計を壊さないのであれば、どのような状況でも揮発性である必要はなかったでしょう。

たとえば、C++03コンパイラが変換することは完全に合法です

int result;
void sum_if_all_positive( std::array<N> ary )
{
    int sum = 0;
    result = -1;
    for( int i = 0; i < N; ++i ) {
        if (ary[i] < 0) return;
        sum += ary[i];
    }
    result = sum;
}

の中へ

int result;
void sum_if_all_positive( std::array<N> ary )
{
    result = 0;
    for( int i = 0; i < N; ++i ) {
        if (ary[i] < 0) { result = -1; return; }
        result += ary[i];
    }
}

(ただし、このような変更は、メモリアクセスが安価でレジスタが非常に少ない少数のアーキテクチャでのみ合計を登録するよりも優れたパフォーマンスを提供します。MicrochipPICアーキテクチャが思い浮かびます。)

于 2010-08-05T00:03:56.077 に答える
0

キーワードは、値が毎回メモリとのvolatile間でロードおよび保存される必要があることを意味します。

コードを考えてみましょう:

int foo(int* bar) { 
    while(*bar){ 
        //Do something...
    }
}

int main()
{
    volatile int num = 1;
    volatile int* baz = &num;
    //Start a seperate thread to change *baz evenutally... 
    foo(baz);
}

コンパイラがwhileループを確認し、barが指すものが常に1になることを知っている場合、毎回チェックする必要があるのはなぜですか。毎回チェックするのは非常に無駄です。 volatileコンパイラ毎回このチェックを実行することを確認します。これにより、他のスレッドが値を変更すると、whileループが終了します。

于 2010-08-04T23:56:59.480 に答える
0

さて、コンパイラでは、 (またはより厳密には)揮発性であるfoo()ことを認識しなくなったため、不適切な最適化を適用しようとする可能性があります。bazbar

于 2010-08-04T23:49:10.587 に答える
-1

はい、「揮発性の正しさ」などがあります

これは、マルチスレッドコードの安全性を確保するために使用されます。

于 2012-11-10T05:48:20.380 に答える