2009-06-03 47 views
4

ポインタを使って定数の値を変更したかったのです。なぜローカルconst変数をポインタキャストで変更できますか?Cでグローバルconst変数を変更できないのはなぜですか?

定数の値が変更された期待されるように、次のコード

int main() 
{ 
    const int const_val = 10; 
    int *ptr_to_const = &const_val; 

    printf("Value of constant is %d",const_val); 
    *ptr_to_const = 20; 
    printf("Value of constant is %d",const_val); 
    return 0; 
} 

を考えます。

グローバルな定数で同じコードを試したところ、次の実行時エラーが発生しています。 Windowsクラッシュレポーターが開かれています。この文で最初のprintf文を印刷した後、実行可能ファイルが停止しています。 "* ptr_to_const = 20;"

const int const_val = 10; 
int main() 
{ 
    int *ptr_to_const = &const_val; 
    printf("Value of constant is %d",const_val); 
    *ptr_to_const = 20; 
    printf("Value of constant is %d",const_val); 
    return 0; 
} 

このプログラムは、コードブロックのIDEとmingwの環境でコンパイルされた次のコードを考えてみましょう。

誰でも何が起こっているのか説明できますか?

+0

エラーはどうなりますか? – Robert

+2

Dup:http://stackoverflow.com/questions/712334/does-the-evil-cast-get-trumped-by-the-evil-compiler(これ以上の回答は、読み込み専用メモリの代わりに何が起こるかを説明しています読み取り/書き込みスタックの) – ephemient

+0

Windowsクラッシュレポーターが開かれています。実行可能ファイルは、最初のprintfステートメントを印刷した後に停止しています – udpsunil

答えて

7

読み取り専用メモリです!

基本的に、コンピュータは2レベルのページテーブルシステムを使用して仮想アドレスから物理アドレスを解決します。その巨大なデータ構造に加えて、ページが読めるかどうかを表す特別なビットがあります。これは、ユーザープロセスがおそらく自分のアセンブリを上書きするべきではないからです(自己修正コードはすばらしいですが)。もちろん、彼らはおそらくまた彼ら自身の定数変数を書くべきではありません。

"const"ファンクションレベルの変数は、スタックに存在するため、読み込み専用メモリに置くことはできません。しかし、コンパイラ/リンカーはあなたのconstを見て、それを読み込み専用メモリ(定数)に入れることで好意を得ます。明らかに、それを上書きすることは、プロセスを終了することによってその怒りを取り除くカーネルのすべての種類の不幸を引き起こすでしょう。

+0

質問がありますが、グローバル変数の値は編集可能ですか?したがって、コンパイラは 'const'を参照して読み取り専用メモリに割り当てますか(これは正確で、どのようにしてマシンに依存しないのでしょうか?) –

16

これは定数ですが、とにかくそれを変更するためにいくつかのトリックを使用しているため、未定義の動作が発生します。グローバル定数はおそらく読み取り専用メモリにあるため、変更することはできません。これを実行しようとすると、ランタイムエラーが発生します。

定数ローカル変数はスタック上に作成され、変更可能です。この場合、定数を変更することで逃げることができますが、それでも変わったことにつながる可能性があります。例えば、コンパイラは、定数の代わりにさまざまな場所で定数の値を使用していた可能性があります。そのため、「定数を変更する」はこれらの場所に影響を与えません。

3

CおよびC++でのポインター定数のキャストは、ポインティングされた変数がもともと非constであったことが確かな場合にのみ安全です。さもなければ、それは未定義で、あなたのコンパイラ、月の位相などに依存して、最初の例もうまく失敗する可能性があります。

1

最初に値を変更するとは思わないはずです。標準によると、未定義の動作です。これは、グローバル変数と最初の両方で間違っています。ただそれをしないでください:)それは、他の方法で、またはローカルとグローバルの両方でクラッシュしている可能性があります。

0

この動作は仕様で定義されていないため、実装固有のものであり、移植性がないため、良い考えではありません。

なぜ定数の値を変更したいのですか?

+1

定数で制御されるコードの一部をテストするためです。 – udpsunil

+0

@udpsunil:いくつかのマクロを使用することをお勧めします。そのようなテスト目的のために必要です – Christoph

+0

