前言

编程语言的类型模型(type model)一直都是编程语言学习的重点。虽然但是,C语言声明的语法实在是过于奇怪,以至于我们需要花费一些时间认真理解一下,首先思考一下简单的类型模型,考虑下面的C语言声明:

typedef char * string;
string punchline = "I'm a frayed knot";

可以将其分为两部分:变量的类型和变量来理解

被称作
变量的类型 string char *
变量 punchline “I;m a frayed knot”

C语言声明的产生

C语言中的声明器(declarator)

简单地说,声明器就是标识符以及和他组合在一起的任何指针、函数符号、数组下标(本表额外添加了初始化内容)

数量 C语言中的名字 C语言中出现的形式
零个或多个 指针 const volatile;volatile;; const; * volatile const
有且只有一个 直接声明器 标识符;标识符[下标];标识符(参数);(声明器)
零个或一个 初始化内容 = 初始值

声明器是一个很模糊的概念,一个简单的思考方式是C语言的声明器是声明中缺少类型以及对类型限定后剩下的部分,这也可以理解 int *p,*q,r 都是什么类型了(p,q是指针,r是int型变量)

C语言的声明

声明增加了至少一个类型说明符(包括类型说明,存储说明和类型限定符),声明器(一个或多个,逗号分割)分号(语句结束)但也请注意,合法的声明存在限定条件,举例说明:

  • 函数的返回值不能是一个函数,所以 foo()() 是非法的
  • 函数的返回值不能是一个数组,所以 foo()[] 是非法的
  • 数组里面不能有函数,所以 foo 是非法的

你也许很快想到,针对这些问题可以用指针解决,所以

  • 函数的返回值允许是一个函数指针,如 int(* fun()) ()
  • 函数的返回值允许是一个指向数组的指针, 如 int(*foo())[]
  • 数组里面允许有函数指针 如 int (*foo[])()
  • 数组里面允许有其他数组,所以你经常看到 int foo[][]

优先级规则

C语言声明的优先级规则是阅读C语言声明关键的一步:因为C语言的声明并不是简单的从左到右,下面给出这条规则

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

分析一个例子:

char * const * (*next) ();

A  首先,确定变量名next
B.1  将括号看成整体,得出next是一个指向..的指针
B.2  考虑括号外面,后缀 () 得出next是一个函数指针,指向一个返回…的函数
B.3  然后,处理前缀 * ,得出函数返回的是另一个指针
C  将 char * const 解释为指向字符的常量指针
得出结论:next是一个指针,它指向一个函数,函数返回另一个指针,返回的指针是一个类型为char的常量指针

对于const,一个方便的理解方法是const默认修饰左边的,否则作用于右边,因此我们可以看到__char * const__是一个char类型的常量指针.给出几个const的例子

语法 含义
const T* 指向const对象的指针
T const * 指向const对象的指针
T * const 指向对象的cosnt指针
const T* const 指向const对象的const指针
T const * const 指向const对象的const指针