Volatileは型指定子であり、おなじみのconstと同様に、さまざまなスレッドによってアクセスおよび変更される変数を変更するように設計されています。volatileは、この命令がコンパイラの最適化の影響を受けないようにするための命令キーワードとして使用され、省略されます。値は毎回直接読み取る必要があります。
揮発性変数は、変数が予期せず変更される可能性があることを意味します。そのため、コンパイラーはこの変数の値を想定しません。
効果
簡単に言えば、コンパイラがコードを最適化できないようにするためです。たとえば、次のプログラム:
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
外部ハードウェアの場合、上記の4つのステートメントはそれぞれ異なる操作を表し、4つの異なるアクションを生成しますが、コンパイラーはXBYTE [2] = 0x58のみを考慮して、上記の4つのステートメントを最適化します(つまり、最初の3つのステートメントを無視します。1つだけです。マシンコードが生成されます)。volatileと入力すると、コンパイラーは1つずつコンパイルし、対応するマシンコードを生成します(4つのコードが生成されます)。
正確には、オプティマイザは、レジスタに格納されているバックアップを使用するのではなく、この変数を使用するたびに、この変数の値を注意深く再読み取りする必要があります。揮発性変数の例をいくつか示します。
1)並列デバイスのハードウェアレジスタ(ステータスレジスタなど)
2)割り込みサービスサブルーチンでアクセスされる非自動変数(非自動変数)
3)マルチスレッドアプリケーションの複数のタスクで共有される変数
これは、Cプログラマーと組み込みシステムプログラマーを区別する最も基本的な問題です。組み込みシステムプログラマーは、ハードウェア、割り込み、RTOSなどを扱うことが多く、これらはすべて揮発性変数の使用を必要とします。揮発性のコンテンツを理解しないと、災害が発生します。
インタビュイーがこの質問に正しく答えると仮定して(まあ、これが事実かどうか疑問に思います)、この男が揮発性の重要性を本当に理解しているかどうかを確認するためにもう少し深く掘り下げます。
1)パラメータをconstまたはvolatileにすることはできますか?理由を説明。
2)ポインタは揮発性ですか?理由を説明。
3)次の関数を使用して、特定の整数の2乗を計算しますが、期待される設計目標を達成できますか?そうでない場合は、次の質問に答えてみてください。
int square(volatile int *ptr)
{
return ((*ptr) * (*ptr));
}
答えは次のとおりです。
1)はい。例として、読み取り専用のステータスレジスタがあります。予期せず変更される可能性があるため、揮発性です。プログラムがそれを変更しようとすべきではないので、それはconstです。
2)はい。これはあまり一般的ではありませんが。例として、割り込みサービスルーチンがバッファへのポインタを変更する場合があります。
3)このコードはいたずらです。このコードの目的は、ポインターptrが指す値の2乗を返すことです。ただし、 ptrは揮発性パラメーターを指すため、コンパイラーは次のようなコードを生成します。
int square(volatile int* &ptr)//这里参数应该申明为引用,不然函数体里只会使用副本,外部没法更改
{
int a,b;
a = *ptr;
b = *ptr;
return a*b;
}
* ptrの値は、2つのvalueステートメント間で変更される可能性があるため、aとbは異なる場合があります。その結果、このコードは期待した二乗値を返さない可能性があります。正しいコードは次のとおりです。
long square(volatile int*ptr)
{
int a;
a = *ptr;
return a*a;
}
個人的な理解について話す:
キーは2つの場所にあります:
⒈コンパイラの最適化(以下の理解を理解するのを手伝ってください)
このスレッドでは、変数を読み取るときに、アクセス速度を向上させるために、コンパイラーが最適化時に変数をレジスターに読み込むことがあります。変数値が後で取得されると、値はレジスターから直接取得されます。
このスレッドで変数値が変更されると、一貫性を維持するために、変数の新しい値が同時にレジスタにコピーされます。
他のスレッドなどで変数の値が変化した場合、それに応じてレジスタの値が変化しないため、アプリケーションプログラムが読み取った値が実際の変数の値と一致しなくなります。
他のスレッドなどによりレジスタの値が変化した場合、元の変数の値は変化せず、アプリケーションが読み取った値と実際の変数の値が一致しなくなります。
不正確な例を挙げてください。
給与を支払う際、会計士は従業員に毎回銀行カード番号を登録するように呼びかけます。トラブルを防ぐために、会計士はすぐに登録せず、以前に登録した銀行カード番号を使用しました。1人の従業員だけが銀行カードと銀行を紛失しました。カード番号が報告されました;その結果、従業員は賃金を受け取ることができません
従業員の元の変数アドレス
銀行カード番号-レジスター内の元の変数のバックアップ
⒉どのような状況で表示されますか
1)並列デバイスのハードウェアレジスタ
2)割り込みサービスサブルーチンでアクセスされる非自動変数(非自動変数)
3)マルチスレッドアプリケーションの複数のタスクで共有される変数
補足:揮発性は「元のメモリアドレスへの直接アクセス」がより適切であると解釈されるべきであり、「揮発性」の解釈は単に誤解を招くものです。
「変動性」は、マルチスレッドや割り込みなどの外的要因によって引き起こされます。揮発性で変更された変数が「揮発性」であるためではありません。外的原因がなければ、揮発性定義を使用しても変化しません。 ;
実際、揮発性の定義を使用した後、この変数は外部の理由で変更されないため、自信を持って使用できます。前の説明(変更可能)が誤解を招くかどうかを確認しましょう。
volatileキーワードは型修飾子です。これで宣言された型変数は、オペレーティングシステム、ハードウェア、その他のスレッドなど、コンパイラーが知らないいくつかの要因によって変更される可能性があります。このキーワードで宣言された変数に遭遇すると、コンパイラーは変数にアクセスするコードを最適化しないため、特別なアドレスへの安定したアクセスを提供できます。
このキーワードの使用例は次のとおりです。
volatile int vint;
volatileによって宣言された変数の値が必要な場合、前の命令がデータを読み取ったばかりであっても、システムは常にそれが配置されているメモリからデータを読み取ります。そして、読み取られたデータはすぐに保存されます。
例えば:
volatile int i=10;
int a=i;
//...
//コンパイラに明示的に通知しなかった他のコードは、iで動作しました
int b=i;
Volatileは、iはいつでも変更される可能性があり、使用するたびにiのアドレスから読み取る必要があることを指摘しています。したがって、コンパイラによって生成されたアセンブリコードは、iのアドレスからデータを再度読み取り、bに配置します。最適化の方法は、コンパイラがiからデータを読み取る2つのコード間のコードがiで動作していないことを検出したため、前回読み取ったデータを自動的にbに配置することです。もう一度私から読む代わりに。このように、iがレジスタ変数であるか、ポートデータを表す場合、エラーが発生しやすいため、volatileは特別なアドレスへの安定したアクセスを保証できます。
vc6では、コードの最適化は一般的なデバッグモードでは実行されないため、このキーワードの効果は表示されないことに注意してください。次に、アセンブリコードを挿入して、volatileキーワードがあるかどうか、およびプログラムの最終コードへの影響をテストします。
まず、classwizardを使用してwin32コンソールプロジェクトをビルドし、voltest.cppファイルを挿入して、次のコードを入力します。
#include<stdio.h>
void main(int argc,char *argv[])
{
int i = 10;
int a = i;
printf("i=%d",a);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm
{
mov dword ptr[ebp-4],20h
}
int b = i;
printf("i=%d",b);
}
次に、プログラムをデバッグバージョンモードで実行すると、出力結果は次のようになります。
i = 10
i = 32
次に、リリースバージョンモードでプログラムを実行すると、出力結果は次のようになります。
i = 10
i = 10
出力結果は、リリースモードで、コンパイラがコードを最適化し、2回目に正しいi値を出力しなかったことを明確に示しています。次に、volatileキーワードをiのステートメントに追加して、何が変わるかを確認します。
#include<stdio.h>
void main(int argc,char *argv[])
{
volatile int i = 10;
int a = i;
printf("i=%d",a);
__asm
{
` mov dword ptr[ebp-4],20h
}
int b = i;
printf("i=%d",b);
}
プログラムをそれぞれデバッグバージョンとリリースバージョンで実行すると、出力は次のようになります。
i = 10
i = 32
これは、このキーワードがその役割を果たしたことを示しています。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
volatileに対応する変数は、プログラムが知らないうちに変更される可能性があります。
たとえば、マルチスレッドプログラムでは、複数のプログラムが共有メモリ内のこの変数を操作できます
独自のプログラムでは、この変数がいつ変更されるかを判断できません
別の例として、外部デバイスの特定の状態に対応します。外部デバイスが操作されると、システムはドライバーイベントと割り込みイベントを介してこの変数の値を変更しますが、プログラムはそれを認識しません。
揮発性変数の場合、システムがそれを使用するたびに、キャッシュ内の元の値を使用して未知の変数がいつ変更されるかを適応させるのではなく、対応するメモリから直接抽出します。この変数の処理は最適化されません-明らかに、その値はいつでも変更される可能性があるためです。