5

次の 2 つのプログラムprog1prog2を考えてみましょう。ここで、ポインターを使用してconst修飾変数の値を変更しようとすると、警告 (エラーではありません)が表示されますが、それでもプログラムは実行され、新しい値が表示されます。代入ステートメントを使用して 2 番目のプログラムの値を変更すると、エラー(警告ではない)が発生します。iptr"initialization discards qualifiers from pointer target type|"iassignment of read-only variable 'i'|

この前提から生じる混乱を次に示します。

const1)どのような状況でも、読み取り専用の修飾変数の値を変更できるのはなぜですか?それはconst修飾子を使用する目的を無効にしませんか?そうしようとすると、エラーが発生するべきではありませんか?

2) 何らかの奇妙な理由により、定数の値を変更することが許可されている場合でもconst、ポインターを使用して読み取り専用の修飾された変数の値を変更すること (警告付きで許可されています) と代入操作を使用することの違いはなぜですか? (これは単に許可されておらず、エラーが発生します)?

//prog1
#include <stdio.h>

int main ()
{
 const int i=8;
 int *ptr=&i;
 *ptr=9;
 printf("%d",*ptr);  //Prints new value nevertheless
}

警告:初期化により、ポインター ターゲット型から修飾子が破棄されます |

//prog2
#include <stdio.h>

int main()
{
const int i=8;
i=10;
printf("%d",i);
}

エラー:読み取り専用変数 'i' の代入 |

H2CO3の編集

ここで、const修飾された変数の値を複数回変更します。prog1

//prog3
#include <stdio.h>

int main ()
{
const int i=8;
int *ptr=&i;
*ptr=9;
*ptr=10;
printf("%d",*ptr);  //Prints 10
}
4

2 に答える 2

9

const1)どのような状況でも、読み取り専用の修飾変数の値を変更できるのはなぜですか? const修飾子を使用する目的に反していませんか?

代入演算子を使用して const 修飾されたオブジェクトを変更しようとすると、制約違反になります。

6.5.16制約の下:

2 代入演算子は、左オペランドとして変更可能な左辺値を持つものとします。

変更可能な左辺値は、6.3.2.1 (1) で定義されています。

変更可能な左辺値は、配列型を持たず、不完全な型を持たず、const 修飾された型を持たず、構造体または共用体の場合はメンバー (再帰的にメンバーを含む) を持たない左辺値です。または、含まれているすべての集約または共用体の要素) を const 修飾された型で指定します。

制約違反として、5.1.1.3 (1) に従ってコンパイラからの診断メッセージが必要です。

準拠する実装は、前処理の翻訳単位または翻訳単位に構文規則または制約の違反が含まれている場合、動作が未定義または実装として明示的に指定されている場合でも、少なくとも 1 つの診断メッセージ (実装定義の方法で識別される) を生成する必要があります。定義されています。他の状況では、診断メッセージを生成する必要はありません。

ただし、実装で無効なプログラムを拒否する必要はないため、診断メッセージがエラーではなく警告になることもあります。

constただし、 const 修飾された型を持たない左辺値を介して宣言されたオブジェクトの変更は、未定義の動作を呼び出しますが、制約違反ではありません 6.7.3 (6):

const 修飾されていない型の左辺値を使用して、const 修飾された型で定義されたオブジェクトを変更しようとした場合、動作は未定義です。

これは制約違反でも無効な構文でもないため、診断メッセージを発行する必要さえありません。

そうしようとすると、エラーが発生するはずではありませんか?

const 修飾型の左辺値を使用してオブジェクトを変更しようとすると、診断メッセージが表示される必要があります。

これは宣言された意図に対する重大な違反であるため、ほとんどのコンパイラはこれらの状況でエラーを出力します。

次のように、const 修飾されていない型の左辺値を使用して const 修飾された型を持つオブジェクトを変更しようとすると、

const int i=8;
int *ptr=&i;
*ptr=9;

i式を変更しようとすると*ptr = 9、未定義の動作が呼び出されますが、制約違反 (または構文エラー) ではないため、診断メッセージは必要ありません (何も表示されません)。

