#include <stdio.h>
int main()
{
const int a = 12;
int *p;
p = &a;
*p = 70;
}
うまくいきますか?
これは「未定義の動作」です。つまり、標準に基づいて、これを試したときに何が起こるかを予測することはできません。特定のマシン、コンパイラ、およびプログラムの状態に応じて、異なる処理を実行する場合があります。
この場合、最も頻繁に発生するのは、答えが「はい」になるということです。変数は、constかどうかにかかわらず、単なるメモリ内の場所であり、constnessの規則を破って、単に上書きすることができます。 (もちろん、プログラムの他の部分がその定数データが一定であることに依存している場合、これは深刻なバグを引き起こします!)
ただし、場合によっては、最も一般的にはconst static
data-コンパイラは、そのような変数をメモリの読み取り専用領域に配置する場合があります。たとえば、MSVCは通常、実行可能ファイルの.textセグメントにconst static intを配置します。つまり、書き込みしようとすると、オペレーティングシステムが保護違反をスローし、プログラムがクラッシュします。
コンパイラとマシンの他の組み合わせでは、まったく異なることが発生する可能性があります。あなたがcanで確実に予測することの1つは、このパターンがあなたのコードを読まなければならない人をいらいらさせることです。
これは未定義の動作です。証明:
/* program.c */
int main()
{
const int a = 12;
int* p;
p = &a;
*p = 70;
printf("%d\n", a);
return 0;
}
gcc program.c
そしてそれを実行します。出力は70(gcc 4.3)になります
次に、次のようにコンパイルします。
gcc -O2 program.c
そしてそれを実行します。出力は12になります。最適化を行うと、コンパイラーはおそらく12をレジスターにロードし、printfのにアクセスする必要があるときに再度ロードする必要はありません。変更できないことを「知っている」ためです。
ポインターを使用してconst
修飾オブジェクトを変更すると、未定義の動作が呼び出され、そのような結果になります。それはあなたが期待しているものである可能性が高いです。以前の値は変更されていません(.text
などに配置されている場合)。
実際、gccで動作します。しかし、それは好きではありませんでした:
test.c:6:警告:割り当てはポインターのターゲットタイプから修飾子を破棄します
しかし、実行すると値は変わりました。私は明白なノーノーを指摘しません...
はい、そのようなコードを使用してそれを行うことができます。しかし、a
がグローバルである場合、コードは適用されません(gccでコンパイルされたプログラムがsegmentation fault
。)
一般的に言えば、最愛のCでは、ほとんどの場合、変更または公開されるべきではないものをハッキングする方法を見つけることができます。ここでの例はconstです。
しかし、貧しい人(多分6か月後の私)がコードを維持していることを考えると、そうしないことがよくあります。
ここで、ポインタp
のタイプはint*
で、タイプconst int*
の値が割り当てられています(&a
=> const int
変数のアドレス)。
Gccは警告をスローしますが、暗黙のキャストは定数を排除します(これは実装に大きく依存することに注意してください)。
ポインタはconst
として宣言されていないため、そのようなポインタを使用して値を変更できます。
ポインタがconst int* p = &a
として宣言される場合、*p = 70
を実行できません。
定数変数を指すポインターを使用して、定数変数の値を変更することはできません。このタイプのポインターはPointer to a constant
と呼ばれます。
Constant Pointer
と呼ばれる別の概念もあります。つまり、ポインタがメモリの場所を指すと、別の場所を指すようにすることはできません。
悪い、悪い考え。
また、動作はプラットフォームおよび実装に固有です。定数が書き込み不可のメモリに格納されているプラットフォームで実行している場合、これは明らかに機能しません。
そして、一体なぜあなたはそうしたいのですか?ソース内の定数を更新するか、それを変数にします。
このコードには制約違反が含まれています。
const int a = 12;
int *p;
p = &a;
違反している制約は、C11 6.5.16.1/1「単純な割り当て」です。両方のオペランドがポインターの場合、左が指す型には、右が指す型のすべての修飾子が必要です。 (そして、型、sans修飾子は互換性がなければなりません)。
したがって、&a
のタイプはconst int *
であり、修飾子としてconst
が含まれているため、制約に違反しています。ただし、その修飾子はp
のタイプ(int *
)には表示されません。
コンパイラは診断を出力する必要があり、実行可能ファイルを生成しない可能性があります。プログラムは言語の規則に準拠していないため、実行可能ファイルの動作は完全に定義されていません。