C语言序列3——全局变量和局部变量,标识符作用域、变量的生存期

#include <stdio.h>

const int A = 10;
int a = 20;
static int b = 30;
int c;

int main(void)
{
	static int a = 40;
	char b[] = "Hello world";
	register int c = 50;

	printf("Hello world %d\n", c);

	return 0;
}

作用域(Scope)这个概念适用于所有标识符,而不仅仅是变量,C语言的作用域分为以下几类:

函数作用域(Function Scope),标识符在整个函数中都有效。只有语句标号属于函数作用域。标号在函数中不需要先声明后使用,在前面用一个goto语句也可以跳转到后面的某个标号,但仅限于同一个函数之中。

文件作用域(File Scope),标识符从它声明的位置开始直到这个程序文件的末尾都有效。例如上例中main函数外面的Aabc,还有main也算,printf其实是在stdio.h中声明的,被包含到这个程序文件中了,所以也算文件作用域的。

块作用域(Block Scope),标识符位于一对{}括号中(函数体或语句块),从它声明的位置开始到右}括号之间有效。例如上例中main函数里的abc。此外,函数定义中的形参也算块作用域的,从声明的位置开始到函数末尾之间有效。

标识符的链接属性(Linkage)

外部链接(External Linkage),如果最终的可执行文件由多个程序文件链接而成,一个标识符在任意程序文件中即使声明多次也都代表同一个变量或函数,则这个标识符具有External Linkage。具有External Linkage的标识符编译后在符号表中是GLOBAL的符号。例如上例中main函数外面的acmainprintf也算。

内部链接(Internal Linkage),内部链接常指一个程序文件中全局变量,可以被程序文件内各个子程序访问,这在编译过程中处理,和link阶段不发生关系。如果变量前加了static,那么它永远不会被外部程序访问,它不会被编译程序写入目标代码的链接区。例如上例中main函数外面的b。如果有另一个foo.c程序和main.c链接在一起,在foo.c中也声明一个static int b;,则那个b和这个b不代表同一个变量。具有Internal Linkage的标识符编译后在符号表中是LOCAL的符号,但main函数里面那个a不能算Internal Linkage的,因为即使在同一个程序文件中,在不同的函数中声明多次,也不代表同一个变量。

无链接(No Linkage)。除以上情况之外的标识符都属于No Linkage的,例如函数的局部变量,以及不表示变量和函数的其它标识符。

存储类修饰符(Storage Class Specifier)有以下几种关键字,可以修饰变量或函数声明:

static,用它修饰的变量的存储空间是静态分配的,用它修饰的文件作用域的变量或函数具有Internal Linkage。

register,编译器对于用register修饰的变量会尽可能分配一个专门的寄存器来存储,但如果实在分配不开寄存器,编译器就把它当auto变量处理了,register不能修饰文件作用域的变量。现在一般编译器的优化都做得很好了,它自己会想办法有效地利用CPU的寄存器,所以现在register关键字也用得比较少了

extern,上面讲过,链接属性是根据一个标识符多次声明时是不是代表同一个变量或函数来分类的,extern关键字就用于多次声明同一个标识符,下一章再详细介绍它的用法

typedef,在“sizeof运算符与typedef类型声明”一节讲过这个关键字,它并不是用来修饰变量的,而是定义一个类型名。在那一节也讲过,看typedef声明怎么看呢,首先去掉typedef把它看成变量声明,看这个变量是什么类型的,那么typedef就定义了一个什么类型,也就是说,typedef在语法结构中出现的位置和是面几个关键字一样,也是修饰变量定义的,所以从语法(而不是语义)的角度把它和前面几个关键字归类到一起

变量的生存期(Storage Duration,或者Lifetime)分为以下几类

静态生存期(Static Storage Duration),具有外部(GLOBAL)或内部(LOCAL)链接属性,或者被static修饰的变量,在程序开始执行时分配和初始化一次,此后便一直存在直到程序结束。这种变量通常位于.rodata.data.bss段,例如上例中main函数外的Aabc,以及main函数里的a

自动生存期(Automatic Storage Duration),链接属性为无链接并且没有被static修饰的变量,这种变量在进入块作用域时在栈上或寄存器中分配,在退出块作用域时释放。例如上例中main函数里的bc。

动态分配生存期(Allocated Storage Duration),以后会讲到调用malloc函数在进程的堆空间中分配内存,调用free函数可以释放这种存储空间




全局变量

定义在所有函数外部的变量称之为全局变量。全局变量可以在任意地方使用。

局部变量

定义在函数内部的变量称之为局部变量。局部变量只能在所定义的函数函数内部中使用。

静态变量

静态变量的存放地址,在整个程序运行期间都不发生改变。全局变量都是静态变量,用'static'修饰的局部变量也是静态变量。未初始化的静态变量将被编译器初始化为0.

标识符的作用域

标识符变量名、函数名、类型名统称为标识符。标识符起作用的范围称之为标识符的作用域。

单文件的程序中,结构、函数和全局变量的作用域是其定义所在的整个文件。

函数形参的作用域是整个函数。

局部变量的作用域,是所在的语句块,即{............}中的内容。

同名标识符的作用域,可能一个被另一个包含。则在小的作用域中,作用域大的那个标识符被屏蔽,不起作用。

变量的生存期

全局变量(静态变量)的生存期,从程序开始到整个程序的结束。

静态局部变量的的生存期,从定义它语句的第一次执行开始,到整个程序的结束。

函数形参的生存期,从函数执行开始,到函数返回时结束。

非静态局部变量的生存期,从执行到定义它的语句开始,一旦执行到其作用于外,生存期便终止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值