web-dev-qa-db-ja.com

C 2文字ポインターの宣言と初期化

私はいつもそれを宣言します

char *c = "line";

と同じでした

char c[] = "line";

そして私はそうしました

char **choices = { "New Game", "Continue Game", "Exit" };

これは私に互換性のないポインタ型を与えます、ここで

char *choices[] = { "New Game", "Continue Game", "Exit" };

しません。これを理解するのに助けはありますか?

25
vascop

まあ、彼らは同じではありません。ほとんどの人にとってそれらを同じであると考える方が簡単なので、上記のような問題が発生するまで誰もがそのように考え始めます:-)

長くて曲がりくねったものを書くつもりでしたが、それから私は考えました...誰か他の人がすでにこれをしているに違いありません。そして、彼らは持っています。これはかなり良い説明です:

http://www.lysator.liu.se/c/c-faq/c-2.html

それについて考える最も簡単な方法は、次のようなことをするときです。

 char * foo = "something"; 

あなたは本当に次のようなことをしています:

 char randomblob [] = "何か"; 
 char * foo = randomblob; 

さて...それは実際には正確な画像ではありません(ただし、私はコンパイラの専門家ではありません)。少なくとも、もう少し正確な方法で物事を考えることができます。

したがって、問題に戻って、[〜#〜] i [〜#〜]が正しく理解している場合(これは保証されません)、次のことができます。 t Cで例の3行目を実行します。ここで、誰かがここで正しいことを行うコンパイラを書くことができるのは正しいですが、gccはそうではありません。ただし、4番目の例は「正しいこと」を実行し、「const char配列自体をそれぞれ指すポインターの配列」を提供します。

私はかつて、複雑なCタイプを英語に翻訳するWebページに出くわしました。それはおそらく90年代前半だったかもしれませんが、Googleで十分にググると、先ほど書き上げたものよりも正確な言い回しの説明が得られると思います。

13
Wes Hardaker
char *c = "line";

is notと同じ

char c[] = "line";

それは本当に同じです

static char hidden_C0[] = "line";
char *c = hidden_C0;

ただし、変数hidden_C0には直接アクセスできません。ただし、生成されたアセンブリ言語をダンプすると表示されます(通常、.LC0のように、有効なC識別子ではない名前が付けられます)。そして、あなたのstring-of-constantsの配列の例では、同じことが起こっています:

char *choices[] = { "New Game", "Continue Game", "Exit" };

なる

char hidden_C0[] = "New Game";
char hidden_C1[] = "Continue Game";
char hidden_C2[] = "Exit";

char *choices[] = { hidden_C0, hidden_C1, hidden_C2 };

現在、これは文字列定数に使用できる特別な動作onlyです。書けない

int *numbers = { 1, 2, 3 };

あなたは書く必要があります

int numbers[] = { 1, 2, 3 };

そしてそれがあなたが書くことができない理由です

char **choices = { "a", "b", "c" };

どちらか。

(混乱は、配列がCのポインターと「同じ」であるという一般的な誤解の特別なケースです。それらは異なります。配列は配列です。配列型を持つ変数は、type decayそれらはsed(ほとんどすべてのコンテキストで)ですが、定義されている場合はそうではありません。)

16
zwol

大丈夫です、ただ書いてください

char **choices = (char *[]){ "New Game", "Continue Game", "Exit" };

ただし、choicesは線形アドレス指定にのみ使用できます。例えば:

printf ("%s", &(*choices)[0]);出力:_New Game_
printf ("%s", &(*choices)[1]);出力:_ew Game_
printf ("%s", &(*choices)[9]);出力:_Continue Game_

したがって、これは冗談ではなく、有効な初期化です。ちょうど別の種類の使用法。


また、非常に近い例 here もあり、複合リテラルの概念を説明しています。

5
Martin Babacaev

オンラインC標準(ドラフト n1256 ):

6.7.8初期化
...
11初期化子 スカラー なります 単一式、オプションで中括弧で囲みます。オブジェクトの初期値は式の初期値です(変換後)。単純な代入の場合と同じ型制約と変換が適用され、スカラーの型は宣言された型の非修飾バージョンになります。
...
16それ以外の場合は、 集合体または共用体タイプ なります 初期化子の括弧で囲まれたリスト 要素または名前付きメンバー。

強調が追加されました。

char **はスカラー型であり、集計ではないため、初期化子{"New Game", "Continue Game", "Exit"}と互換性がありません。対照的に、char *[]は集約(配列)タイプです。

同様に、あなたは次のようなものを書くことができませんでした

int *foo = {1, 2, 3};

int *は配列型ではないためです。

あなたの理解

char *c = "line";

そして

char c[] = "line";

少しずれています。それらは同じではありません。最初の形式は、文字列リテラルのaddressをポインタ値cにコピーします。 2番目の形式は、配列式"line"contentscで指定されたバッファーにコピーします。

2
John Bode