「スパイラル」ルールは、次の優先ルールには含まれていません。
T *a[] -- a is an array of pointer to T
T (*a)[] -- a is a pointer to an array of T
T *f() -- f is a function returning a pointer to T
T (*f)() -- f is a pointer to a function returning T
添え字演算子[]
と関数呼び出し()
演算子は、単項よりも優先順位が高い*
ため、*f()
として解析され*(f())
、*a[]
として解析され*(a[])
ます。
したがって、配列へのポインターまたは関数へのポインターが必要な*
場合は、(*a)[]
またはのように、を識別子で明示的にグループ化する必要があります(*f)()
。
次に、それを認識しa
、f
単なる識別子よりも複雑な式にすることができます。in T (*a)[N]
、a
単純な識別子、または(*f())[N]
(a
-> f()
)のような関数呼び出し、または(*p[M])[N]
(a
-> p[M]
)のような配列、または(*(*p[M])())[N]
(a
-> (*p[M])()
)のような関数へのポインターの配列等
間接演算子*
が単項ではなく後置演算子であると、宣言が左から右にいくらか読みやすくなります(void f[]*()*();
間違いなくよりもフローがよくなりますvoid (*(*f[])())()
)が、そうではありません。
そのような毛深い宣言に遭遇したとき、左端の識別子を見つけて、上記の優先規則を適用し、それらを任意の関数パラメーターに再帰的に適用することから始めます。
f -- f
f[] -- is an array
*f[] -- of pointers ([] has higher precedence than *)
(*f[])() -- to functions
*(*f[])() -- returning pointers
(*(*f[])())() -- to functions
void (*(*f[])())(); -- returning void
signal
標準ライブラリの関数は、おそらくこの種の狂気の型標本です:
signal -- signal
signal( ) -- is a function with parameters
signal( sig, ) -- sig
signal(int sig, ) -- which is an int and
signal(int sig, func ) -- func
signal(int sig, *func ) -- which is a pointer
signal(int sig, (*func)(int)) -- to a function taking an int
signal(int sig, void (*func)(int)) -- returning void
*signal(int sig, void (*func)(int)) -- returning a pointer
(*signal(int sig, void (*func)(int)))(int) -- to a function taking an int
void (*signal(int sig, void (*func)(int)))(int); -- and returning void
この時点で、ほとんどの人は「use typedefs」と言っていますが、これは確かにオプションです。
typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);
innerfunc *f[N];
だが...
式でどのように使用 f
しますか?あなたはそれがポインタの配列であることを知っていますが、正しい関数を実行するためにどのように使用しますか?typedefを調べ、正しい構文を理解する必要があります。対照的に、「ネイキッド」バージョンはかなり目立たないですが、式での使用 方法を正確に示しますf
(つまり、(*(*f[i])())();
どちらの関数も引数を取らないと仮定して)。