読んでいると、このタイプの宣言と次の行に出くわしました-
const volatile char *p=(const volatile char *) 0x30;
Pの値は外部条件によってのみ変更されます
外部条件がわかりません。また、このタイプのdeclarationの実用的な用途は何ですか?
const
は、プログラムのフローがp
が指すものを変更しないことを示しています。ポインターを逆参照した後で値を変更しようとすると、コンパイル時エラーが発生します。
*p = 'A'; // will not compile
これは特に強力な契約ではないことに注意してください。場所0x30
の値は、p
以外のエイリアスでない非constポインターを使用して変更できます。
volatile char *q = 0x30;
*q = 'A'; // will compile
この契約を破る別の方法は、const
からp
をキャストすることです:
*(volatile char *) p = 'A'; // will compile
ただし、volatile
は、別のスレッド、カーネル、非同期シグナルハンドラー、または同じメモリ空間にアクセスできる外部デバイスによって引き起こされる可能性のある変更を除外しません。このようにして、コンパイラーはp
が指す値は変更されず、参照されるたびにメモリーからロードされるという誤った想定を行うことはできません。
/*
The character at 0x30 will be read on every iteration,
even if the compiler has proven that the program itself
doesn't modify the value at that address.
*/
while (*p) {
...
}
コンパイラが誤ってそのような構成を最適化する場合、メモリから値を1回だけロードしてレジスタに保持する命令を発行する可能性があります。レジスターは本質的に独立したコピーであり、元の場所への変更はそこに反映されません、そして言うまでもなく、これはいくつかの非常に厄介なバグを引き起こす可能性があります。
たとえば、ネットワークカードの読み取り専用ハードウェアレジスタについて考えてみます。
プログラムの制御外で変更される可能性があるため、コンパイラーはその値をレジスターにキャッシュしたり、離れた場所で最適化したりすることはできません。したがって、volatile
です。
また、読み取り専用であるため、書き込みを行うべきではありません。したがって、const
です。
まず、C11
標準の例、6.7.3章のType qualifiersから例を引用します。
宣言されたオブジェクト
extern const volatile int real_time_clock;
ハードウェアによって変更可能である場合がありますが、割り当て、増分、減分はできません。
また、関連する脚注(134)、
揮発性宣言を使用して、メモリマップされた入出力ポートに対応するオブジェクト、または非同期割り込み関数によってアクセスされるオブジェクトを記述できます。そのように宣言されたオブジェクトに対するアクションは、式を評価するためのルールで許可されている場合を除き、実装によって「最適化」されたり、並べ替えられたりしてはなりません。
つまり、変数の値はハードウェアによって(メモリマッピングを介して)変更できますが、「プログラムによって」変更することはできません。
つまり、ここでの利点は2つあります。
volatileキーワードの概要 という記事を使用できます。
変数は、値が予期せず変更される可能性がある場合は常に、揮発性として宣言する必要があります。実際には、3つのタイプの変数のみが変更できます。
- メモリマップされた周辺レジスタ
- 割り込みサービスルーチンによって変更されたグローバル変数
- マルチスレッドアプリケーション内のグローバル変数
そして:
組み込みシステムには、通常は高度な周辺機器を備えた実際のハードウェアが含まれています。これらのペリフェラルには、プログラムフローと非同期に値が変化する可能性のあるレジスタが含まれています。非常に単純な例として、アドレス0x1234にある8ビットのステータスレジスタを考えてみます。ステータスレジスタがゼロ以外になるまでポーリングする必要があります。身廊と間違った実装は次のとおりです。
UINT1 * ptr = (UINT1 *) 0x1234; // Wait for register to become non-zero. while (*ptr == 0); // Do something else.
コンパイラーは次のようなアセンブリー言語を生成するため、オプティマイザーをオンにするとすぐに、これはほぼ確実に失敗します。
mov ptr, #0x1234 mov a, @ptr loop bz loop
Constは、プログラムは変数を変更しないと述べていますが、記事の外部ソースに記載されているように、揮発性では変数が最適化されない可能性があります。
* const volatile char * p =(const volatile char)0x30;
意味:pの値は、外部条件によってのみ変更されます。
概念的には、このタイプの変数はlogical viewerと考えることができます。コンセプトはドアののぞき穴に似ています。のぞき穴を使用すると、ドアの反対側にあるものを表示できますが、反対側にあるものを変更することはできません(const) 。ただし、ドアの外側の状態は、自分の意志で変わる可能性があります(volatileです)。あなたは何が起こるかを見ることができますが、何が起こるかを変更することはできません。
たとえば、組み込みシステムには、外界で発生しているイベントに関する状態情報を提供するように設計されたハードウェアレジスタがあります。たとえば、RPMを検出するために使用される光学式エンコーダは、レジスタに値を設定します。回転するたびに、LEDからの光を感知し、ハードウェアレジスタの値を変更します。これは、外部条件が意味するものです。図の反対側、つまりコード(おそらくPID制御ループ)では、この情報を読み取ってループの調整に使用できますが、この値を変更したり、変更したりすることはできません。 (const)
ソフトウェアの観点からすると、これは次のことを示しています。
const
は変数定数を作成しません。コンパイラに書き込みアクセスを拒否させるだけです。これはまだ変数に書き込むことが可能です(たとえば、const-castedポインターを介して)。
const
は、コーディングの誤りに対する保護として見ることができます。
この宣言は、外部のイベントがp
に書き込む可能性があるため、アクセス(キャッシュ、順不同実行(?)、...)を最適化しないようコンパイラーに指示しながら、誤ってp
に書き込まないようにします。