学了C这么久,scope、memory、link属性应该都清楚了。转载本文请联系编程诸暨公众号。前言这些是编程语言中的基本概念。如果你对标题的问题不是很清楚,不知道作用域、链接属性、保存期限等概念的具体含义,那你不要错过这篇文章。为了更清楚地理解我们的问题,我们需要理解三个概念:作用域、链接属性和存储期限。作用域在C语言中,作用域用来描述从哪些区域可以访问一个标识符。常见作用域如下:块作用域,可见作用域是从定义到包含定义函数作用域的块结束,goto语句的标号有函数作用域文件作用域,从定义到定义文件都是可见的在最后。在函数外部定义的变量具有文件范围。函数原型范围,从形参定义到原型声明结束。为了便于说明,我们来看一个例子,很容易理解:/***************************作者:Mr.守望者出处:公众号编程诸暨个人博客:https://www.yanbinghu.com***************************************/#includeintnum1=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有块作用域,错误的标签://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有block作用域,所以在花括号外,不能直接使用。而num1和num2有文件作用域,所以main函数可以直接使用它。链接属性在《hello程序是如何变成可执行文件的》中我们讲了编译过程,最后一步就是链接。linkage属性决定了不同作用域中同名的标识符是否可以绑定到同一个对象或函数上。也就是说,不同作用域的标识符编译后是否是同一个实体。C变量具有三种链接属性:外部链接、extern修饰、或非静态修饰变量具有文件作用域具有外部链接属性internal链接、静态修饰变量具有文件作用域具有内部链接属性nolinkage、block作用域、函数作用域中的变量和函数原型作用域没有链接属性,没有静态修饰,和文件作用域的变量,当它们被链接时,多个具有相同名称标识符的变量最终绑定到同一个实体。static修饰的文件作用域的变量是不同的。在不同的文件中,即使标识符名称相同,它们也被绑定到不同的实体。因此,如果我们希望某个变量或函数只在某个文件中使用,使用静态修饰是一个很好的做法。同样,让我们??看一个例子。/*******************************作者:守望先生来源:公众号编程珠玑个人博客:https://www.yanbinghu.com*********************************************/#includeinta=5;//文件范围,外部链接属性,其他文件可以通过externinta=6使用本文件的astaticb;//文件范围,内部链接属性,即使其他文件也有同名的标识符,他们不同intmain(void){intsum=0;//没有链接属性sum=a+b;printf("sumis%d\n",sum);return0;}从代码可以看出,a和b都是具有文件函数域,a具有外部链接属性,b具有内部链接属性,而sum具有块作用域,因此没有链接属性。存储时长实际上,scope和link属性都描述了标识符的可见性,而存储时长则描述了这些标识符对应的对象的生命周期。存储期也分为以下几种:静态存储期,在程序执行过程中一直存在。文件范围内的变量有静态存储期和自动存储期。它(可变长度数组除外)从块开始并在块的末尾结束。因此,块作用域的变量具有自动存储持续时间,存储在堆栈中,并且需要显式初始化。动态分配存储时长,即通过malloc分配内存的变量。它存储在堆上,需要显式初始化。线程存储期,看名字就知道,是跟线程有关的。用关键字_Thread_local声明的变量有一个线程存储期,从声明到线程结束一直存在。初始化请参考《C语言入坑指南-被遗忘的初始化》。同样,我们通过下面的代码来更好的理解存储期/**********************************作者:看守先生来源:公众号编程诸暨个人博客:https://www.yanbinghu.com*****************************************/#includeintnum1=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=666返回0;另外,如果我们通过nm命令查看编译后的程序文件的符号表,可以找到num1、num2、tempSum,但是没有sum,前者占用的内存大小是编译时决定的。$gcc-g-olifetimelifetime.c$nmlifetime|grepnum10000000000601038Dnum1$nmlifetime|grepnum2000000000060103cdnum2$nmlifetime|greptempSum000000000601044btempSum.2289$nmlifetime|grepsum$在这里我们可以很方便的区分静态类型,局部变量,局部变量,静态变量,局部变量上面没了其实这只是另一种说法:Global:具有文件作用域的变量Static:具有静态存储持续时间或内部链接属性的Local:具有函数或块作用域的变量这样结合起来,就很容易理解了。局部变量:函数或块作用域的变量静态局部变量:函数或块作用域,静态存储持续时间全局变量:具有文件作用域的变量静态全局变量:具有内部链接属性的变量,具有文件作用域当然,这只是为了区分它们,这不是对它们的严格定义。更好的办法是通过代码理解:#includeintnum1=222;//全局变量staticintnum2=111;//static全局变量intadd(inta,intb){staticinttempSum=0;//staticlocalVariabletempSum=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;}总结本文总结如下:具有文件作用域的变量具有静态存储持续时间,具有不期望其他文件的链接属性最好使用static来修改访问的文件范围变量。static关键字的含义需要结合上下文来理解。如果可能的话,尽量避免使用全局变量,因为它可能会导致变量被意外修改。使用动态内存通常比栈内存慢,但是栈内存非常有限,参考https://en.wikipedia.org/wiki/Global_variableshttps://en.wikipedia.org/wiki/Local_variable《C11标准文档》作者:守望,Linux应用开发者,目前在公众号【编程珠玑】分享Linux/C/C++/数据结构与算法/工具等原创技术文章和学习资源。