📝笔记:使用Clockwise/Spiral Rule技巧轻松读懂变量/函数声明

本文介绍一种让编程者很方便搞清楚C/C++变量或者函数声明的小技巧Clockwise/Spiral Rule,本文很大部分内容参考自David Anderson于1994年05月06日写的一篇博客

规则

Step 1. 从未知的变量开始,以“螺旋状+顺时针”方向移动,当遇到以下内容时,请用相应的英文语句来代替它们:

1
2
3
4
5
6
7
8
[X] or []
=> Array X size of... or Array undefined size of...

(type1, type2)
=> function passing type1 and type2 returning...

*
=> pointer(s) to...

Step 2. 重复步骤1,直到所有的符号都被遍历; Step 3. 一定要先解决括号里的东西!

例1: 简单声明

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

首先问自己的一个问题:str是什么玩意? > str is an ...

  • 我们从str开始,以螺旋式顺时针方向移动,我们看到的第一个字符是[,所以,这意味着这里有一个数组,所以... > str is an array 10 of...

  • 继续以螺旋式的顺时针方向前进,我们遇到的下一符号是*,所以,这意味着这里有个指针,所以... > str is an array 10 of pointers to...

  • 继续沿螺旋方向前进,我们看到了行的末端(;),所以继续前进,得到了char类型,所以... > str is an array 10 of pointers to char

  • 现在已经访问了每一个符号,搞定!

例2: 指向函数声明的指针

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

首先问自己的一个问题:fp是什么玩意? > fp is a ...

  • 我们从fp开始,以螺旋式顺时针方向移动,我们看到的第一个字符是),因此fp在括号内,所以我们在括号内继续螺旋式移动,看到的下一个字符是*,所以... > fp is a pointer to...

  • 我们离开了小括号,继续以螺旋式顺时针方向前进,我们看到了(;因此,我们有一个函数,所以... > fp is a pointer to a function passing an int and a pointer to float returning...

  • 继续以螺旋方式进行,然后我们看到 * 字符,所以... > fp is a pointer to a function passing an int and a pointer to float returning a pointer to

  • 以螺旋的方式继续,我们看到了;,但还没有访问所有的符号,所以继续,最后到了char类型,所以... > fp is a pointer to a function passing an int and a pointer to float returning a pointer to char

  • 现在已经访问了每一个符号,搞定!

例3: 终极挑战!

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

首先问自己的一个问题:signal是什么玩意? (注意,变量signal在括号内,所以我们必须先搞定它) > signal is a ...

  • 沿着顺时针方向移动,我们看到(,所以我们有... > signal is a function passing an int and a ...

  • 额,函数传参的第二个变量有点复杂;那我们可以对fp使用同样的规则,所以...fp是什么玩意?fp也在小括号内,所以继续我们看到一个*,所以... > fp is a pointer to...

  • 继续沿顺时针方向螺旋上升,我们得到了(,所以知道遇到了函数... > fp is a pointer to a function passing int returning...

  • 现在继续走出函数的小括号,我们看到了void,所以... > fp is a pointer to a function passing int returning nothing(void)

  • 我们已经完成了解析fp,所以让我们继续解析signal,我们现在有... > signal is a function passing an int and a pointer to a function passing int returning nothing(void)

  • 注意到我们仍然在括号内,所以看到的下一个字符是 *,所以... > signal is a function passing an int and a pointer to a function passing int returning nothing returning a pointer to...

  • 我们现在已经解决了括号内的符号,所以继续按顺时针方向,我们看到另一个(,我们知道遇到了函数,所以.... > signal is a function passing an int and a pointer to a function passing int returning nothing returning a pointer to a function passing int returning...

  • 最后我们继续,唯一剩下的就是void,所以signal的最终完整定义是... > signal is a function passing an int and a pointer to a function passing int returning nothing returning a pointer to a function passing int returning nothing(void)

  • 现在已经访问了每一个符号,搞定! d ## 其它

这个规则同样适用于const 以及 volatile,例如:

1
const char *ptr; 

ptr is a pointer to a char constant (ptr 是指向 const char 的指针,即ptr指向的值不可改变,但是指针可以改变)

1
char const *ptr; 

ptr is a pointer to a constant char

所以 const char *ptrchar const * ptr表示一个意思。

1
char * const ptr;

ptr is a constant pointer to char (ptr 是一个const指针指向char,即ptr不可改变,但是指针指向的内容可以改变)

1
volatile char * const ptr;

ptr is a constant pointer to char volatile

参考

  1. David Anderson, Clockwise/Spiral Rule, http://c-faq.com/decl/spiral.anderson.html, 1994.
  2. stackoverflow, What is the difference between const int, const int const, and int const *?