C++随记(八)---存储持续性、作用域和链接性

 

作者:teeyohuang

邮箱:teeyohuang@163.com

本文系原创,供交流学习使用,转载请注明出处,谢谢

 

版权声明:本篇文章是阅读《C++primer plus (第6版)中文版》第9章之后所作的笔记。部分文字和图表摘自于这本书。

 

C++随记(八)---存储持续性、作用域和链接性

一、存储持续性

 

C++中一般使用3种(C++11中是四种,这里只说3种)不同的方案存储数据,这些方案的区别在于数据保留在内存中的时间。

①自动存储持续性:

在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属的函数或者代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。

C++中有两种存储持续性为自动的变量:自动变量、寄存器变量。

②静态存储持续性变量:

在函数定义外的变量和使用关键字static定义的变量。它们在程序整个运行过程中都存在。C++中有3中存储持续性为静态的变量。静态无链接性、静态内部链接性、静态外部链接性。

③动态存储持续性:

用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或者程序结束为止。这种内存的存储持续性为动态,有时候被称为自由存储或者堆。

 

二、作用域

 

作用域描述了名称在文件(翻译单元)的多大范围内可见。

作用域有多种:

作用域为局部的变量只在定义它的代码块中可用。

作用域为全局(也叫文件作用域)的变量在定义位置到文件结尾之间都可用。

还有函数原型作用域、名称空间作用域、函数作用域等等。

 

三、链接性

链接性描述了名称如何在不同单元间共享。

链接性为外部 的名称可在文件间共享。

链接性为内部 的名称只能由一个文件中的函数共享。

自动变量的名称没有链接性,因为它们不能共享。

 

总结:不同的C++存储方式是通过存储持续性、作用域、链接性来描述的。

 

①自动存储持续性:

在默认情况下,函数中声明的函数参数和 变量 为 自动存储持续性作用域为局部无链接性

自动变量:只在定义它们的时候才创建,在定义它们的函数返回时系统回收变量所占存储空间。对这些变量存储空间的分配和回收是由系统自动完成的。一般情况下,不作专门说明的局部变量,均是自动变量。自动变量也可用关键字auto作出说明。

寄存器变量:在程序运行时,根据需要到内存中相应的存储单元中调用,如果一个变量在程序中频繁使用,例如循环变量,那么,系统就必须多次访问内存中的该单元,影响程序的执行效率。因此,C\C++语言还定义了一种变量,不是保存在内存上,而是直接存储在CPU中的寄存器中,这种变量称为寄存器变量。(了解即可)

②静态存储持续变量

编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在!(比如你在一个函数中定义了一个静态变量,函数结束后这个变量依然存在,直到程序结束为止,如果是自动变量的话,函数结束后就会被释放)

C++为静态存储持续变量提供了3种链接性:

外部链接性(可在其他文件中访问)、内部链接性(只能在当前文件中访问)、和无链接性(只能在当前函数或代码块中访问)。

 

A、创建链接性为外部的静态持续变量,必须在代码块外面声明它;

B、创建链接性为内部的静态持续变量,必须在代码块外面声明它,并加限定符static

C、创建无链接性的静态持续变量,必须在代码块内声明它,并加限定符static

所有静态变量都有如下初始化特征:未初始化的静态变量的所有位都被置为0.这叫做零初始化。除默认的零初始化外,还可以对静态变量进行常量表达式初始化动态初始化

int x;              //零初始化

int y = 1;           //常量表达式初始化

int z = y + f(x);      //动态初始化。要初始化z,就要调用函数f(),所以要等到该函数被链接且程序执行时。

 

 

 

 

A、外部链接性 的静态持续变量 的使用

链接性为外部的变量简称为外部变量,它们的存储持续性为静态,作用域为整个文件

外部变量是定义在外部的,因此对所有函数而言都是外部的,即可以在main( )函数前面或者头文件中定义他们。 可以在文件中位于外部变量定义后面的任何函数中使用它们。因此外部变量也被称为全局变量

●单定义规则:

          一方面,在每个使用外部变量的文件中,都必须声明它们;另一方面,单定义规则指出变量只能定义一次。因此C++提供了两种变量声明:一种是定义声明(简称定义),它给变量分配存储空间;另一种 是引用声明(简称声明),它不给变量分配存储空间,因为它引用已有的变量。引用声明使用关键字extern 且不初始化。

