1. 什么是螺旋法则

螺旋法则又称顺时针法则, 就是让我们用来解析 C 中的任意复杂指针定义的.

而且,不仅在 C 中还是在 C++ 中, 都可以用这个法则来手动解析指针变量的含义.

下面我们结合实例说下如何使用该法则.

2. 基础例子

以下图片来源

指向  的指针

  1. 变量 ptr 首先根据螺旋法则从近到远先与指针声明符 * 结合,说明 ptr 是一个指针.
  2. *ptr 作为一个整体, 与 int 结合, 说明 ptr 是一个指向 int 的指针.

指向  的常指针

  1. 变量 ptr 首先根据螺旋法则从近到远先与 const 结合, 继续利用螺旋法则, const ptr 与指针声明符 * 结合,说明 ptr 是一个常指针.

  2. * const ptr 作为一个整体, 与 int 结合, 继续利用螺旋法则, intconst 结合, 说明 ptr 是一个指向 const int 的常指针.

实际上这条声明语句 const intint const 两者调换位置意义依旧相等

但是我们一般会选择前者的写法

理由是如果选择后者的写法可能容易产生误导

1
2
+ const int * const ptr;
- int const * const ptr;

指向  的指针

  1. 变量 ptr 首先根据螺旋法则从近到远先与指针声明符 * 结合,说明 ptr 是一个指针.

  2. *ptr 作为一个整体, 与 int 结合, 说明 ptr 是一个指向 int 的指针.

  3. int *ptr 作为一个整体, 与 const 结合, 说明 ptr 是一个指向 const int 的指针.

与上一条语句类似


指向  的常指针

  1. 变量 ptr 首先根据螺旋法则从近到远先与 const 结合, 继续利用螺旋法则, const ptr 与指针声明符 * 结合,说明 ptr 是一个常指针.

  2. * const ptr 作为一个整体, 与 int 结合, 说明 ptr 是一个指向 int 的常指针.

3. 扩展阅读*

我们试着手工解析下这句声明:

1
void (*signal(int, void (*fp)(int)))(int);

终极声明

建议从里到外分析, 已结合元素的注意不要重复结合

  • fp
  1. 变量 fp 首先根据螺旋法则从近到远先与 ) 结合, 继续利用螺旋法则, fp)* 结合, 说明 fp 是一个指针.

  2. (*fp) 作为一个整体, 与 (int) 结合, 说明 fp 是一个参数表为 (int) 的函数指针.

  3. (*fp)(int) 作为一个整体, 与 void 结合, 说明 fp 是一个参数表为 (int) , 返回值为 void 的函数指针.

  • signal
  1. 变量 signal 首先根据螺旋法则从近到远先与 (int, fp) 结合, 说明 signal 是一个参数表为 (int, fp) 的函数.

  2. signal(int, fp) 作为一个整体, 与 * 结合, 继续利用螺旋法则, 与 (int) 结合, 说明 signal 是一个参数表为 (int, fp) 的函数, 其返回一个参数表为 (int) 的函数指针.

  3. (*signal(int, fp)(int) 作为一个整体, 与 void 结合, 说明 signal 是一个参数表为 (int, fp) 的函数, 其返回一个参数表为 (int) , 返回值为 void 的函数指针.

补充:

对于这样复杂的声明, 可以使用 typedef 简化其表达式.

例如以下两句声明意义是一致的:

1
2
3
4
void (*signal(int, void (*fp)(int)))(int);
// --------------
typedef void (*fp)(int);
fp signal(int, fp);

4. 注意点

如果大家理解了以上的各个例子, 基本上也搞懂了螺旋法则是怎么用来解析声明的

比如 int* p;int *p; 实际上是一样的, 指针声明符 * 修饰的是变量 p , 而不是 int

但是我们定义的时候最好就选择其中的一种方法, 不要总是变来变去.

下面罗列一下它们在声明定义时的区别

1
2
3
4
5
6
7
8
9
10
11
// 每条语句定义多个变量
int* p1, p2; // p1 是指向 int 的指针, p2 是 int

int *p1, *p2; // p1 和 p2 都是指向 int 的指针

// 每条语句只定义一个变量
int *p1; // p1 是指向 int 的指针
int *p2; // p2 是指向 int 的指针

int* p1; // p1 是指向 int 的指针
int* p2; // p2 是指向 int 的指针

我自己的话, 选择变量名与 * 连在一起的写法, 大家可以参考一下, 不过实际上这两种方法没有孰对孰错之分, 大家觉得哪种比较好理解, 不容易产生误导就选哪种.

5. 转换工具

有的很复杂的声明, 手动分析也会觉得很恶心, 我们可以用这个在线工具 C gibberish ↔ English , 可以将我们写的一些声明翻译成英文解释.

不过有一点, 我们试着输入 int * const ptr, 发现会解析错误

1
2
> int * const ptr
syntax error

这个问题我们改下变量名就能解析出来了…

1
2
> int * const p
declare p as const pointer to int

我们把 扩展阅读 里的代码粘贴上去看看结果

1
2
> void (*signal(int, void (*fp)(int)))(int);
syntax error

解析不了…只能分开来解析

1
2
3
4
5
> void (*signal(int))(int);
declare signal as function (int) returning pointer to function (int) returning void

> void (*fp)(int)
declare fp as pointer to function (int) returning void

大概的意思还是看得懂的…

评论