33

複合リテラルを変数に割り当てようとしていますが、機能しないようです。以下を参照してください。

  int *p[] = (int *[]) {{1,2,3},{4,5,6}};

gccでエラーが発生しました。

しかし、私がこれだけを書くと:

  int p[] = (int []) {1,2,3,4,5,6};

それなら大丈夫です

しかし、私が望むものではありません。

エラーが発生する理由がわかりません。配列のように初期化するか、charの配列のポインターで使用すると、問題ありません。以下を参照してください。

  int *p[] = (int *[]) {{1,2,3},{4,5,6}}; //I got a error
  int p[][3] = {{1,2,3},{4,5,6}}; //it's okay
  char *p[] = (char *[]) {"one", "two"...}; // it's okay!

注最初の形式でエラーが発生した理由がわかりません。できません。または、複合リテラルである必要があるため、2番目の形式のように記述したくありません。コンパイラにとっての配列の大きさを言います。2番目のようなものが必要ですが、int値用です。

前もって感謝します。

4

5 に答える 5

31

まず、キャストはすべての例で冗長であり、削除できます。次に、多次元配列を初期化するための構文を使用しています。これには、メモリのシーケンシャルブロックを割り当てるために2番目の次元を定義する必要があります。代わりに、以下の2つのアプローチのいずれかを試してください。

  • 多次元配列:

    int p[][3] = {{1,2,3},{4,5,6}};
    
  • 1次元配列へのポインターの配列:

    int p1[] = {1,2,3};
    int p2[] = {4,5,6};
    int *p[] = {p1,p2};
    

後者の方法には、さまざまな長さのサブアレイを使用できるという利点があります。一方、前者の方法では、メモリが連続して配置されます。

使用しないことを強くお勧めするもう1つのアプローチは、整数を文字列リテラルにエンコードすることです。これは移植性のないハックです。また、文字列リテラルのデータは一定であると想定されています。アレイは可変である必要がありますか?

int *p[] = (int *[]) {
    "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00",
    "\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
};

この例は32ビットのリトルエンディアンマシンで機能する可能性がありますが、iPadから入力しているため、現時点では確認できません。繰り返しますが、それは使用しないでください。育てても汚いです。

あなたが発見したキャスト方法は、ポインターへのポインターでも機能するようです。多次元配列のようにインデックスを付けることもできます。

int **p = (int *[]) { (int[]) {1,2,3}, (int[]) {4,5,6} };
于 2011-03-31T06:31:22.203 に答える
13

まず、 「配列はポインタではない」ことを理解してください。

int p[] = (int []) {1,2,3,4,5,6};

上記の場合pは整数の配列です。要素{1,2,3,4,5,6}をにコピーしますp。ここでは型キャストは必要ありません。整数配列であるrvalueと型の両方が一致するため、エラーは発生しません。lvalue

int *p[] = (int *[]) {{1,2,3},{4,5,6}};

「最初のものでエラーが発生した理由がわかりません。..」

上記の場合、p整数ポインターの配列。ただし、これ{{1,2,3},{4,5,6}}は2次元配列(つまり、[] [])であり、ポインターの配列に型キャストすることはできません。-として初期化する必要があります

int p[][3] = { {1,2,3},{4,5,6} };
  // ^^ First index of array is optional because with each column having 3 elements
  // it is obvious that array has two rows which compiler can figure out.

しかし、なぜこのステートメントはコンパイルされたのですか?

char *p[] = {"one", "two"...};

文字列リテラルは整数リテラルとは異なります。この場合も、pは文字ポインタの配列です。実際に言うと"one"配列にコピーするか、読み取り専用と見なしてその場所を指すことができます

char cpy[] = "one" ;
cpy[0] = 't' ;  // Not a problem

char *readOnly = "one" ;
readOnly[0] = 't' ;  // Error because of copy of it is not made but pointing
                     // to a read only location.

文字列リテラルでは、上記のいずれの場合も可能です。だから、それがステートメントがコンパイルされた理由です。だが -

