君の...
int n = 1;
...n
読み取り/書き込みメモリに存在することを確認します。これはconst
変数ではないため、後で変更しようとすると動作が定義されます。このような変数を指定するとconst
、ポインタおよび/またはconst
それ以外のポインタと参照を混在させることができます。それぞれの constness は、プログラマがそのコードの「ブランチ」で偶発的な変更を防ぐための単なる方法です。「ブランチ」と言うのは、与えられたアクセスをn
ツリーとして視覚化できるためです-ブランチがマークされるとconst
、すべてのサブブランチ(n
追加のローカル変数、関数パラメーターなどがそこから初期化されるかどうかへのさらなるポインター/参照)const
もちろん、その constness の概念を明示的にキャストしない限り、 のままにする必要があります。キャストアウェイconst
n
最終的には変更可能/変更可能/非- のメモリアドレスに書き戻すため、のように変更可能な変数に対しては安全です (混乱する可能性がある場合) const
。これらのシナリオで問題を引き起こすと想像できるすべての奇妙な最適化とキャッシングは許可されていません。これは、標準が、私が説明したケースでの正常な動作を要求し、保証しているためです。
const
悲しいことに、 say のような真に本質的な変数の constness を取り除くことも可能であり、それらをconst int o = 1;
変更しようとすると、未定義の動作が発生します。これには多くの実際的な理由があります。たとえば、コンパイラがそれらをメモリに配置する権利があり、読み取り専用としてマークします (たとえば、UNIX を参照してください)。mprotect(2)
) 書き込みを試みると CPU トラップ/割り込みが発生するか、最初に設定された値が必要になるたびに変数から読み取られるようにする (値を使用するコードで変数の識別子が言及されていない場合でも)、または inlined-at を使用する-元の値のコンパイル時のコピー - 変数自体への実行時の変更を無視します。したがって、標準は動作を未定義のままにします。意図したとおりに変更されたとしても、プログラムの残りの部分はその後未定義の動作をします。
しかし、それは驚くべきことではありません。型についても同じ状況です - もしあなたが...
double d = 1;
*(int*)&d = my_int;
d += 1;
... の型についてコンパイラに嘘をついたことがありd
ますか? 最終的d
には、おそらくハードウェア レベルで型指定されていないメモリを占有するため、コンパイラがこれまでに持っていたのは、ビット パターンをシャッフルして出し入れするパースペクティブだけです。ただし、ハードウェアの値と double 表現によっては、有効な double 値を表さないmy_int
ビットの無効な組み合わせを作成した可能性があります。これにより、メモリを CPU レジスタに読み戻そうとしたり、d
または、未定義の動作があり、d
たとえば+= 1
、CPU トラップ/割り込みを生成する可能性があるものを使用します。
これは C や C++ のバグではありません...これらは、ハードウェアに対して疑わしい要求を行うことができるように設計されているため、自分が何をしているのかを知っていれば、奇妙だが便利なことを実行でき、フォールバックする必要はほとんどありません。アセンブリ言語を使用して、デバイス ドライバーやオペレーティング システム用の低レベル コードを記述できます。
それでも、C++ でより明示的で対象を絞ったキャスト表記法が導入されたのは、まさにキャストが安全でない可能性があるためです。リスクを否定することはできません。自分が何を求めているのか、なぜそれが時々大丈夫で他の人はそうではないのかを理解し、それと一緒に暮らす必要があるだけです.