初期化のために発行された診断メッセージがあります

int *ptr = &i;

6.5.16.1 (1) によると、これは再び制約違反であるためです。

次のいずれかが成り立つ:

  • 左のオペランドはアトミック、修飾、または非修飾の算術型を持ち、右のオペランドは算術型を持ちます。
  • 左のオペランドには、右の型と互換性のある構造体または共用体型のアトミック、修飾、または非修飾バージョンがあります。
  • 左のオペランドがアトミック、修飾、または非修飾のポインター型を持ち、(左辺値変換後に左のオペランドが持つ型を考慮して) 両方のオペランドが互換性のある型の修飾または非修飾バージョンへのポインターであり、左が指す型がすべてを持っているright が指す型の修飾子
  • 左のオペランドには、アトミック、修飾、または非修飾のポインター型があり、(左辺値変換後の左のオペランドの型を考慮して) 1 つのオペランドはオブジェクト型へのポインターであり、もう 1 つは修飾または非修飾バージョンのポインターです。 void であり、左が指す型には、右が指す型のすべての修飾子があります。
  • 左のオペランドは原子ポインタ、修飾ポインタ、または非修飾ポインタで、右オペランドは null ポインタ定数です。また
  • 左のオペランドの型はアトミック、修飾済み、または非修飾の _Bool で、右のオペランドはポインターです。

ただし、その診断は通常警告であり、エラーではありませんconst

int *ptr = (int*)&i;

一方、constfrom をキャストすることはできませんi

const 修飾されたオブジェクト型へのポインターからキャストすることによって取得された、const 修飾されていないオブジェクト型へのポインターを介してオブジェクトを変更することは、指しているオブジェクトが modifiable である場合にconst有効です。愚かな例:

int i = 8;
const int *cptr = &i;  // valid, no problem adding const
int *mptr = (int*)cptr;
*mptr = 9;             // no problem, pointee is non-const

2) 何らかの奇妙な理由で定数の値を変更することが許可されている場合でも、ポインターを使用して読み取り専用の const 修飾変数の値を変更すること (これは警告付きで許可されています) と代入を使用することの違いはなぜですか?操作 (これは単に許可されておらず、エラーが発生します)?

const 修飾された型を持つオブジェクトに直接割り当てることは、制約違反であるだけでなく、規定されたセマンティクスの明らかな違反でもあります。オブジェクトを明示的に宣言すると、 const「そのオブジェクトを変更したくない」ということになります。

const 修飾されていない型へのポインターを介してオブジェクトを変更することは、制約違反ではなく、ポインターが const 修飾された型を持っている場合にのみ未定義の動作になります。const 修飾された型へのポインターを対応する const 修飾されていない型へのポインターに変換することは許可されており、そのポインターを介してポインターを変更することは有効な場合があるため、警告のみが表示され、変換が行われない場合にのみ表示されます。明示的。

与えられた短い例では、コンパイラは、ポインティング先が const 修飾された型を持っていることを検出できたため、変更によって未定義の動作が呼び出されますが、一般に、そのようなことは困難であり、多くの場合検出が不可能です。したがって、コンパイラは単純なケースを検出しようとさえしません。努力する価値はありません。

于 2013-05-14T13:38:40.767 に答える
2

constどのような状況でも、読み取り専用の修飾変数の値を変更できるのはなぜですか?

私たちはそうではありません。なぜあなたがそれを想定しているのか、どの例がそれを示しているのかわかりません。

ポインターを使用して読み取り専用の const 修飾変数の値を変更することの違い (これは許可されていますが、警告があります)

繰り返しますが、許可されていないため、警告が表示されます。(警告は真剣に受け止めてください - あなたはそれらに何の意味も与えていないようです...) コンパイラは、ポインターがconst修飾されたオブジェクトを指していることを認識していないだけです (非 const として宣言されているためT *) 。 .

変数の変更が機能する理由について:

説明 #1: 未定義の動作 (制約違反) であるため、何でもできます。

説明 #2: おそらくローカル自動変数のようにスタックに格納されているだけで、実際に変更することができます。

于 2013-05-14T12:15:38.607 に答える