次のコードを検討してください。
int square(volatile int *p)
{
return *p * *p;
}
現在、volatile
キーワードは、メモリの場所の値がコンパイラにとって未知の方法で変更されたり、他の未知の副作用(たとえば、信号割り込み、ハードウェアレジスタ、またはメモリマップI/Oによる変更)を持つ可能性があることを示しています。 )プログラムコードの内容が変更されることはありませんが。
では、ポインタを揮発性として宣言すると、正確にはどうなりますか?
上記のコードは常に機能しますか、それともこれとは異なりますか?
int square(volatile int *p)
{
int a = *p;
int b = *p
return a*b;
}
ポインタが揮発性であるため、異なる数を乗算することになりますか?
それとももっと良い方法がありますか?
ポインターを
volatile
にすることはできますか?
絶対に;関数と参照を除くすべての型は、volatile
で修飾できます。
揮発性ポインターは_T *volatile
_ではなく_volatile T*
_として宣言され、代わりにpointer-toを宣言することに注意してください。 -volatile。
揮発性ポインターとは、ポインターの値、つまりそのアドレスであり、ポインターが指す値ではないという副作用があり、コンパイラーがアクセスしたときに見えないことを意味します。したがって、「as-ifルール」から派生した最適化は、それらのアクセスでは考慮されない場合があります。
int square(volatile int *p) { return *p * *p; }
コンパイラーは、_*p
_の読み取りが同じ値をフェッチするとは想定できないため、その値を変数にキャッシュすることはできません。あなたが言うように、結果は異なる場合があり、_*p
_の二乗ではない場合があります。
具体例:int
sの配列が2つあるとします
_int a1 [] = { 1, 2, 3, 4, 5 };
int a2 [] = { 5453, -231, -454123, 7565, -11111 };
_
そのうちの1つへのポインタ
_int * /*volatile*/ p = a1;
_
先の尖った要素を操作して
_for (int i = 0; i < sizeof(a1)/sizeof(a1[0]); ++i)
*(p + i) *= 2;
_
ここで、p
をvolatile
にした場合は、繰り返しごとに読み取る必要があります。これは、おそらく、外部イベントのために実際に_a2
_を指している可能性があるためです。
はい、もちろん揮発性のポインタを持つことができます。
揮発性とは、揮発性オブジェクト(すべてのタイプの)へのすべてのアクセスが目に見える副作用として扱われるため、最適化が免除されることを意味します(特に、これは、アクセスの順序が変更されたり、折りたたまれたりしないことを意味します)。まとめて最適化されています)。これは、値の読み取りまたは書き込み、メンバー関数の呼び出し、および逆参照の場合にも当てはまります。
前の段落で「並べ替え」とある場合、単一の実行スレッドが想定されていることに注意してください。揮発性は、アトミック操作またはミューテックス/ロックに代わるものではありません。
より単純な言葉で言えば、volatile
は一般に、「最適化しないで、私が言うとおりに実行する」に変換されます。
ポインターのコンテキストで、Chris Lattnerの有名な「すべてのプログラマーが未定義の動作について知っておくべきこと」によって与えられた例示的な使用パターンを参照してください article =(はい、その記事はC++ではなくCについてですが、同じことが当てはまります):
LLVMベースのコンパイラーを使用している場合、「揮発性」のnullポインターを逆参照すると、揮発性のロードとストアは通常オプティマイザーに影響されないため、探している場合はクラッシュが発生します。
はい。 int * volatile
。
C++では、int * const
は定数への定数ポインター、int const *
は定数整数へのポインター、int const * const
は定数整数への定数ポインターのように、タイプ/ポインター/参照によるキーワードはトークンの後に続きます。等タイプの前にキーワードを記述できるのは、最初のトークンの場合のみです:const int x
がint const x
と等しい。
volatile
キーワードは、コンパイラーのヒントです(7.1.6.1/7)。
注:volatileは、オブジェクトの値が実装によって検出されない方法で変更される可能性があるため、オブジェクトに関する積極的な最適化を回避するための実装へのヒントです。さらに、一部の実装では、volatileは、オブジェクトにアクセスするために特別なハードウェア命令が必要であることを示す場合があります。詳細なセマンティクスについては、1.9を参照してください。一般に、volatileのセマンティクスは、C++でもCと同じであることを意図しています。—終了ノート]
どういう意味ですか?さて、このコードを見てください:
bool condition = false;
while(!condition)
{
...
}
デフォルトでは、コンパイラーは条件を簡単に最適化します(変更されないため、反復のたびにチェックする必要はありません)。ただし、条件をvolatile
として宣言すると、最適化は行われません。
もちろん、揮発性のポインタを持つことができ、そのためにクラッシュするコードを書くことは可能ですが、変数がvolative
であるという事実は、それが必ずしも変更されることを意味するわけではありませんいくつかの外部干渉に。
はい、たとえポインタが指している変数が予期せず変更される可能性がある場合でも、これがどのように発生するかがコードから明らかでない場合でも、ポインタは揮発性である可能性があります。
例は、制御スレッドの外部にある何かによって変更できるオブジェクトであり、コンパイラーは最適化すべきではありません。
Volatile指定子を使用する可能性が最も高いのは、ハードウェアを直接処理する低レベルのコードであり、予期しない変更が発生する可能性があります。
それは揮発性であり、予期せず変更される可能性があるため、異なる数を乗算することになるかもしれません。だから、あなたはこのようなことを試すことができます:
int square(volatile int *p)
{
int a = *p;
return a*a;
}