C 语言的储存类和链接

 

C的强大功能之一在于它允许您控制程序的细节。C的内存管理系统正是这种控制能力的例子。它通过让您决定那些函数知道那些变量以及一个变量在程序中存在多长时间;来实现这种控制。使用内存存储是程序设计的有一元素。C为标识符提供了5种不同的存储模型(或称存储类),还有基于指针的第6种存储模型。

       在开始之前我先解释一下几个概念:

       1.作用域(scope

       作用域描述了程序可以访问标识符(变量)的一个或多个区域,当标识符在程序的某个部分被声明时,它只有在程序的一定区域才能被访问,这个区域有标识符的作用域决定(以上解释来自C Primer PlusPointers On C)。

       作用域分为:代码块作用域,文件作用域,原型作用域,函数作用域

              代码块作用域:任何在代码块中声明的标识符都可以被代码块中的语句所访问,当代码块结束则标识符被销毁。处于嵌套状态,如果内层标识符与外层标识符相同,则在内层隐藏外层的标识符,内层语句无法访问外层标识符。传统上,拥有代码块作用域的标识符必须在代码块的开始处声明,C99则放宽了这一规则,允许代码块中任意位置声明变量,这就意味着变量的声明可能出现在For循环,While循环,Do While循环或者If语句的控制部分,以及其控制的代码中,——即使这些代码没有用花括号括起来。

              文件作用域:任何在所有代码块之外声明的标识符都有文件作用域,它表示这些标识符从它们声明之处直到它所在的源文件结尾处都可以访问。函数名也具有文件作用域,因为函数名本身并不属于任何代码块,这也就意味着函数只关心形参的类型而非名字。如下:

                                   int functionint achar b//原来写法

                                   int function (int,char)//简化写法

              原型作用域:原型作用域只适用于在函数原型中声明的参数名,在原型中,参数名并非必需,它不必与函数定义中的实参相匹配,也不必与实际调用中的实参相匹配。事实上,唯一可能出现冲突的就是在同一原型中不止一次使用同一名字。

              函数作用域:函数作用域仅用于一处,即语句标签,用于Goto语句,其基本上可以总结为一条规则:一个函数中的所有标签必须唯一。我希望你们永远不要用这个知识。

       2.链接属性(Linkage

              标识符链接属性决定如何处理在不同文件中出现的标识符,标识符的作用域和它的链接属性有关,但这两个属性并不同。(链接属性的改变不会影响到它的作用域)

              链接属性分为3种:External Linkage(外部链接)

                                            Internal Linkage(内部链接)

                                            No Linkage   (无链接)

              空链接属性的标识符总是被当做单独的个体,也就是说该标识符的多个声明被当作独立不同的实体。

              内部链接属性标识符同一个源文件内所有声明都引用同一个实体,不同源文件则分属为不同的实体。

              外部链接属性标识符无论多少次声明,位于多少个源文件都表示一个实体。

              具有代码块作用域或原型作用域的标识符有空链接属性。

              具有文件作用域的可能具有内部或外部链接属性。

              关键字StaticExtern用于在声明中修改标识符的链接属性。从技术上来说,这两个声明只有在声明中才是必需的(请理清楚上下文中定义与声明的区别)。StaticExternal属性改为Internal属性且只对缺省链接属性为External的声明才有改变链接属性的效果。Extern为一个标识符指定具有External链接属性。如果你在一个地方定义一个变量,在使用这个变量的地方添加Extern关键字,可以便于其他读者更容易理解你的意图。

存储类

时期

作用域

链接

声明方式

自动

自动

代码块

代码块内

寄存器

自动

代码块

代码块内,使用关键字register

具有外部链接的静态

静态

文件

外部

所有函数之外

具有内部链接的静态

静态

文件

内部

所有函数和之外,使用关键字Static

空链接的静态

静态

代码块

代码块内,使用关键字Static

 

       3.存储类型(Storage Class

              变量的存储类型是指变量的内存类型。变量的存储类型决定了变量何时创建,何时销毁以及值将保持多久。3个地方用于存储变量:普通内存,运行时堆栈,硬件寄存器。

C语言2种存储时期:静态存储时期(Static Storage Duration)和自动存储时期(Automatic Storage Duration)。具有静态存储时期的变量从程序运行之前被创建,程序运行期间一直保持,直到程序结束。具有自动存储时期的变量,程序运行到其所在的代码块中被创建,代码块结束则销毁。注意,修改变量的存储类型并不表示修改该变量的作用域。若用Register修饰,则可加快频繁访问自动变量的访问速度,但编译器并不一定理睬关键字Register,如果太多变量被声明为Register,则编译器会选择前几个存储与寄存器中,其余的按普通变量处理(注意:寄存器变量不可取址)。如果编译器有自己的一套寄存器优化方案,它会忽略一些Register关键字,因为有编译器决定那些变量存储与寄存器比人脑更合理一些。

变量的存储类型取决于它的声明位置。代码块内声明的变量具有自动存储时期。存储于运行时堆栈,称为自动变量(或局部变量)。若用Static加以修饰,则具有静态存储时期,称为静态变量。

注释:当用于上下文环境的不同,Static拥有不同的意思。确实很不幸,这给很多新手带来混淆。所以我对Static做了一些总结:当Static作用于函数定义时或用于代码块之外变量声明时,Static关键字用于修改标识符链接属性,从External改为Internal,但存储类型和作用域不会改变,这种方式声明的变量或函数只能在当前的源文件中被访问。但Static作用于代码块内部的变量声明时,Static用于修改变量的存储类型,从自动变量修改为静态变量,但变量的链接属性和作用域不会改变,这种方式声明的变量在程序执行之前创建,并且程序执行期间一直存在,而不是每次在代码块开始执行时创建,代码块执行完后销毁了。

       介绍完概念,我们就拿一个实例来解说一下(以下实例来自Pointer On C

       第一行a链接属性为External。如果b的定义在其他的地方,则第二行的Extern关键字在技术上并非必需的,但风格上还是加上这个关键字为好。第三行Static关键字修改c的缺省链接属性,把它修改为Internal。声明了ab的其他源文件使用的变量实际访问的时此处的两个变量。而c只能有本文件语句访问。abc为静态存储类型,表示它们并不是存储于堆栈中的,程序执行期间其值将一直保持。第七行和第十三行声明了局部变量ab,在那部分程序中将隐藏同名静态变量,因此,这三个变量作用域为:

                                   a       第一行至第十二行,第十七行至第二十九行

                                   b       第二行至第六行,第二十五行至第二十九行

                                   c       第三行至第二十九行

第四行声明了2个标志符,d的作用域从第四行至结尾,具有External链接属性,存储于静态内存中。E具有无链接属性,但它于局部变量冲突,所以,它的作用域为第六行至第十一行,第十七行至第十九行,第二十三行至第二十四行。第六行至第八行声明的时局部变量,所以它们的作用域到函数结束为止,具有无链接属性,自动存储类型,存储于堆栈中。第九行并不需要,因为这行代码位于第一行的作用域中。第十二行和第十三行声明的时局部变量,具有无链接属性,自动存储类型,在这代码块中,以前声明的同名变量将被隐藏,不可访问。第十四行时全局变量h在这里可以被访问,它具有External链接属性。第十九,二十行同上面一样。第二十五行声明的函数i具有Internal链接属性,只能有当前源文件的语句访问。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值