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

2024-03-23 06:38

本文主要是介绍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.cppint students_number = 2017; //定义了外部变量char students_name = Obama;…int main( ) { … }//File02.cppextern 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.cppint students_number = 2017; //定义了常规外部变量…int main( ) { … }//File02.cppstatic int students_number = 2016;//定义了内部静态变量 other_function() {…}

 

 

 

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

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

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

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

 

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

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

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

 

补充:函数和链接性

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

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

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

 

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

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

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

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

 

 

 

 

 

 

 

这篇关于C++随记(八)---存储持续性、作用域和链接性的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/837451

相关文章

SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法

《SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法》在SQLyog中执行存储过程时出现的前置缩进问题,实际上反映了SQLyog对SQL语句解析的一个特殊行为,本文给大家介绍了详... 目录问题根源正确写法示例永久解决方案为什么命令行不受影响?最佳实践建议问题根源SQLyog的语句分

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

关于MongoDB图片URL存储异常问题以及解决

《关于MongoDB图片URL存储异常问题以及解决》:本文主要介绍关于MongoDB图片URL存储异常问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录MongoDB图片URL存储异常问题项目场景问题描述原因分析解决方案预防措施js总结MongoDB图

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下: