0%

C 迷你系列(一)解析复杂声明

引言

这里会讲解一些在 C 中常常遇到的比较复杂的声明,这些声明可能会劝退你学习 C 的热情,但是相信我,读完这一篇之后,你会发现,再复杂的声明也不过如此嘛。

规则

  • A. 声明从它的名字开始读取,然后按照优先级顺序依次读取。
  • B. 优先级从高到低依次是
         B1. 声明中被括号括起来的那部分。
         B2. 后缀操作符:括号 () 表示这是一个函数;方括号 [] 表示这是一个数组。
         B3. 前缀操作符:星号 * 表示指针。
  • C. 如果 const 或 volatile 关键字的后面紧跟类型说明符(如 int,long 等),那么它作用于类型说明符。在其他情况下,const 和 volatile 关键字作用于它左边紧邻的指针星号。
  • D. restrict 关键字用来修饰指针,一般都是出现在 * 的后边。

简单类型说明

有了以上规则,我们可以一步一步开始了。

  • int p
    这是一个普通的整型变量。
  • int *p
    首先从 p 开始,先与 * 结合,说明 p 是一个指针,然后再与 int 结合,说明指针所指向的内容的类型为 int 。
    总结: p 是一个整型数据的指针。
  • int p[3]
    首先从 p 开始,先与 [] 结合,说明 p 是一个数组,然后与 int 结合,说明数组里的元素是整型的。
    总结: p 是一个由整型数据组成的数组。
  • int *p[3]
    首先从 p 开始,先与 [] 结合,因为其优先级比 * 高,所以 p 是一个数组,然后再与 * 结合,说明数组里的元素是指针类型,然后再与 int 结合,说明指针所指向的内容的类型是整型的。
    总结:p 是一个由整型数据的指针所组成的数组。
  • int (*p)[3]
    首先从 p 开始,先与 * 结合,说明 p 是一个指针然后再与 [] 结合 ,说明指针所指向的内容是一个数组,然后再与 int 结合,说明数组里的元素是整型的。
    总结:所以 p 是一个指向由整型数据组成的数组的指针。
  • int **p
    首先从 p 开始,先与 * 结合,说是 p 是一个指针,然后再与 * 结合,指针所指向的元素是指针,然后再与 int 结合,说明该指针所指向的元素是整型数据。由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针。
    总结:二级指针多出现在为一级指针分配内存的场景下。
  • int p(int)
    从 p 开始,先与 () 结合,说明 p 是一个函数,然后进入 () 里分析,说明该函数有一个整型变量的参数,然后再与外面的 int 结合,说明函数的返回值是一个整型数据。
    总结:p 是一个具有一个 int 类型的参数,返回值为整型的函数。
  • int (*p)(int)
    从 p 开始,先与指针结合,说明P 是一个指针,然后与 () 结合,说明指针指向的是一个函数,然后再与 () 里的 int 结合,说明函数有一个 int 型的参数,再与最外层的 int 结合,说明函数的返回类型是整型。
    总结: p 是一个指向 “具有一个整型参数且返回类型为整型的函数” 的指针。

    复杂类型说明

    有了以上基础,我们可以挑战一下稍微复杂一点的声明了。

  • int *(*p(int))[3]
    从 p 开始,先与 () 结合,说明 p 是一个函数,然后进入 () 里面,与 int 结合,说明函数有一个整型变量参数,然后再与外面的 * 结合,说明函数返回的是一个指针,然后到最外面一层,先与 [] 结合,说明返回的指针指向的是一个数组,然后再与 * 结合,说明数组里的元素是指针,然后再与 int 结合,说明指针指向的内容是整型数据。
    总结:所以 p 是一个 “具有一个整型参数且返回一个指向由整型指针变量组成的数组” 的指针变量的函数。
  • int *(* p[3])(int)
    从 p 开始,先与 [] 结合,说明 p 是一个数组,在与 * 集合,说明数组的元素是指针,在与 () 集合,说明指针指向的是函数,进入 () 里面,与 int 集合,说明函数有一个整型参数,在与最外边的 * 结合,说明函数返回一个指针,在与 int 结合,说明返回的指针指向一个整型。
    总结:p 是一个 “具有一个 int 类型参数且返回指向 int 类型指针的函数指针组成” 的数组。
  • void (*signal(int, void(*func)(int)))(int)

    我们最后以这个著名的函数收尾,这应该算是最复杂的声明了吧,如果还有更复杂的,欢迎大家留言。长话短说,让我们开始吧!
    从 signal 开始,先跟 () 结合,说明 signal 是一个函数,进入到 () 里面,该函数有两个参数,一个是 int 类型的,一个是具有 int 类型的参数且无返回值的函数指针(分析:从 func 开始,先与 * 结合,说明 func 是一个指针,与 () 结合,说明这个指针指向一个函数,进入 () 里面,与 int 结合,说明指向的函数有一个 int 类型的参数,最后与 void 结合,说明该函数没有返回值),在与 * 结合,说明函数返回一个指针,在与最右边的 () 集合,说明指针指向一个函数,进入 () 里面,与 int 结合,说明该函数有一个 int 类型的参数,最后与 void 集合,说明该函数没有返回值。

虽然比较复杂,但是我们可以利用 typedef 关键字简化它来对本章进行收尾。

1
2
3
4
5
6
7
8
9
/*
* 表示 func 是一个函数指针,指向一个参数为 int,返回值为 void 的函数。
*/
typedef void (*func)(int)

/*
* 表示 signal 是一个函数,它接受两个参数,一个是 int,一个是 func,返回值为 func
*/
func signal(int, func);

参考