例子:

 

 

 

 //File01.cpp
        int students_number = 2017; //定义了外部变量
        char students_name = Obama;
        …
        int main( ) { … }
  
      //File02.cpp
        extern int students_number;//引用声明了来自于File01.cpp的变量
        extern char students_name;
        other_function() {…}

 

 

 

 解释:首先可以看到我在File01.cpp中定义的students_number和students_name这两个变量是在main()函数外面定义的,且未加限定符static,所以它们是外部变量,也就是全局变量,我在File02.cpp中需要用到这两个变量,所以使用引用声明,关键字extern表示这两个变量是来源于其它文件中的,我不需要定义,只要对File02.cpp宣告它们的存在即可。

      

      注意:如果在一个函数中,定义与全局变量同名的局部变量,那么局部变量将隐藏全局变量。如果需要使用全局变量,那么可使用作用域解析运算符(::),将此运算符放在变量名前时,表示使用改变量的全局版本。

    

      全局变量很诱人,因为所有函数都能访问它,因此不用传递参数了,在函数中修改了值就是对原变量修改了值,但是易于访问的代价很大---程序不可靠。通常情况下,应使用局部变量,应在需要知晓时才传递数据,而不应不加区分地使用全局变量。

B、内部链接性 的静态持续变量 的使用

将static限定符用于作用域为整个文件的变量时,改变量的链接性将为内部的。在多文件程序中,内部链接性和外部链接性之间的差别很有意义。链接性为内部的变量只能在其所属的文件中使用;但常规外部变量都具有外部链接性,即可以在 其他文件中使用,如情况A。

如果文件定义了一个静态外部变量,其名称与另一个文件中声明的常规外部变量相同,则在该文件中,内部静态变量将隐藏常规外部变量

 

//File01.cpp
        int students_number = 2017; //定义了常规外部变量
        …
        int main( ) { … }
  
      //File02.cpp
        static int students_number = 2016;//定义了内部静态变量 
        other_function() {…}

 

 

 

File02.cpp中定义的内部静态变量将隐藏File01.cpp中的常规外部变量,所以这样定义是合法的,如果不加static限定符,就违反了单定义规则。

可使用外部变量在多文件程序的不同部分之间共享数据;

可使用链接性为内部的静态变量在同一个文件中的多个函数之间共享数据(名称空间提供了另外一种共享数据的方法,本篇博文不予讨论)。

另外,如果将作用域为整个文件的变量加上static限定符,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突。

 

C、无链接性的静态持续变量的使用

   将static限定符用于在代码块中定义的变量,导致局部变量的持续性为静态,这意味着虽然该变量只在该代码块中可用,但它在该代码块不处于活动状态时仍然存在!因此在两次函数调用之间,静态局部变量的值将保持不变。另外如果初始化了静态局部变量,则程序只在启动时进行一次初始化以后再调用该函数时,将不会像自动变量那样再次被初始化

(这样的性质有利也有弊,如果你希望一个变量在每次使用该函数时都能被重新初始化,比如我们经常有int i = 0;这样的操作,那么就不能将其设为静态;反之,如果只是希望该变量在函数第一次使用时有个初值,之后再次使用函数时不希望将之前的结果抹去,比如计算累加数据时sum可能只需要第一次初始化0就好,之后还要利用前面相加的结果,就可使用static来避免第二次使用函数时,变量被初始化掉

 

补充:函数和链接性

函数也具有链接性,C++不允许在一个函数中定义另一个函数,因此所有函数的存储持续性自动为静态的,即整个程序执行期间都一直存在。在默认情况下,函数的链接性为外部的,即可在文件间共享。

实际上可在函数原型中使用关键字extern来指出函数是在另一个文件中定义的,不过这是可选的(要让程序在另一个文件中查找函数,该文件必须作为程序的组成部分被编译,或者是由链接程序搜索的库文件)。

也可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用。必须同时在原型和函数定义中使用该关键字。

 

C++在哪里查找函数的定义?

·如果该文件中的函数原型指出该函数是静态的,则编译器将只在该文件中查找函数的定义。

·否则,编译器(包括链接程序)将在所有的程序文件中查找。如果找到两个定义,编译器将发出错误的消息,因为每个外部函数只能有一个定义。

·如果程序文件中没有找到,编译器将在库中搜索,这意味着如果定义了一个与库函数同名的函数,编译器将使用程序员定义的版本,而不是库函数。

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值