auto、register、static、extern等存储类修饰符的区别
一、标识符的链接(linkage)
(1)外部链接
表示在整个程序中(多个程序文件)是相同的函数或对象。常见的有,在函数体外声明的extern变量。
(2)内部链接
表示只在当前程序文件中是相同的函数或对象。其它程序文件不能对其进行访问。常见的有,在函数体外声明的static变量。
(3)无链接
一般声明在函数内部的auto、register变量、还有函数的参数,都是无链接。它的作用域是函数内部。
二、对象的生存周期(lifetime)
(1)静态生存周期
具有静态生存周期的所有对象,都是在程序开始执行之前就被事先创建和初始化。它们的寿命覆盖整个程序的执行过程。如在函数内定义了一个static变量,那第一次调用该函数后,该变量的值将会被保留,当第二次被调用时,该变量的值还是第一次调用结束时的值。
(2)自动生存周期
自动生存周期的对象的寿命由“对象定义所处在的大括号{}”决定。每次程序执行流进入一个语句块,此语句块自动生存周期的对象就会被创建一个新实例,同时被初始化。
三、存储类修饰符
(1)auto
auto修饰符只能用在函数内的对象声明。声明中有auto修饰符的对象具有自动生存周期。
在ANSI C中,函数内的对象声明在默认情况下有自动生存周期,所以在函数内声明时auto可省略。
(2)register
当声明对象有自动生存周期时,可以使用register修饰符。因此,register也只能用在函数内的声明中。
此关键字告诉编译器:此对象的存取应该尽量快,最好存储在CPU的寄存器中。然而,编译器不见得会这么做。
另外要注意的是,当一个对象声明为register,就不可使用地址运算符&了,因为它有可能被放到寄存器中。
(3)static
函数标识符如果被声明为static,就具有静态生命周期。
如果是定义在函数外,那么该对象具有内部链接,其它程序文件不能对其访问。
如果是定义在函数内,那么该对象具有无链接,函数外不能对其访问。
注意:static变量初始化时,只能用常量。
(4)extern
如果声明在函数外,那么该对象具有外部链接,能够在其它程序文件使用。但要注意它有可能会被函数内定义的重名的变量所隐藏起来。
如果声明在函数内,该对象具有何种链接取决于当前程序文件中定义在函数外的相同名字的对象。如果在函数外也定义了一下相同名字的static对象,则该函数内的对象具有无链接,否则具有外部链接。
extern的对象都具有静态生命周期。
使用extern时,注意不能重复定义,否则编译报错,如:
程序文件一:
extern int a = 10; //编译警告,extern的变量最好不要初始化
程序文件二:
extern int a = 20; //重复定义,应改为extern int a;
一般最好这样,如果需要初始化,可把extern修饰符去掉(但也不要重复定义),另外如果其它程序文件也需要用到该变量,可用extern来声明该变量。这样会比较清晰。
(5)缺省修饰符
函数内,与auto相同;
函数外,与extern相同;
| | linkage | lifetime |
auto | 函数内 | no linkage | 自动 |
函数外 | 语法错 | 语法错 | |
register | 函数内 | no linkage | 自动 |
函数外 | 语法错 | 语法错 | |
缺省 | 函数内 | no linkage | 自动 |
函数外 | external linkage | 静态 | |
static | 函数内 | no linkage | 静态 |
函数外 | internal linkage | 静态 | |
extern | 函数内 | 与它在函数外所声明的一致 | 静态 |
函数外 | external linkage | 静态 |
int func1(void);
int a = 10;
extern int b = 1;
static int c;
static int e;
static void func2(int d){
}