即使是 C 语言新手对于一些简单的声明也是没有阅读障碍的:
int foo[5]; // foo is an array of 5 ints
char *foo; // foo is a pointer to char
double foo(); // foo is a function returning a double
但是,对于一些复杂的声明是难以理解的:
char *(*(**foo[][8])())[]; // huh ?????
本文介绍如何理解这种复杂的声明。
基本类型和派生类型
一个声明包含一个“基本类型”(basic type),0个或多个“派生类型”(derived type)以及变量名。
完整的基本类型列表如下:
| 1 | 2 | 3 |
|---|---|---|
| char | signed char | unsigned char |
| short | unsigned short | |
| int | unisgned int | |
| long | unsigned long | |
| float | doulbe | void |
| struct tag | union tag | enum tag |
| long long | unsigned long long | long double |
派生类型可以扩充基本类型,包括以下 3 种:
- * 指代pointer to
- [] 指代array of
- () 指代function returning…
优先级及解析规则
“array of” [] 和 “function returning…” () 的优先级高于 “pointer to” *。
我们从变量名开始读起,以基本类型结束。中间部分根据“go right when you can, and go left when you must” 的规则填充,即,从变量名开始,遵循优先级规则,尽量向右读取派生类型标记,而不要碰到分组括号,然后向左走到匹配的括号。
示例
下面以一个简单的例子说明:
long **foo[7];
- long **foo[7];
- 从变量名开始,以基本类型结束,得到 “foo is … long”
long**foo[7]- 此例中,变量名有 2 个派生类型:“array of 7” 和“pointer to”,根据规则,尽可能向右读取:
- “foo is array of 7 … long”
long**foo[7]- 现在根据规则向左读取:
- “foo is array of 7 pointer to pointer to long”
根据上例,我们可以得到下面这个更加复杂的声明的含义:
char *(*(**foo [][8])())[];
“foo is array of array 8 pointer to pointer to function returning pointer to array of pointer to char”
抽象声明
C 语言中的抽象声明理解起来就更加困难一些,
int (*(*)())()
此时,我们需要按照以下规则找到变量名应该存在的位置:
- 所有 “pointer to” 的最右边
- 所有 “array of” 的最左边
- 所有 “function returning” 的最左边
- 且在所有的分组括号内
在本例中,我们可以找到在所有 “pointer to” 的最右边和所有 “function returning” 的最左边有 2 个可能的位置(黑色圆点标记):
int (*(* · )·())()
但是只有第一个标记位置符合“在所有分组括号内” 的规则,所以可以得到实际的声明应该如下:
int (*(*foo)())()
“foo is pointer to function returning pointer to function returning int”
限制
并非所有的派生类型的组合都是合法的。
不能创建 array of functions
- 此时,可以使用 “array of pointer to function returning…”
function 不能返回 function
- 此时,可以使用 “function returning pointer to function returning…”
function 不能返回 array
- 此时,可以使用 “function returning pointer to array of…”
在 array 中,只有第一维的维度可以省略,比如
int array[][2][3]void类型是受限的- 具体来说,只有对于派生类型 “pointer to” 和 “function returning”才可使用
void。void *foo; // legal void foo(); // legal void foo; // not legal void foo[]; // not legal
- 具体来说,只有对于派生类型 “pointer to” 和 “function returning”才可使用