声明在C语言编程中起着核心的作用。通过声明变量和函数,可以在检查程序潜在的错误以及把程序翻译成目标代码两方面为编译器提供至关重要的信息。
声明的语法
一般地,声明具在下列形成:
[声明] 声明说明符 声明符;
声明说明符描述声明变量或函数的性质。声明符给出了它们的名字,并且可以提供关于其性质的额外信息。声明说明符分为以下4大类:
- 存储类型。存储类型一共有4种:auto、static、extern和register。在声明中最多可以出现一种存储类型。如果存储类型存在,则必须把它放置在最前面。
- 类型限定符。有3种:const、volatile和restrict. 声明可以包含零个或多个限定符。
- 类型说明符。关键字void, char, short, int,long, float,double, signed 和unsigned都是类型说明符。这些单词可以组合使用。类型说明符也包含结构、联合和枚举的说明(如struct point{ int x, y}、struct{ int x, y})。用typedef创建的类型的类型名也是类型说明符。
- 函数说明符。它只用于函数声明。这一类说明符只有一个 ,即inline.
类型限定符和类型说明符必须跟随在存储类型的后边,但是两者的顺序没有限定。
声明符包含着标识符(变量的名字)、后边跟[ ]的标识符(数组名)、前放置*的标识符(指针)和后边跟随()的标识符(函数名)。声明符之间用逗号分隔。表示变量的声明后边可以跟随初始化式。
存储类型
变量的性质
c程序中的每个变量都具有以下3个性质
- 存储期限:变量的存储期限决定了为变量预留和内存被释放的时间。具有自动存储期限的变量在所属块被执行时货得内存单元,并在块终止时释放内存单元,从而会导致量失去值。具有静态存储期限的变量在程序运行期间占有同一个的存储单元,也就允许变量无限期地保留它的值。
- 作用域:变量的作用域是指可以引用变量的那部分程序文本。
- 链接:变量的链接确定了程序的不同部分可以共享此变量的范围。
变量的默认存储期限、作用域和链接都依赖于变量声明的位置。
int i ; // 静态存储期限, 文件作用域, 外部链接
void f(void) {
int j ; // 自动存储期限, 块作用域, 无链接
}
atuo存储类型
atuo存储类型只对属于块的变量有效。具有自动存储期限, 块作用域,并且无链接。
static存储类型
static存储类型可以用于全部变量,而无需考虑变量声明的位置。但是,作用于块外部声明的变量和块内部声明的变量时会有不同的效果。当块外部时,static说明变量具有内部链接。当块内部时,static把变量的存储期限从自动的变成了静态的。
static int i ; // 静态存储期限, 文件作用域, 内部链接
void f(void) {
static int j ; // 静态存储期限, 块作用域, 无链接
}
块内声明的static变量在程序执行期间驻留在同一存储单元内。和每次程序离开所在块就会失去值的自动变量不同,static变量会无限期地保留值。
extern存储类型
extern存储类型使几个源文件可以共享同一个变量。extern int i不会导致编译器为变量i分配存储单元。只是提示编译器需要访问定义在别处的变量。extern声明中的变量始终具有静态存储期限。变量的作用域声明的位置。
register存储类型
声明变量具有register存储类型就要求编译器把变量存储在寄存器中,而不是像其他变量一样保留在内存中。它只对声明在块内的变量有效,同时与atuo变量一样的存储期限、作用域和链接。但对register变量使用取地址运算符&是非法的。
小结
int a ;
extern int b;
static int c;
void f( int d, register int e){
atuo int g ;
int h ;
stetic int i;
extern int j;
register int k;
}
下表说明上述例子中每个变量和形成参数的性质。
名字 | 存储期限 | 作用域 | 链接 |
---|---|---|---|
a | 静态 | 文件 | 外部 |
b | 静态 | 文件 | - |
c | 静态 | 文件 | 内部 |
d | 自动 | 块 | 无 |
e | 自动 | 块 | 无 |
f | 自动 | 块 | 无 |
g | 自动 | 块 | 无 |
h | 自动 | 块 | 无 |
i | 静态 | 块 | 无 |
j | 静态 | 块 | - |
k | 自动 | 块 | 无 |
类型限定符
C 语言中一共有两种类型限定符:const和 volatile。它只用于指针。const用来声明一些类似于变量的对象,但这些变量是“只读”的。程序可以访问const型对象的值,但是无法改变它的值。我们来比较const与#define之间明显的差异。
- 可以用#define指令为数值、字符或字符串常量创建名字。const可以于产生任何类型的只读对象,包括数组、指针、结构和联合。
- const对象遵守与变量相同的作用域规则,而用#define创建的常量不受于这些规则的。
- 不同于宏,const对象不可以用于常量表达式。
- 对const对象应用取地址运算(&)合法的。
声明符
声明符包含着标识符(声明的变量或函数的名字)、标识符的前边可能有符号*,后边可能有[ ] 和()。通过把*、[ ] 和()组合在一起,可以创建复杂声明符。
- 用* 开头的声明符表示指针:int * p;
- 用[ ] 结尾的声明符表示数组:int a[10];如果数组是形式参数,或数组有初始化式,再或数组的存储类型为extern,那么方括号内可以为空:extern int a[ ];
- 用( ) 结尾的声明符表示函数:int abs( int i); int find_largerst( int a[ ], int).
解释复杂声明
到目前为止,我们可以通过下面两条简单的规则可以用来理解任何声明。
1)始终从内往外读声明符
2)在作选择时,始终使[ ]和( )优于*。如: void *pf (int) 理解为 void (*pf) (int);