13

これは基本的にこの質問の続きです。これまでのところ、次のような関数がある場合は次のようになります。

void SecureZeroMemory( void* ptr, size_t cnt )
{
   volatile char *vptr = (volatile char *)ptr;
   while (cnt) {
       *vptr = 0;
       vptr++;
       cnt--;
   }
}

次のように呼び出します。

{
    char buffer[size];
    SecureZeroMemory( buffer, size );
}

volatile と宣言されてbufferいないため、volatile へのポインタが使用されても問題ありません。データ自体は volatile ではないため、変数への書き込みは観察可能な動作 (1.9/6) を構成せず、コンパイラはそれらを最適化することができます。 .

しかし最近、重要なのはポインター宣言だけだという声明に出くわしました。具体的には、C++03 5.3.1/1 は次のようにインダイレクション (*) を記述します。

単項 * 演算子は間接参照を実行します [...] 式の型が「T へのポインター」の場合、結果の型は「T」です。</p>

つまり、avolatile char*で間接参照を使用しているため、それらへの getvolatile charおよび write は観察可能な動作を構成し、実際のデータがどのように宣言されているかはもはや重要ではありません。

C++03 5.3.1/1 のインダイレクションの記述はvolatile T*、上記のサンプルのようにポインタを使用してメモリを上書きすることは、監視可能な動作を構成し、最適化されないようにすることを本当に保証していますか?

4

2 に答える 2

4

*vptr「新しい」引用符が追加するのは、それが type の左辺値式であることだけだと確信していますvolatile char

左辺値の型は、その左辺値式が参照するオブジェクトの型には影響しません。これは、非 const オブジェクトを指す const へのポインターがオブジェクトを const にしないのと同じ理由でです。したがって、元の分析はこの引用の影響を受けません。オブジェクトにはまだ volatile 修飾された型がありません。

通常の言い回しでは、 の型は であると言いますが*vptrvolatile char &5/5 は、「式が最初に「T への参照」型を持つ場合、それ以上の分析の前に型が T に調整される」と述べています。*vptr式を分析する前にvolatile char、それが左辺volatile char &値であっても、型から参照を削除します。

[編集: 私の回答には、cv-qualifications が整数型の非オブジェクト値に対して重要ではないというテキストが含まれていました。それは本当でした(非クラス型の左辺値から右辺値への変換はcv修飾子を破棄します、4.1/1)が無関係です(引用したテキストが非参照型について言及しているため、この後の型について話していると誤って考えました変換)]

于 2012-12-06T14:51:04.963 に答える
4

興味深い質問です。標準の意図は、これが機能することだったと思います。ただし、標準 (C++03、§1.9/6,7) を読むと:

抽象マシンの観察可能な動作は、揮発性データへの読み取りと書き込み、およびライブラリ I/O 関数の呼び出しのシーケンスです。

volatile 左辺値で指定されたオブジェクトへのアクセス、オブジェクトの変更、ライブラリ I/O 関数の呼び出し、またはこれらの操作のいずれかを実行する関数の呼び出しはすべて、実行環境の状態の変化である副作用です。式の評価により、副作用が生じる場合があります。シーケンスポイントと呼ばれる実行シーケンスの特定のポイントでは、以前の評価のすべての副作用が完了し、後続の評価の副作用は発生していません。

2 つの段落の文言の違いは重要なように思われます。「観察可能な動作」とは、揮発性データの読み取りと書き込みのシーケンスです。あなたの場合、揮発性データでbufferない ため、コンパイラはおそらく自由にアクセスを最適化できます。これが意図したものではないと思いますが、それはそれが言っていることのようです。

あなたの場合、関数自体で volatile への変換が行われるため、最適化は特に簡単です。vptrコンパイラは、 が実際に揮発性のデータを指していないことを簡単に判断できます。パラメーターの型を に変更するvoid volatile*と、最適化を安全に行うために、コンパイラーは呼び出しサイトと関数の両方を同時に確認する必要があります。

最後に、標準の内容に関係なく、コンパイラには独自の解釈がありvolatileます。実際には、すべてではないにしてもほとんどのコンパイラは volatile、何らかの理由で使用していると想定し、書き込みを行うためのマシン命令を生成します。(実際には、それが彼らが行うことのすべてです。つまり、書き込みがスレッドの外部で可視になる順序、スレッドが実行されているコードは未定義のままです。これは使用上の問題ではありませんが、他にも色々と使えます。)

于 2012-12-06T14:58:23.920 に答える