步骤

可以根据顺时针螺旋原则,对C的复杂声明进行解读

按照以下三个步骤:

  1. 从变量名开始,沿着顺时针方向(变量名处向上走),从第一个类型声明符号开始,当遇到下一个类型声明 符号则使用对应的自然语言描述它:
    [X] or []
    => [^Array X size of… or Array undefined size of…]
    [^Array X size of… or Array undefined size of…]:(数组 大小为 X 的… or 数组 大小为 未知 的…)
    (type1, type2)
    => function passing type1 and type2 returning... (函数 传入 type1 和 type2 返回...)
    *
    => pointer(s) to... (指针 指向...)
  2. 继续沿着顺时针方向使用自然语言描述对应声明符号直到所有符号描述成为止
  3. 圆括号()内的符号要先描述

示例

示例1:简单声明

1
2
3
4
5
6
7
 x   +-------+
| | +-+ |
| | ^ | |
char *str[10];
^ ^ | |
| +---+ |
+-----------+

首先看,变量str是什么
str is an... (变量str 是一个 ...)

  • 从变量名str开始沿着顺时针方向(向上)走,遇到第一个字符是[,即有数组,所以:
    str is an array 10 of... (变量str 是一个数组大小为10的..).
  • 继续沿着顺时针方向,下一个遇到的字符是*,即有指针,所以:
    str is an array 10 of pointers to... (变量str 是一个数组大小为10的指针...)
  • 继续沿着顺时针方向,下一个遇到的字符是本行结束符;,so跳过,下一个是char:
    str is an array 10 of pointers to char (变量str 是一个数组大小为10的指针指向char类型)
  • 现在每个字符都遍历了,因此完结

示例2:函数指针的声明

1
2
3
4
5
6
7
8
9
 x   +--------------------+
| | +---+ |
| | |+-+| |
| | |^ || |
char *(*fp)( int, float *);
^ ^ ^ || |
| | +--+| |
| +-----+ |
+------------------------+

首先看,变量fp是什么?
fp is a... (fp是...)

  • 从fp开始,沿着顺时针方向(向上)遇到的第一个是);因此fp是在圆括号()内的,所以在圆括号内以顺时针螺旋方式继续,下一个是*:
    fp is a pointer to... (fp是一个指针指向...)
  • 继续,出了圆括号范围后看到的是(,也即有函数:
    fp is a pointer to a function passing an int and a pointer to float returning... (fp是一个指针指向函数,该函数传入int类型和指向float的指针类型)
  • 继续,下个是*:
    fp is a pointer to a function passing an int and a pointer to float returning a pointer to... (fp是一个指针指向函数,该函数传入int类型和指向float的指针类型并返回一个指针)
  • 继续,下个是;,虽然到了行结束符,但还没有遍历完全部声明符号,因此继续并最后遇到char:
    fp is a pointer to a function passing an int and a pointer to float returning a pointer to a char ((fp是一个指针指向函数,该函数传入int类型和指向float的指针类型并返回一个指针,该指针指向char类型)

示例3

void (*signal(int, void (*fp)(int)))(int);
根据顺时针螺旋定则,分别以signal和fp为起点,以变量名为起点向上画线,再按照顺时针方向画圆,得出下图(当遇到(时,则跳过()之间的内容包括);遇到)时,则不能跳过()之间除了(的内容)

1
2
3
4
5
6
7
8
9
 x    +-----------------------------+
| | x +---+ |
| | +---+ | |+-+| |
| | ^ | | |^ || |
void (*signal(int, void (*fp)(int)))(int);
^ ^ | ^ ^ || |
| +------+ | +--+| |
| +--------+ |
+----------------------------------+

分析

  1. 先找到全部变量名:signal、fp

  2. 以fp为圆心画的圆:暂称fp圆;以signal为圆心画的圆:暂称signal圆

  3. 因为fp圆被包含在signal圆内,所以signal层级比fp高

  4. 根据顺时针螺旋定则,分别以signal和fp为起点,从12点钟方向开始顺时针转动,得出:
    signal圆所在层级,得出:
    signal(int,…),[设signalAsWhole=signal(int,…)]
    *signalAsWhole,[设signalAWPointer=*signalAsWhole]
    void (signalAWPointer)(int) 也即 void signalAWPointer(int)

    fp圆所在层级,得出:
    fp
    *fp,[设fpAsWhole=*fp]
    void (fpAsWhole)(int) 也即 void fpAsWhole(int)

从最高层开始分析:

  1. void signalAWPointer(int),可知是个函数,该函数传入int,返回void (函数)
  2. signalAWPointer = *signalAsWhole,可知signalAWPointer是个指针 (指针)
  3. signalAsWhole = signal(int,…), 可知是个函数 (函数)
  4. 其中「…」是fp圆 = void fpAsWhole(int), 可知是个函数,该函数传入int,返回void
  5. 设fpAsWhole = *fp 是个指针

所以最后得出:

  • 「fp是指针,指向一个函数,该函数传入int返回void」 即 「fp是指向 传入int返回void 的函数指针」
  • 「signal是个函数,该函数传入int,fp 并返回一个指针,该指针指向一个函数,该函数传入int返回void」 即 「signal是个函数,该函数传入int,fp并返回一个指向 传入int返回void的函数指针」

同样,该原则可以应用到const常量和volatile:

示例4

1
const char *chptr;

chptr是什么?
chptr是一个指向「char常量」的指针 (chptr is a pointer to a char constant.)

示例5

1
char * const chptr;

chptr是什么?
chptr是一个指向「char」的「常量指针」 (chptr is a constant pointer to char.)

示例6

1
volatile char * const chptr;

chptr是什么?
chptr是一个指向指向「char volatile」的「常量指针」 (chptr is a constant pointer to a char volatile.)

欢迎转载但请附上原文链接,谢谢。

参考:

The Clockwise/Spiral Rule 顺时针螺旋原则

搭配食用:

Go’s Declaration Syntax

如有错误,欢迎指出、讨论,大家共同进步 _