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

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

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

一、存储持续性

C++中一般使用3种(C++11中是四种,但是书上貌似没给第四种)不同的方案存储数据,这些方案的区别在于数据保留在内存中的时间。

①自动存储持续性:

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

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++在哪里查找函数的定义?

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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Linyb极客之路

并发编程之Synchronized关键字

一、Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作...

2966
来自专栏WD学习记录

html5学习笔记(二)

1. min、max、step属性用于包含数字或日期的input类型规定限定(约束)。

761
来自专栏Jerry的SAP技术分享

使用javap深入理解Java整型常量和整型变量的区别

下面我们就用javap将.class文件反编译出来然后深入研究Java里整型变量和整型常量的区别。

1493
来自专栏个人分享

JVM 类型的生命周期学习

Java虚拟机通过装载、连接和初始化一个JAVA类型,使该类型可以被正在运行的JAVA程序所使用,其中,装载就是把二进制形式的JAVA类型读入JAVA虚拟机中;...

933
来自专栏云霄雨霁

发布与逸出

1273
来自专栏决胜机器学习

设计模式专题(十六)——迭代器模式

设计模式专题(十六)——迭代器模式 (原创内容,转载请注明来源,谢谢) 一、概述 迭代器模式(Iterator)提供一种方法,顺序访问一个聚合对象中的每个元素...

3698
来自专栏逆向技术

C++反汇编第二讲,不同作用域下的构造和析构的识别

               C++反汇编第二讲,不同作用域下的构造和析构的识别 目录大纲:   1.全局(静态)对象的识别,(全局静态全局一样的,都是编译期间...

20610
来自专栏阿凯的Excel

Python读书笔记17(while与列表、字典)

今天分享利用while函数处理列表和字典,顺便温习一下历史知识 一、论如何将一个列表折腾至另外一个列表!(两个列表是独立的) 论折腾列表有几种方法! 先分...

3675
来自专栏小二的折腾日记

面试总结-C++

堆、栈、自由存储区、全局/静态存储区、常量存储区 自由存储区存储malloc申请的内存 (1)从静态存储区域分配 。内存在程序编译的时候就已经分配好,这块内存在...

1881
来自专栏大数据挖掘DT机器学习

Python一些基础面试题目总结

1 Python是如何进行内存管理的? 答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 一、对象的引用计数机制 pytho...

3866

扫码关注云+社区

领取腾讯云代金券