「文字列の配列」というフレーズには注意してください。「文字列」はCのデータ型ではありません。それはデータレイアウトです。具体的には、文字列は次のように定義されます。
最初の null 文字で終了し、最初の null 文字を含む連続した文字列
の配列には文字列char
が含まれる場合があり、char*
ポインターは文字列(の最初の文字)を指す場合があります。(標準では、文字列の最初の文字へのポインターを string へのポインターとして定義しています。)
char some_array_of_strings[3][200];
これは、各要素が の 200 要素配列である 3 要素配列を定義しますchar
。(これは 2 次元配列であり、C では単に配列の配列です。)
strcpy(some_array_of_strings[2], "Some garbage");
文字列リテラル"Some garbage"
は、静的に割り当てられた無名のchar
;の配列を参照します。プログラムの実行全体にわたって存在し、変更することはできません。このstrcpy()
呼び出しは、その名前が示すように、その配列の内容を、終端のnull 文字までを含めて にコピーします。'\0'
some_array_of_strings[]
strcpy(some_array_of_strings[2], "Some other garbage");
同じこと: これは内容"Some other garbage"
をsome_array_of_strings[2]
にコピーし、前の行でコピーしたものを上書きします。どちらの場合も、十分なスペースがあります。
文字列リテラルを変更するのではなく、文字列リテラル (より正確には、上記の無名配列) からバイトをコピーして独自の配列を変更しています。
some_array_of_strings[1]="Some garbage";
これは単に「機能しない」だけでなく、違法です。C には配列の代入はありません。
もっと簡単な例を見てみましょう:
char arr[10];
arr = "hello"; /* also illegal */
arr
配列型のオブジェクトです。ほとんどのコンテキストでは、配列型の式は、配列オブジェクトの最初の要素へのポインターに暗黙的に変換されます。arr
これは、オブジェクト名と文字列リテラルの代入の両側に適用されます"hello"
。
しかし、左側のポインターは単なるポインター値です。ポインター オブジェクトはありません。技術的に言えば、これはlvalueではないため、割り当ての左側には記述できません42 = x;
)。
(配列からポインターへの変換が行われなかったとしても、C では配列の代入が許可されていないため、依然として不正です。)
割り当ての左側にある配列の問題に関する詳細:
配列式がポインターに分解されないコンテキストは、配列式が次の場合です。
- 単項演算子のオペランド
sizeof
。
&
単項(address-of) 演算子のオペランド。また
- 配列オブジェクトを初期化するために使用される初期化子の文字列リテラル。
割り当ての左側は、これらのコンテキストのいずれでもないため、次のようになります。
char array[10];
array = "hello";
LHS は原則としてポインターに変換されます。しかし、結果のポインター式は左辺値ではなくなり、割り当てが制約違反になります。
これを見る 1 つの方法は、式array
がポインターに変換され、代入が不正になることです。もう 1 つは、代入が不正であるため、プログラム全体が有効な C ではないため、動作が定義されておらず、変換が行われるか行われないかを尋ねても意味がないということです。
(私は「違法」という言葉を少し早口でゆるく使っていますが、この回答はすでに十分に長いので、ここには入りません。)
推奨される読み物: comp.lang.c FAQのセクション 6 。これは、C における配列とポインターの間のしばしば戸惑う関係を説明する優れた仕事をしています。