来源:公众号【编程珠玑】
作者:守望先生
前言
这些是编程语言中的基本概念,如果你还不是非常明确地清楚标题的问题,并且不知道作用域,链接属性,存储期等概念的具体含义,那么本文你不该错过。为了更加清晰的理解我们的问题,需要先了解三个概念:作用域,链接属性,存储期。
作用域
C语言中,作用域用来描述标识符能够被哪些区域访问。
而常见作用域有以下几种:
为了便于说明,我们来看一个例子,就很容易理解了:
#include<stdio.h> intnum1=222;//定位在函数外,具有文件作用域 staticintnum2=111;//定义在函数外,具有文件作用域 intswap(int*a,int*b);//这里的a,b是函数原型作用域 intswap(int*a,int*b) { if(NULL==a||NULL==b) gotoerror; else { inttemp=*a;//定义在函数内,块作用域 *a=*b; *b=temp; return0; } //printf("tempis%d\n",temp);//因为temp具有块作用域,因此在这里不能直接使用 error://goto语句的标签,函数作用域,因此在前面就可以引用 { printf("inputparaisNULL\n"); return-1; } } intmain(void) { printf("num1=%d,num2=%d\n",num1,num2); swap(&num1,&num2);//num1num2具有文件作用域,可以在main函数中直接使用 printf("num1=%d,num2=%d",num1,num2); return0; }/****************************作者:守望先生来源:公众号编程珠玑个人博客:***************************************/#include<stdio.h>intnum1=222;//定位在函数外,具有文件作用域staticintnum2=111;//定义在函数外,具有文件作用域intswap(int*a,int*b);//这里的a,b是函数原型作用域intswap(int*a,int*b){if(NULL==a||NULL==b)gotoerror;else{inttemp=*a;//定义在函数内,块作用域*a=*b;*b=temp;return0;}//printf("tempis%d\n",temp);//因为temp具有块作用域,因此在这里不能直接使用error://goto语句的标签,函数作用域,因此在前面就可以引用{printf("inputparaisNULL\n");return-1;}}intmain(void){printf("num1=%d,num2=%d\n",num1,num2);swap(&num1,&num2);//num1num2具有文件作用域,可以在main函数中直接使用printf("num1=%d,num2=%d",num1,num2);return0;}
可以看到,error标签具有函数作用域,整个函数内都可见,而temp具有块作用域,因此在大括号外部,不能直接使用它。而num1和num2具有文件作用域,因此main函数可以直接使用它。
链接属性
在《hello程序是如何变成可执行文件的》我们说到了编译的过程,最后一个步骤就是链接。链接属性决定了在不同作用域的同名标识符能否绑定到同一个对象或者函数。或者说,不同作用域的标识符在编译后是否是同一个实体。
c变量有三种链接属性:
再稍作解释,没有static修饰,且具有文件作用域的变量,他们在链接时,多个同名标识符的变量最终都绑定到同一个实体。而static修饰的具有文件作用域的变量就不一样了,不同文件内,即便标识符名字相同,它们也绑定到了不同的实体。
因此,如果我们希望某个变量或函数只在某一个文件使用,那么使用static修饰是一个很好的做法。
同样的,来看一个例子。
#include<stdio.h> inta=5;//文件作用域,外部链接属性,其他文件可通过externinta的方式使用该文件的a staticb=6;//文件作用域,内部链接属性,即便其他文件也有同名标识符,它们也是不同的 intmain(void) { intsum=0;//无链接属性 sum=a+b; printf("sumis%d\n",sum); return0; }/****************************作者:守望先生来源:公众号编程珠玑个人博客:***************************************/#include<stdio.h>inta=5;//文件作用域,外部链接属性,其他文件可通过externinta的方式使用该文件的astaticb=6;//文件作用域,内部链接属性,即便其他文件也有同名标识符,它们也是不同的intmain(void){intsum=0;//无链接属性sum=a+b;printf("sumis%d\n",sum);return0;}
从代码中可以看到,a和b都具有文件作用域,a具有外部链接属性,而b具有内部链接属性,sum具有块作用域,因此无链接属性。
存储期
实际上作用域和链接属性都描述了标识符的可见性,而存储期则描述了这些标识符对应的对象的生存期。存储期,也分下面几种:
关于初始化,可参考《C语言入坑指南-被遗忘的初始化》。
同样地,我们通过下面的代码来更好地理解存储期:
#include<stdio.h> intnum1=222;//静态存储期 staticintnum2=111;//静态存储期 intadd(inta,intb) { staticinttempSum=0;//静态存储期 tempSum=tempSum+a+b; returntempSum; } intmain(void) { printf("num1=%d,num2=%d\n",num1,num2); intsum=0;//自动存储期 sum=add(num1,num2); printf("firsttimesum=%d\n",sum);//sum=333 sum=add(num1,num2); printf("secondtimesum=%d\n",sum);//sum=666 return0; }/****************************作者:守望先生来源:公众号编程珠玑个人博客:***************************************/#include<stdio.h>intnum1=222;//静态存储期staticintnum2=111;//静态存储期intadd(inta,intb){staticinttempSum=0;//静态存储期tempSum=tempSum+a+b;returntempSum;}intmain(void){printf("num1=%d,num2=%d\n",num1,num2);intsum=0;//自动存储期sum=add(num1,num2);printf("firsttimesum=%d\n",sum);//sum=333sum=add(num1,num2);printf("secondtimesum=%d\n",sum);//sum=666return0;}
另外,如果我们通过nm命令查看编译出来的程序文件的符号表,我们可以找到num1,num2,tempSum,而没有sum,前者所用的内存数量在编译时就确定了。关于nm命令的使用可以参考《linux常用命令-开发调试篇》。
0000000000601038Dnum1 $nmlifetime|grepnum2 000000000060103cdnum2 $nmlifetime|greptempSum 0000000000601044btempSum.2289 $nmlifetime|grepsum $$gcc-g-olifetimelifetime.c$nmlifetime|grepnum10000000000601038Dnum1$nmlifetime|grepnum2000000000060103cdnum2$nmlifetime|greptempSum0000000000601044btempSum.2289$nmlifetime|grepsum$|grepnum1
什么全局变量,局部变量,静态局部变量,静态全局变量
到这里,我们就可以很容易区分上面的变量类型了。实际上这里只是换了一种说法:
全局:具有文件作用域的变量
静态:具有静态存储期或内部链接属性
局部:具有函数或块作用域的变量
因而结合起来,也就很好理解了。
当然,这仅仅是为了区分它们,这并不是它们的严格定义。更好的方法,是通过代码来理解:
intnum1=222;//全局变量 staticintnum2=111;//静态全局变量 intadd(inta,intb) { staticinttempSum=0;//静态局部变量 tempSum=tempSum+a+b; returntempSum; } intmain(void) { printf("num1=%d,num2=%d\n",num1,num2); intsum=0;//局部变量 sum=add(num1,num2); printf("firsttimesum=%d\n",sum);//sum=333 return0; }#include<stdio.h>intnum1=222;//全局变量staticintnum2=111;//静态全局变量intadd(inta,intb){staticinttempSum=0;//静态局部变量tempSum=tempSum+a+b;returntempSum;}intmain(void){printf("num1=%d,num2=%d\n",num1,num2);intsum=0;//局部变量sum=add(num1,num2);printf("firsttimesum=%d\n",sum);//sum=333return0;}
总结
本文总结如下:
参考
/wiki/Global_variables
/wiki/Local_variable
《C11标准文档》
关注公众号【编程珠玑】,获取更多Linux/C/C++/Python/Go/算法/工具等原创技术文章。后台免费获取经典电子书和视频资源