C/C++存储类型平时似乎用到不多,但是面试中会被问及,从《高质量程序设计指南》中学习之,记录在此。
标准C语言为变量、常量和函数等定义了四种存储类型:extern、auto、static、register。一个程序元素的存储类型与它的作用域、生存期限及连接类型有微妙关系,但是一个具有作用域和连接类型的标识符不一定就具有存储类型。
上述四种存储类型可分为两种生存期限:永久的(整个程序期间存在)和临时的(暂时保存在堆栈和寄存器中)。
extern和static用来表示永久生存期限的的变量和函数,而auto和register则标识临时生存期限的变量(只有变量才有临时生存期限,函数、常量都是永久的)。一个变量和函数只能有一种存储类型,当然也只有一种生存期限。
默认情况下,全局变量和全局函数的存储类型是extern,能够被定义在他们之后的同一个编译单元内的函数所调用。如果变量和函数前被显示的加上extern申明,则其他编译单元也能调用他们。
显示地申明为static的全局变量和全局函数具有static存储类型,只能被同一个编译单元内的函数调用。
局部变量默认为auto存储类型,除非用static或register定义,但是其作用域都是局部作用域,连接类型是内连接。register和auto只能用于申明局部变量和局部常量。
全局常量默认存储类型为static,除非在定义了它的编译单元之外的其它编译单元显示的用extern申明,否则不能被访问。
extern说明示例,
全局变量和全局函数的默认存储类型是extern,对于我们通常的include,相当于一个extern语句复制到要使用的地方,因此不同编译单元间的全局变量可以相互使用。
举例:
文件A.h
#include "C.h" //因为全局变量、函数的默认存储类型是extern ,其在C.h中申明,include的效果相当于拷贝该句extern到此处
#include <iostream>
文件A.cpp
extern int i; //使用extern使得A编译单元可以找到B编译单元里的全局变量以及函数
extern float fun_extern(
void);
int main()
{
fun_extern();
fun_include();
}
文件B.cpp
#include <iostream>
using namespace std;
int i = 1;
float fun_extern(void)
{
cout << "fun_extern()" << endl;
return 2;
}
文件C.h
#include <iostream>
float fun_include(
void
);
文件C.cpp
#include "C.h"
using namespace std;
float fun_include(void)
{
cout << "fun_include()" << endl;
return 3;
}
虽然extern可以起到include的作用,不过通常的编码规范中都,都规定禁止在.c中通过extern方式使用外部函数接口、变量,而应在定义该接口的对应.h文件中申明该接口(是否使用extern均可,因为默认的存储类型为extern),之后在调用文件中通过include头文件的方式引用该接口。这样可以在对接口修改时申明和定义不一致的问题。
连接类型
连接类型分为外连接、内连接和无连接,连接类型表明一个表示符的可见性,因此易与作用域混淆。
如果一个标识符能够在其它编译单元或者在定义它的编译单元中的其他范围内被调用,则他是外连接的。通常的全局变量,函数都是外连接的。
如果一个标识符能在定义它的编译但与中的其它范围内被调用,但是不能再其它编译单元中被调用,则它是内连接的。
static void f2(){...} //f2加入了static标识符,只能在其编译单元调用,为内连接。
一个仅能在申明它的范围内被调用的名字是无连接的,所以局部变量是无连接的。