26

重複の可能性:
const の正しさを売り込む

そのようなことが許可されているため、キーワードの有用性は何constですかC?C++

void const_is_a_lie(const int* n)
{ 
    *((int*) n) = 0;
}

int main()
{
    int n = 1;
    const_is_a_lie(&n);
    printf("%d", n);
    return 0;
}

出力: 0

constが引数の変更不可能性を保証できないことは明らかです。

4

5 に答える 5

45

constはコンパイラに対する約束であり、保証するものではありません。

例えば、

void const_is_a_lie(const int* n)
{ 
    *((int*) n) = 0;
}

#include <stdio.h>
int main()
{
    const int n = 1;
    const_is_a_lie(&n);
    printf("%d", n);
    return 0;
}

http://ideone.com/Ejogbに表示される出力は

1

のおかげでconst、コンパイラは値が変更されないと想定できるため、プログラムが高速になる場合は、値の再読み取りをスキップできます。

この場合、const_is_a_lie()契約に違反しているため、奇妙なことが起こります。契約に違反しないでください。そして、コンパイラーが契約の維持を支援してくれることをうれしく思います。キャストが悪い。

于 2012-06-05T23:59:40.760 に答える
10

この場合、nは定数へのポインタintです。キャストすると、修飾子int*が削除されるため、操作が許可されます。const

コンパイラにconst修飾子を削除するように指示すると、喜んでそうします。コンパイラーは、その仕事をさせれば、コードが正しいことを確認するのに役立ちます。const 性をキャストすることで、ターゲットnが非定数であることを知っていて、本当にそれを変更したいことをコンパイラーに伝えます。

ポインターが指しているものが実際に最初に宣言されていた場合、それを変更しようとすることで未定義の動作constが呼び出され、何かが起こる可能性があります。それはうまくいくかもしれません。書き込み操作が表示されない場合があります。プログラムがクラッシュする可能性があります。モニターがあなたを殴る可能性があります。(わかりました、おそらく最後のものではありません。)

void const_is_a_lie(const char * c) {
    *((char *)c) = '5';
}

int main() {
    const char * text = "12345";
    const_is_a_lie(text);
    printf("%s\n", text);

    return 0;
}

特定の環境によってはconst_is_a_lie、コンパイラ/ランタイムが書き込み不可のメモリ ページに文字列リテラル値を格納する可能性があるため、segfault (別名アクセス違反) が発生する可能性があります。

標準では、const オブジェクトの変更について次のように述べています。

7.1.6.1/4 cv 修飾子 [dcl.type.cv]

ミュータブル (7.1.1) と宣言されたクラス メンバーを変更できることを除いて、その有効期間 (3.8) 中に const オブジェクトを変更しようとすると、未定義の動作が発生します。

「先生、これをすると痛いです!」「だから、そうしないでください。」

于 2012-06-06T00:02:31.947 に答える
6

君の...

int n = 1;

...n読み取り/書き込みメモリに存在することを確認します。これはconst変数ではないため、後で変更しようとすると動作が定義されます。このような変数を指定するとconst、ポインタおよび/またはconstそれ以外のポインタと参照を混在させることができます。それぞれの constness は、プログラマがそのコードの「ブランチ」で偶発的な変更を防ぐための単なる方法です。「ブランチ」と言うのは、与えられたアクセスをnツリーとして視覚化できるためです-ブランチがマークされるとconst、すべてのサブブランチ(n追加のローカル変数、関数パラメーターなどがそこから初期化されるかどうかへのさらなるポインター/参照)constもちろん、その constness の概念を明示的にキャストしない限り、 のままにする必要があります。キャストアウェイconstn最終的には変更可能/変更可能/非- のメモリアドレスに書き戻すため、のように変更可能な変数に対しては安全です (混乱する可能性がある場合) 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++ でより明示的で対象を絞ったキャスト表記法が導入されたのは、まさにキャストが安全でない可能性があるためです。リスクを否定することはできません。自分が何を求めているのか、なぜそれが時々大丈夫で他の人はそうではないのかを理解し、それと一緒に暮らす必要があるだけです.

于 2012-06-06T01:42:27.497 に答える
2

型システムは、あなたを子守するためではなく、助けるためにあります。const に関してだけでなく、さまざまな方法で型システムを回避できます。そのたびに、プログラムから 1 つの安全性を取り除くことになります。void*必要に応じて渡してキャストすることで、const-correctness または基本的な型システムを無視できます。これは、constが嘘であることを意味するのではなく、コンパイラのやり方を強制できるということだけです。

constコンパイラに関数の契約を認識させ、違反しないようにする方法としてあります。型付けされた変数が存在するのと同じように、データを解釈する方法を推測する必要がなく、コンパイラーが助けてくれます。しかし、それはベビーシッターではありません。強制的に const ネスを削除するように指示したり、データを取得する方法を指定したりすると、コンパイラは、アプリケーションを設計した後、誰がそれを行うかを許可します。次にあなたの判断を推測してください...

さらに、場合によっては、実際に未定義の動作を引き起こし、アプリケーションがクラッシュすることさえあります (たとえば、実際には const であるオブジェクトから const をキャストし、オブジェクトを変更すると、副作用が見られないことがわかる場合があります)。いくつかの場所 (コンパイラは値が変更されないと想定し、定数の折りたたみを実行した) または、定数が読み取り専用メモリ ページに読み込まれた場合にアプリケーションがクラッシュする可能性があります。

于 2012-06-06T03:20:18.193 に答える
1

const不変性は保証されていません。標準では、 const_castconst データの変更を許可する が定義されています。

constより多くの意図を宣言し、読み取り専用のデータを変更しないようにするのに役立ちます。そうしないと、よく考えるように求めるコンパイル エラーが表示されます。考えを変えることはできますが、お勧めしません。

他の回答で述べたように、 const-ness を使用すると、コンパイラもう少し最適化される可能性がありますが、その利点は必ずしも重要ではありません。

于 2012-06-06T00:03:13.337 に答える