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

相关文章

使用SpringBoot+InfluxDB实现高效数据存储与查询

《使用SpringBoot+InfluxDB实现高效数据存储与查询》InfluxDB是一个开源的时间序列数据库,特别适合处理带有时间戳的监控数据、指标数据等,下面详细介绍如何在SpringBoot项目... 目录1、项目介绍2、 InfluxDB 介绍3、Spring Boot 配置 InfluxDB4、I

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)