web-dev-qa-db-ja.com

一時的なchar **引数が不正なのはなぜですか?

関数f(char **p)があり、できる限り簡単な方法で呼び出したいと思いました。

私は試した

char ** p = {"a", "b"};
f(p);

そして得た:

スカラーオブジェクトには初期化子に1つの要素が必要です

に変更しました

char * p[2] = {"a", "b"};
f(p);

そしてそれはうまくいきました[ちょうどchar * p[]]。

次のようなポインタの配列をその場で作成できないのはなぜですか?

f({"a", "b"});
25
CIsForCookies

これは警告を与えます:

char ** p = {"a", "b"};

pは配列ではないためです。

これも違法です:

f({"a", "b"});

式では中括弧自体は許可されていません(ただし、初期化子として使用できます)。

compound literalを使用して、その場で配列を作成できます:

f((char *[]){"a", "b"});

複合リテラルを使用して一時を初期化することもできます。

char ** p = (char *[]){"a", "b"};

最初のステートメントとは異なり、リテラルはchar *[2]型の配列であり、この型の変数を初期化するために使用できるchar **に減衰するため、これは有効です。

複合リテラルの詳細については、 C標準 のセクション6.5.2.5を参照してください。

41
dbush

この宣言char ** p = {"a", "b"};は、Cが{}中括弧の内容を解釈する方法を知らないため、警告をトリガーします。

  • 宣言タイプchar**は、単一のポインターからポインターへのポインターであることを示唆しています
  • {}のコンテンツは、2つのアイテムを指定します。

中括弧の前に型を指定して、匿名配列の初期要素へのポインターでポインターへのポインターを初期化していることをCに伝える必要があります。

char ** p = (char*[]){"a", "b"}

このコンストラクトは「複合リテラル」と呼ばれます。宣言で使用できますが、式としても機能します。

注:そのような配列を関数に渡す場合は、配列のサイズを決定する方法を提供する必要があります。 1つのアプローチは、次のように、配列を「終了」するためにNULLを渡すことです。

void f(char **p) {
    int i = 0;
    while (*p) {
        printf("%d:%s\n", i++, *p++);
    }
}
int main(void) {
    f((char*[]){"a", "b", NULL});
    return 0;
}

デモ

14
dasblinkenlight

C99以降では、正確な目的のために 複合リテラル が追加されました:配列と構造体をその場で作成する
例:

struct foo {int a; char b[2];} structure;
structure = ((struct foo) {1, 'a', 0});
int *y = (int []) {1, 2, 3};
int *z = (int []) {1}; 

Std C99以降とは別に、GCCはこの機能を拡張機能として提供します。
あなたの場合、これは機能します

f((char *[]){"a","b"});

(char *[]){"a","b"}は、charへの2つのポインターの配列をその場で作成する複合リテラルです。

5
haccks