何を達成しようとしているのかは完全には明らかではないため、複数の可能性について説明します。
まず、C で複雑な宣言を読み書きする方法を復習します。
単項よりも優先順位が高いこと()
を覚えておいてください。したがって、ポインタの配列であり、一方は配列へのポインタです。同様に、はポインタを返す関数であり、 は関数へのポインタです。 []
*
*a[]
(*a)[]
*f()
(*f)()
毛むくじゃらの宣言を読み込もうとしているときは、上記のルールを思い出しながら、一番左の識別子から始めて解決してください。したがって、
int (*(x)())[2];
として読む
x -- x
(x) -- x
(x)() -- is a function
*(x)() -- returning a pointer
(*(x)())[2] -- to a 2-element array
int (*(x)())[2] -- of int
この場合、すぐ周囲のかっこx
は冗長であり、削除できます: int (*x())[2];
.
このような関数を作成して使用する方法は次のとおりです。
int (*x())[2]
{
int (*arr)[2] = malloc(sizeof *arr); // alternately, you could simply write
return arr; // return malloc(sizeof (int [2]));
} // type of *arr == int [2]
int main(void)
{
int (*p)[2] = NULL; // alternately, you could write
... // int (*p)[2] = x();
p = x();
...
free(p);
}
arr
、、p
およびx()
すべての宣言が同じに見えることに注意してください。それらはすべてパターンに適合しますint (*_)[2];
。 これは重要です。あるものを として宣言しT (*p)[N]
、別のものをとして宣言するとT **q
、それらの型は異なり、互換性がない可能性があります。の配列へのポインターT
は、へのポインターへのポインターとは異なる型ですT
。
を返す関数へのポインターの配列を作成することが目標の場合、int
型は のようint (*f[2])();
になります。
f -- f
f[2] -- is a 2-element array
*f[2] -- of pointers
(*f[2])() -- to functions
int (*f[2])(); -- returning int
それは次のようになります。
int foo() {...}
int bar() {...}
int main(void)
{
int (*f[2])() = {foo, bar};
...
}
を返す関数が必要な場合はf
、少しトリッキーです。C 関数は配列型を返すことができません。それらは配列へのポインターのみを返すことができるため、関数宣言は次のように構築されます
g -- g
g() -- is a function
*g() -- returning a pointer
(*g())[2] -- to a 2-element array
*(*g())[2] -- of pointers
(*(*g())[2])() -- to functions
int (*(*g())[2])() -- returning int
そして、そのようなビースティは次のように使用されます。
int foo() {...}
int bar() {...}
int (*(*g())[2])()
{
int (*(*f)[2])() = malloc(sizeof *f);
(*f)[0] = foo; // the type of the *expressions* foo and bar
(*f)[1] = bar; // is `int (*)()`, or pointer to function
return f; // returning int
}
int main(void)
{
int (*(*p)[2])();
int x, y;
...
p = g();
x = (*(*p)[0])();
y = (*(*p)[1])();
...
free(p);
...
}
置換メソッドを使用して、外部から内部への毛むくじゃらの宣言を構築することもできることに注意してください。そう、
int x(); -- x is a function returning int
int (*p)(); -- replace x with (*p) to get a pointer to a function
returning int
int (*a[2])(); -- replace p with a[2] to get an array of pointers
to functions returning int
int (*(*q)[2])(); -- replace a with (*q) to get a pointer to an array
of pointers to functions returning int
int (*(*g())[2])(); -- replace q with g() to get a function returning
a pointer to an array of pointers to functions
returning int.
同じ結果、異なるパス。私は最初の方法を好みますが、どちらでも機能するはずです。
多くの人typedef
が、読みやすくするために使用することを推奨しています。
typedef int ifunc(); // ifunc is a synonym for "function returning int"
typedef ifunc *pifunc; // pifunc is a synonym for "pointer to function
// returning int
typedef pifunc farr[2]; // farr is a synonym for "2-element array of
// pointer to function returning int
typedef farr *pfarr; // pfarr is a synonym for "pointer to 2-element
// array of pointer to function returning int
pfarr g()
{
pfarr f = malloc(sizeof *f);
(*f)[0] = foo;
(*f)[1] = bar;
return f;
}
int main(void)
{
pfarr p = g();
int x, y;
x = (*(*p)[0])();
y = (*(*p)[1])();
...
}
はい、宣言は読みやすくなっていますが、 の宣言p
と式の間に関連はありません(*(*p)[1])()
。なぜtypedefs
その式がそのように書かれているのかを理解するには、すべての をくまなく調べて、それぞれの のメンタル マップを作成する必要があります。typedef
はい、次のような宣言int (*(*g())[2])()
は、目を曇らせるように設計されており、その背後にあるすべてを隠して、typedef
IMO の状況を悪化させます。