C语言复杂声明的解析


Reading C type declarations (unixwiz.net)

即使是 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

文章作者: Shichao Zhang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Shichao Zhang !
  目录