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

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

C++读写word文档(.docx)DuckX库的使用详解

《C++读写word文档(.docx)DuckX库的使用详解》DuckX是C++库,用于创建/编辑.docx文件,支持读取文档、添加段落/片段、编辑表格,解决中文乱码需更改编码方案,进阶功能含文本替换... 目录一、基本用法1. 读取文档3. 添加段落4. 添加片段3. 编辑表格二、进阶用法1. 文本替换2

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

k8s搭建nfs共享存储实践

《k8s搭建nfs共享存储实践》本文介绍NFS服务端搭建与客户端配置,涵盖安装工具、目录设置及服务启动,随后讲解K8S中NFS动态存储部署,包括创建命名空间、ServiceAccount、RBAC权限... 目录1. NFS搭建1.1 部署NFS服务端1.1.1 下载nfs-utils和rpcbind1.1

Redis高性能Key-Value存储与缓存利器常见解决方案

《Redis高性能Key-Value存储与缓存利器常见解决方案》Redis是高性能内存Key-Value存储系统,支持丰富数据类型与持久化方案(RDB/AOF),本文给大家介绍Redis高性能Key-... 目录Redis:高性能Key-Value存储与缓存利器什么是Redis?为什么选择Redis?Red

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法