char *p[] = {"one", "two"...}; // All the string literals are stored in 
                               // read only locations and at each of the array index 
                               // stores the starting index of each string literal.

コンパイラにとっての配列の大きさは言いたくありません。

を使用してメモリを動的に割り当てることmallocが解決策です。

それが役に立てば幸い !

于 2011-03-31T06:54:37.480 に答える
5

誰も言っていないので:2D配列へのポインタが必要な場合は、(おそらく)次のようなことを行うことができます。

int (*p)[][3] = &(int[][3]) {{1,2,3},{4,5,6}};

編集:または、を介して最初の要素へのポインタを持つことができます

int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};

例が機能しない理由{{1,2,3},{4,5,6}}は、がタイプの有効な初期化子ではないint*[]ためです({1,2,3}はの有効な初期化子ではないためint*)。それはではないことに注意してくださいint[2][3]—それは単に無効な式です。

文字列に対して機能する理由は、と"one"の有効な初期化子であるためです(一部のN> 3の場合)。としては、コンパイラが定数を失ったときにあまり文句を言わないことを除いて、ほぼ同等です。char[]char[N](const char[]){'o','n','e','\0'}

そして、はい、初期化子と式の間には大きな違いがあります。私はchar s[] = (char[]){3,2,1,0};C99(そしておそらくC ++ pre-0x)のコンパイルエラーであると確信しています。他にもたくさんのことがありますがT foo = ...;、それらは似ているように見えますが、割り当てではなく変数の初期化です。(代入演算子が呼び出されないため、C ++では特に異なります。)

そして、ポインタとの混同の理由:

  • タイプT[]は、必要に応じて暗黙的にタイプT*(最初の要素へのポインター)に変換されます。
  • T arg1[]関数の引数リストでは、実際にはを意味しT * arg1ます。さまざまな理由で関数に配列を渡すことはできません。それは不可能。試してみると、実際には配列へのポインタを渡しています。(ただし、固定サイズの配列を含む構造体を関数に渡すことはできます。)
  • それらは両方とも、同じ(私が思うに)セマンティクスで逆参照および添え字を付けることができます。

編集:観察者は、私の最初の例が構文的にほぼ同等であることに気付くかもしれませんがint * p = &1;、これは無効です。これはC99で機能します。これは、関数内の複合リテラルが「囲んでいるブロックに関連付けられた自動保存期間を持っている」ためです(ISO / IEC 9899:TC3)。

于 2011-04-12T00:52:29.227 に答える
1

使用しているのはintポインターの配列です。配列へのポインタを使用する必要があります:

int (*p)[] = (int *) {{1,2,3}, {4,5,6}}

詳細については、この回答をご覧ください。

于 2011-04-08T07:15:18.443 に答える
1

ポインタと配列を混同しているようです。それらは同じものではありません!配列はリスト自体ですが、ポインタは単なるアドレスです。次に、ポインタ演算を使用すると、ポインタが配列であるかのように見せかけることができます。配列の名前が最初の要素へのポインタであるという事実により、すべてが混乱します。;)

int *p[] = (int *[]) {{1,2,3},{4,5,6}};      //I got a error

ここで、pはポインタの配列であるため、アドレスが1、2、3の要素を最初の配列に割り当て、4、5、6を2番目の配列に割り当てようとしています。これらのメモリ位置にアクセスできないため、セグメンテーション違反が発生します。

int p[][3] = {{1,2,3},{4,5,6}};              //it's okay

これは配列の配列であるため、これは問題ありません。したがって、今回は1、2、3、4、5、および6はアドレスではなく、要素自体です。

char *p[] = (char *[]) {"one", "two"...};    // it's okay!

文字列リテラル( "one"、 "two"、...)は実際には文字列ではなく、それらの文字列へのポインタであるため、これは問題ありません。したがって、p[1]に文字列リテラル"one"のアドレスを割り当てます。 。
ところで、これはを行うのと同じchar abc[]; abc = "abc";です。char *def; def = "def";問題を解決している間は配列にポインタを割り当てることができないため、これはコンパイルされません。

于 2011-04-08T12:25:35.397 に答える