const 修飾子は、このコードが変数の値を変更できないことを意味しますが、このコードの外部で値を変更できないことを意味するわけではありません。
const 修飾子を適用するには、2 つの異なる方法があります。
const で修飾されたオブジェクトをプログラムで変更してはなりません。そうしないと、プログラムの動作が未定義になります。const volatile
オブジェクトは、OS/ハードウェアなどによって変更できますが、プログラムによって割り当てられることはありません。誤解を避けるために、const オブジェクトとは、その定義で const 型を使用するオブジェクトです。
const 修飾型へのポインターは、そのポインターを介した (コンパイル時の) 変更を防ぎますが、同じオブジェクトへの他のポインターを使用して変更することができます。オブジェクト自体が const でない場合、動作は定義されます。ただし、コンパイラは、OS/ハードウェア/必要なものによる任意の変更を考慮して、プログラムのみがオブジェクトを変更すると想定する場合がありますvolatile
。
非 const 修飾型へのポインターは、他のポインターによる変更に関する限り、const へのポインターとまったく同じです。
ただし、 volatile は、「このデータは、このプログラムのコード以外の何かによって変更される可能性がある」と述べているため、コンパイラは最適化時にそのデータについて何も仮定しません。
したがって、違いは次のとおりです。
#include <stdio.h>
void some_other_function(const int *);
int main() {
int a = 0;
int volatile b = 0;
int const c = 0;
int const *constptr = &a;
int *ptr = (int*) constptr;
printf("%d\n", a); // compiler can assume a == 0 at this point, and
// replace the code with puts("0") if it wants to
printf("%d\n", b); // compiler cannot assume a value for b, it's volatile[*]
some_other_function(constptr); // defined in another TU
printf("%d\n", a); // compiler can *no longer* assume that a == 0,
// it might have changed
*ptr = 1; // there's another example of a changing, legally
some_other_function(&c);
printf("%d\n", c); // compiler can assume c == 0 because c is const
}
[*] 私は「値を想定できない」と言っていますが、仮想的な C 実装の中には、自動変数を検出するために必要な手段で変更する OS またはハードウェア メカニズムがないことをたまたま認識している可能性がありvolatile
ます。特にこの場合、への参照がb
関数をエスケープしていません。もしそうなら、実装がvolatile
この特定のコードで実際に無視できることに気付くかもしれませんが、リンカが I/O ポートなどのアドレスにマップする手段を提供することを知っているため、extern グローバルvolatile
変数を「適切に」扱う可能性があります。 .