跳转至

第十八章 声明


什么是声明

一个变量或者函数应该首先被声明,才会被使用。因为声明会告诉编译器这个变量或者函数的信息,然后编译器就可以检查其存储空间和作用域,以及使用时的语法是否正确。

声明的语法

声明的格式(声明式)是:

声明说明符 声明符

声明说明符描述了变量或者函数的性质,声明符代表变量名或者函数名,并且可以指明它的额外信息(如是一个数组or指针or函数)。

声明说明符分为以下3类:

  1. 存储类型。四种:auto(块内默认存储类型,无需显示声明),static,extern和register(已经被现代编译器优化,一般不需要声明)。

  2. 类型限定符,有const和volatile 。

  3. 类型说明符,诸如int,long,unsighed,或者自定义数据类型等,对于函数,类型说明符就是返回类型。

声明符就是一个标识符,然后可以用星号(代表指针),方括号(代表数组),圆括号(代表函数)修饰。

可以看到,声明没有赋值的内容。

存储类型

变量的性质

C程序中,变量都有三种性质:

  1. 存储期限,决定了变量的生存周期。具有自动存储期限的变量在第一次执行时获得内存单元,出块时释放内存单元;具有静态存储期限的变量在程序的生命周期内都拥有内存单元。

  2. 作用域,拥有块作用域的变量只能在块内被使用;拥有文件作用域的变量从声明变量开始到文件结束的范围都可以使用。

  3. 链接,拥有外部链接的变量(内存单元)可以被程序的其它文件访问;拥有内部链接的变量只可以在文件内访问;无链接的变量只能在一个函数内访问。

上述三种性质取决于变量的声明位置以及变量的存储类型,比如不指明存储类型的话:

  • 在块内声明的变量,具有自动存储期限,块作用域,无链接。

  • 在程序的最外层声明的变量,具有静态存储期限,文件作用域,外部链接。

作用域

决定一个变量的作用域的,仅在于它的声明位置:

  1. 在块内声明的,具有块作用域。

  2. 在文件最外层声明的,具有文件作用域。

作用域是编译级别的(非链接)语法,编译器根据变量的作用域检查其使用的位置是否正确。

static存储类型

当static作用于一个块内声明的变量时,将改变它的存储期限为静态存储期限。

当static作用于一个最外层声明的变量时,将改变它的链接为内部链接,使这个变量的内存单元不能被其它文件所访问。

extern存储类型

用extern来声明一个变量,不会让编译器为它分配内存单元,它只是告诉编译器,这个变量是在别的地方定义的。因此:当extern作用于一个变量时,这个变量必须拥有静态存储期限且一般有外部链接。一般这个变量都是一个在最外层定义的变量。

函数存储类型

默认情况下,函数存储类型都是extern的,代表此函数的链接是外部链接,可以被其它文件访问。

如果给函数加上static声明,那么这个函数的链接就会被修改成内部链接,只能在文件内访问。如果一个函数不需要被多个模块共享,那么就应该声明成static的。

const限定符

声明一个编译器维度上的常量,但却不能看做一个常量表达式,从而不能定义一个数组的边界(应该用#define)。

const主要用于保护一个指针指向的对象不被修改,也就是定义一个常量指针,使其指向的空间不允许被修改。

声明符

声明符是由标识符和三个特殊符号组成的,这三个特殊符号是:

  • 放在标识符前面的*

  • 放在标示符后面的()或者[]

解释复杂声明

有时候声明符包含了多个特殊符号,这就要通过两条规则进行解释才能理解。它们是:

  • 始终从内向外读声明符,也就是先定位标识符,然后往外读

  • []()始终优先于*,但()可以强制修改优先级

初始化式

在声明一个变量时,可以给它=一个初始值,这叫初始化式,而不是赋值。

需要注意的几点:

  • 静态变量只能用常量表达式初始化,如果没有初始化,那么就是0

  • 拥有自动存储期限的变量如果没有初始化,其值就是未定义的(包括数组)

  • 数组的初始化(大括号闭合)必须用常量表达式初始化每一个元素