C 知识量:16 - 74 - 317
属于自动存储类别的变量具有自动存储期、块作用域且无链接。通常,声明在块或函数头中的任何变量都属于自动存储类别。可以显式的使用关键字auto来表明一个变量属于自动存储类别,例如:
auto int a = 5;
关键字auto是存储类别说明符。由于auto在C与C++中用法存在差异,如果要编写C/C++兼容的程序,就不应使用auto关键字。
只有在自动变量定义所在的块中才能通过变量名访问该变量,程序在进入该变量声明所在的块时变量存在,而在程序退出该块时变量消失,其原占用的内存可作他用。
在嵌套块中声明的变量仅限于该块及其包含的块使用,示例如下:
#include <stdio.h> int main(void) { int a = 5; int b = 0; int i = 0; for (int i = 1; i <= 5; i++) { if (i == 2) { int b = 100; printf("part1:\na=%d\nb=%d\ni=%d\n", a, b, i); } if (i == 5) printf("part2:\na=%d\nb=%d\ni=%d\n", a, b, i); a++; } printf("part3:\na=%d\nb=%d\ni=%d\n", a, b, i); system("pause"); return 0; }
运行结果为:
part1: a=6 b=100 i=2 part2: a=9 b=0 i=5 part3: a=10 b=0 i=0
以上代码中:
for循环外的变量a、b、i是文件作用域变量即全局变量,在整个文件中都有效。
for循环内的变量i和b与for循环外的变量i和b是不同的变量,for循环内的变量i和b是自动变量。
for循环内的变量会暂时覆盖for循环外的同名变量,也就是说块内的变量会暂时覆盖块外的同名变量。
在for循环内,变量i在整个for循环体内有效,而变量b只在if语句块内有效。
if块没有花括号,但是紧跟if之后的语句仍被视为if块的组成部分。
需要注意的是:自动变量不会初始化,除非显式初始化它。也就是说如果不初始化一个自动变量,不用指望它会是0。
寄存器变量存储在CPU寄存器中,与普通变量相比,访问和处理寄存器变量速度更快。
因为寄存器变量存储在寄存器而非内存中,所以无法获取寄存器变量的地址。除此之外,寄存器变量与自动变量都一样,也就是说寄存器变量是块作用域、无链接和自动存储期。
使用存储类别说明符register便可以声明寄存器变量,例如:
register int x;
需要注意的是,即使像上面那样声明了寄存器变量,该变量也不一定会存储到寄存器中,这个声明更像是请求,编译器会根据寄存器数量等情况来决定是否满足请求。即使不能真的存储到寄存器中,也不能对该变量使用地址运算符。
静态变量中静态的意思是指该变量在内存中原地不动,并非值不变。
具有文件作用域的变量具有静态存储期,此外还可以创建具有静态存储期、块作用域的局部变量,即块作用域的静态变量。
块作用域的静态变量与自动变量一样,具有相同的作用域,但是程序离开它们所在的函数后,这些变量依然存在。以下是一个示例:
#include <stdio.h> void box(void); int main(void) { int i; for (int i = 1; i <= 5; i++) { printf("No.%d is :\n", i); box(); } system("pause"); return 0; } void box(void) { int a = 1; static b = 1; //创建块作用域的静态变量 printf("a = %d and b = %d\n", a, b++); }
运行结果为:
No.1 is : a = 1 and b = 1 No.2 is : a = 1 and b = 2 No.3 is : a = 1 and b = 3 No.4 is : a = 1 and b = 4 No.5 is : a = 1 and b = 5
在函数box()中,变量a是自动变量,而变量b是具有块作用域的静态变量,从运行结果可以看出,即使在程序每次循环退出了函数box,仍然保留了变量b的值,下次调用时,在上次结果的基础上再加1。
注意:不能在函数的形参中使用static。
外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别的变量也称为外部变量。
把变量的定义性声明放在所有函数(包括main()函数)外面便创建了外部变量。可以在函数内部使用关键字extern再次声明已定义的外部变量,这样做通常用于表示将会在该函数内使用该外部变量。下面是使用外部变量的示例:
#include <stdio.h> void box(void); int a; //外部定义的变量 double b[10]; //外部定义的数组 extern float c; //如果c被定义在另一个文件中,则必须这样声明。 int main(void) { extern int a; //可选 extern double b[]; //可选,已经声明数组大小,不必再次填写。 ... } void box(void) { ... }
在函数main()中不一定要再次声明外部变量,但是如果要声明,则必须使用extern关键字,否则会另外创建自动变量。
外部变量只能初始化一次,且必须在定义该变量时进行。如果没有初始化,它们会自动初始化为0,这与自动变量完全不同。
外部变量在所有文件和函数中都可见,可以在任何地方直接访问。
内部链接的静态变量具有静态存储期、文件作用域和内部链接。使用时,在所有函数外部,用存储类别说明符static来定义,例如:
#include <stdio.h> void box(void); int a; //外部链接的静态变量 static double b; //内部链接的静态变量 int main(void) { extern int a; //可选 extern double b; //可选 ... } void box(void) { ... }
以上代码中,变量b通过static关键字来定义为内部链接的静态变量。
普通外部变量可以用于同一程序中的任意文件中的函数,而内部链接的静态变量只用用于同一个文件中的函数。
当程序由多个翻译单元组成时,就会体现出内部链接与外部链接的区别。复杂的程序通常由多个单独的源代码文件组成,需要共享一个外部变量时,C通过在一个文件中进行定义式声明,然后在其他文件中进行引用式声明来实现共享。
需要注意的是,如果外部变量定义在一个文件中,其他文件在使用该变量之前,必须先用extern关键字声明它,在此声明之前,是不能直接使用该外部变量的。
C语言有6个关键字作为存储类别说明符,分别是:auto、register、static、extern、_Thread_local和typedef。在绝大多数情况下,不能在声明中使用多个存储类别说明符,唯一的例外是_Thread_local,它可以和static或extern一起使用。
auto说明符。 表明变量是自动存储期,只能用于块作用域的变量声明。
register说明符。 只用于块作用域的变量,其变量归为寄存器存储类别,请求最快速度访问该变量,同时保护该变量的地址不被获取。
static说明符。 其创建的对象具有静态存储期,在载入程序时创建对象,当程序结束时对象消失。如果用于文件作用域声明,作用域受限于该文件;如果用于块作用域声明,作用域受限于该块。
extern说明符。 表明声明的变量定义在别处。如果包含extern的声明具有文件作用域,则引用的变量必须具有外部链接。如果包含extern的声明具有块作用域,则引用的变量可能具有外部链接或内部链接,这取决于该变量的定义式声明。
函数也有存储类别,可以是外部函数(默认),也可以是静态函数或内联函数。外部函数可以被其他文件的函数访问,而静态函数只能用于其定义所在的文件,例如:
void box(void); static int count(int, int);
以上代码中,box()是外部函数,count()是静态函数,其他文件中的函数可以调用box()函数,但是不能调用count()函数。
保护性程序设计的黄金法则是:“按需知道”原则。尽量在函数内部解决该函数的任务,只共享那些需要共享的变量。因此,自动存储类别是最常用的,使用其他类别前需要考虑好是否有必要这样做。
Copyright © 2017-Now pnotes.cn. All Rights Reserved.
编程学习笔记 保留所有权利
MARK:3.0.0.20240214.P35
From 2017.2.6