@Buggieboyいいえ、それは定義されていません。単純に定義されていません。それは2つの異なるものです! – dhein

0

注:これは、この質問に重複してリンクするCan we change the value of an object defined with const through pointers?への回答を意図しています。

標準では、constオブジェクトへのポインタを構成し、そのオブジェクトへの書き込みを試みるコードでコンパイラが実行する必要がないことが要求されます。可能であれば、不揮発性RAMを使用する実装は、書き込み可能なメモリ領域に変数constを正当に置くことができますが、ユニットの電源が切れても内容は残ります標準が、constのメモリへのポインタでないものを作成するコードをどのように処理するかについてコンパイラがどのように処理するかについての要件を課していないということは、そのようなコードの正当性に明白に影響するものではない。。コンパイラはconstに書き込むコードに不要な「最適化」を適用する傾向になる場合

void protected_ram_store_u32(uint32_t volatile const *dest, uint32_t dat) 
{ 
    BYPASS_WRITE_PROTECT = 0x55; // Hardware latch which enables writing to 
    BYPASS_WRITE_PROTECT = 0xAA; // const memory if written with 0x55/0xAA 
    BYPASS_WRITE_PROTECT = 0x04; // consecutively followed by the bank number 
    *((volatile uint32_t*)dest)=dat; 
    BYPASS_WRITE_PROTECT = 0x00; // Re-enable write-protection of const storage 
} 
void protected_ram_finish(void) {} 
... 
protected_ram_store(&action_count, action_count+1); 
protected_ram_finish(); 

volatile const uint32_t action_count; 

BYPASS_WRITE_PROTECT = 0x55; // Hardware latch which enables writing to 
BYPASS_WRITE_PROTECT = 0xAA; // const memory if written with 0x55/0xAA 
BYPASS_WRITE_PROTECT = 0x04; // consecutively followed by the bank number 
*((uint32_t*)&action_count)++; 
BYPASS_WRITE_PROTECT = 0x00; // Re-enable write-protection of const storage 

:でも、このような実装では、しかし、それはのようなものを置き換えるために、おそらく良いアイデアです個別にコンパイルされたモジュールに「protected_ram_store」を移動することで、そのような最適化を防ぐことができます。それはまた有用であり得る。コードは、他のプロトコルを使用してメモリに書き込むハードウェアに移動する必要があります。たとえば、一部のハードウェアでは、より複雑な書き込みプロトコルを使用して、誤った書き込みの可能性を最小限に抑えることができます。明示的な目的が "通常のconst"メモリに書き込むことであるルーチンを持つことは、そのような意図を明確にするでしょう。

1

ここに2つのエラーがあります。最初のものである:

C11 6.5.4/3に係る 制約違反(以前の規格は同様のテキストを持っていた)である
int *ptr_to_const = &const_val; 

制約

コンバージョン6.5.16.1の制約によって許可されている以外のポインタは、明示的なキャストによって指定されるものとする。

const int *からint *への変換は、6.5.16.1(これは、hereと表示される)の制約によって許可されていません。

コンパイラが制約違反に遭遇したとき、スイッチに応じて「警告」(またはスイッチに応じてまったく何も書かない)を書いて、あなたのコードに何か書いたふりをし続けます。これはしばしば、プログラマーが期待したとおりに動作しないプログラムにつながり、実際には予測可能な方法で動作しません。コンパイラはなぜこれを行うのですか?私を打ち負かすが、それは確かにこのような質問の無限のストリームのためになります。


gccが、あなたがint *ptr_to_const = (int *)&const_val;を書いたかのように進行するように見えます。

このコードは、明示的なキャストが使用されているため、制約違反ではありません。しかし、これは第二の問題を引き起こします。 *ptr_to_const = 20;行は、constオブジェクトに書き込もうとします。これは、標準から関連するテキストは、6.7.3/6であり、undefined behaviourが発生します。

試みが非const-と左辺値を使用してconst修飾型で定義オブジェクトを変更するためになされた場合修飾された型では、動作は未定義です。

このルールは、拘束ではなくセマンティックです。つまり、標準では、コンパイラに警告やエラーメッセージを表示する必要はありません。プログラムはちょうど間違っていて、あなたが観察したものに限定されるものではありません。

関連する問題