第 13 章 · 复杂指针声明解析

复杂声明的两件武器:右左法则(right-left rule)+ typedef 拆碎。

12.1 运算符优先级与结合性

声明里的三个符号按优先级处理:

  1. 后缀 ()(函数)、[](数组)——优先级最高;
  2. 前缀 *(指针)——其次;
  3. 括号 () 用来改变结合顺序

所以 int *f(int) 是「f 是函数,返回 int*」,而 int (*f)(int) 是「f 是指针,指向函数」。

12.2 右左法则

从一个声明里读出类型,按「从名字出发,先右后左,遇括号拐弯」的顺序:

  1. 标识符开始;
  2. 向右看,遇到 [] 读「…的数组」、() 读「…的函数」;
  3. 向左看,遇到 * 读「…的指针」、类型名读「指向…」;
  4. 遇到括号,跳到括号内继续。

例:int *(*f[5])(char*)

f        → f 是
[5]      → 5 个元素的数组(向右)
*        → 元素是「指针」(向左)
(char*)  → 指向「接收 char* 的函数」(向右)
int *    → 返回「int*」(向左)

结论:f 是「含 5 个指向 (char*)->int* 函数的指针的数组」

12.3 经典对照:指针数组 vs 数组指针 vs 函数指针

声明读法类型
int *p[5]p 是含 5 个 int* 的数组指针数组
int (*p)[5]p 是指向 int[5] 的指针数组指针
int *f(int)f 是返回 int* 的函数函数
int (*f)(int)f 是指向 (int)->int 函数的指针函数指针
int (*f[5])(int)f 是含 5 个函数指针的数组函数指针数组
int (*f(int))(char)f 是函数,返回函数指针返回函数指针的函数

12.4 用 typedef 拆碎复杂声明

任何复杂声明都能用 typedef 一层层拆,让每个中间类型都有名字:

// 原始:int *(*f[5])(char*)
typedef int *(*Handler)(char*);   // Handler = 指向 (char*)->int* 函数的指针
Handler f[5];                     // f 是含 5 个 Handler 的数组 —— 一眼看懂

12.5 经典例子:signal 原型

标准库 signal 的声明是「复杂声明」的教科书案例:

void (*signal(int sig, void (*func)(int)))(int);

用 typedef 拆:

typedef void (*sighandler_t)(int);        // 信号处理函数指针
sighandler_t signal(int sig, sighandler_t func);   // 清晰

读法:signal 是函数,参数是 int 和「函数指针」,返回的也是「函数指针」——传一个处理函数进去,返回旧的处理函数

12.6 与汇编 / 其他语言对照

  • 汇编:所有这些花哨类型,编译后都坍缩成一个地址(或一块地址数组),声明只存在于编译期、用来约束访问方式和生成正确的寻址。
  • Java:没有「指向数组的指针」「函数指针数组」这类类型,接口/数组/泛型各自独立,声明永远简单。
  • C++std::function<int(char*)>std::array<int,5>auto 把复杂类型全藏起来了,auto f = &handler; 根本不用手写。
  • Python:动态类型,没有「声明」,也就没有「复杂声明」这回事。

从标识符出发,先右后左、遇括号拐弯;配合 typedef 分层命名。