作用域 链接属性 存储时期 存储类型内容请先参考博文
C语言有5中存储类型修饰符: auto
register
static
extern
typedef
下面分别介绍各说明符的用法:
auto
: 表明变量具有自动存储类型
auto说明符只能用在具有代码块作用域的变量的声明中, 但是由于这类变量本身就具有自动存储类型(存储于运行时堆栈中), 所以auto通常只是起显式说明的作用.
register
: 表明变量具有硬件寄存器存储类型
register也只能用在具有代码块作用域的变量的声明中, 表示程序员希望将该变量放在CPU的寄存器中, 从而可以比普通变量更快的访问和操作该变量. 但是无法获得寄存器存储类型的变量的地址, 并且具体是否会将register声明的变量存放于寄存器中由编译器决定.
static
:表明变量具有静态存储类型或则标识符具有内部链接属性
extern
: 表明标识符具有外部链接属性或者该变量在别处定义
想要理解extern的用法首先要理解C语言的声明与定义的区别:
声明: 描述 程序其他地方定义的对象
定义: 为对象 分配内存
如果下列语句出现在所有函数体之外
int i;
定义了int型变量, 为其分配内存, 并执行默认初始化为0int i = 1;
定义了int型变量, 为其分配内存, 并初始化为1extern int i = 1;
定义了int型变量, 为其分配内存, 并初始化为1, 由于标识符i具有文件作用域, 所以默认具有外部链接属性, extern的作用只是显式的说明标识符i具有外部链接属性extern int i;
描述了变量i为int型, 并且在程序的 其他地方 定义(分配内存), 这里的其他地方包括当前编译单元(同一个源文件)或者其他编译单元(其他源文件), z这种用法可以称为 extern声明, 这种用法是的具有外部链接属性的标识符可以在多个源文件中使用, 并且指向同一个实体// main.c
#include
extern int i; // i的extern声明
int main(void)
{
int i = 1; // i在当前编译单元定义
printf("%d", i);
return 0;
}
// main.c
#include
extern int i; // i的extern声明
int main(void)
{
printf("%d", i);
return 0;
}
// define.c
int i = 1; // i在另一个编译单元定义
如果下列语句出现在函数体内
int i;
定义了int型变量, 为其分配内存, 但是不执行任何初始化, i的值随机, 访问该变量非法int i = 1;
定义了int型变量, 为其分配内存, 并初始化为1extern int i = 1;
该语句同样试图定义一个int变量i, 并且将标识符i设置为外部链接属性, 但是由于C语言规定无法在函数内部初始化外部变量, 所以该语句非法. 那么如果我们在函数外部使用语句 int i = 0
定义并初始化, 而将 extern int i = 1
看作赋值操作呢? 由于函数外部的 int i = 0
是i的定义, 函数内部的 extern int i = 1
也是i的定义, 这就造成i的重复定义错误extern int i;
这条语句放在函数体内部时比较复杂, 举几个例子;// program 1
// main.c
#include
int main(void)
{
extern int i;
return 0;
}
program 1编译通过..
// program 2
// main.c
#include
int main(void)
{
extern int i;
printf("%d", i);
return 0;
}
program 2在win7+VS2015平台下报错: error LNK2001: 无法解析的外部符号 _i
// program 3
// main.c
#include
int main(void)
{
printf("%d", i);
return 0;
}
program 3在win7+VS2015平台下报错: error C2065: “i”: 未声明的标识符
// program 4
// main.c
#include
int i = 1;
int main(void)
{
extern int i;
printf("%d", i);
return 0;
}
program 4编译成功, 打印出1.
// program 5
// main.c
#include
int main(void)
{
int i = 1;
extern int i;
return 0;
}
program 5在win7+VS2015下报错 error C2086: “int i”: 重定义
// program 6
// main.c
#include
int main(void)
{
extern int i;
int i = 1;
return 0;
}
program 6在win7+VS2015下报错 error C2086: “int i”: 重定义
// program 7
// main.c
#include
int main(void)
{
extern int i;
printf("%d", i);
return 0;
}
// define.c
int i = 1;
program 7编译通过, 打印出1.
综合上面例子可以猜想:
编译程序时首先判断i是否定义(存在内存), 若是未定义则将 extern int i;
当作对i的extern声明; 若是存在i的定义, 则再判断i的链接性, 若是外部链接则 extern int i;
作为i的extern声明, 若是i为无链接属性, 则 extern int i
作为对i的定义, 并且使用extern强制i为外部链接属性(由于未赋初始值所以不会产生 无法在函数内部初始化外部变量 的报错), 这样就会有两个i的定义, 所以报错 重复定义.
以上关于extern的用法只是个人猜想
typedef
关于存储类说明符有以下几点要说明:
static
既可以用来修改存储类型也可以用来修改链接属性, 具体起什么作用由其所修饰的标识符的作用域决定extern
修饰的标识符具有外部链接属性时才会被当作 extern声明register
修饰的变量实际会不会存放在寄存器中有编译器决定