啃书《C++ Primer Plus》之 内存模型(上) 变量的存储持续性 作用域 连接性
发布日期:2021-06-23 17:48:05 浏览次数:3 分类:技术文章

本文共 3133 字,大约阅读时间需要 10 分钟。

啃书系列持续更新ing,关注博主一起xiao习鸭~

系列文章:


某位大牛说过,C/C++语言的核心问题,是内存管理和指针。这一节我们来梳理一下书中内存模型一章的部分内容:有关存储持续性、作用域、连接性的问题。

书中对于本章内容的排布,放在了名称空间之前,笔者在刚学习内存空间时是不甚理解的。后来,在对本节内容梳理时,才渐渐明白了其中的用意。

笔者有着这样的理解:

对于变量名称的以及其占用内存的管理,始终工程开发中绕不开的问题。本节梳理的内容就是在名称空间机制出现之前的管理方式。之所以他们是重要的,是因为这些名称空间的使用并没有颠覆这套规则,而是在其上添砖加瓦 (java?什么java?哪里有java?) 。因此这套规则仍旧是基本法,并且仍在大量使用。因此,梳理本节课的内容是有必要的。

另外,本文的内容主要对变量上述性质进行讨论,至于书中提到的函数即语言连续性的问题,本文不做讨论。

下面思维导图:

在这里插入图片描述
图中关系乱吗?不要紧,等下文梳理完内容,相信你可以理清楚其中关系的!


存储与访问

存储持续性

首先我们来说存储的持续性。什么是存储的持续性?说白了,就是一个变量的数据在内存中持续的时间,也就是一个变量什么时候会占用一块内存而什么时候又会被释放。

在C++11中有四种不同的存储持续性:

  • 自动存储持续性
  • 静态存储持续性
  • 线程存储持续性
  • 动态存储持续性

对于线程存储持续性,本文不予探讨。对于动态存储持续性,将放在内存模型(下)进行细致剖析,本文也不予探讨。

主要介绍的,是前两种持续性,即自动存储持续性,静态存储持续性。

自动存储持续性

指的是一个变量的存储是自动的,在程序执行其所属的代码块或是函数时被创建,当所属代码块或函数运行结束时被销毁,内存被释放。

静态存储连续性

使用 static 关键字修饰的函数、变量的存储持续性是静态的,它代表着这个变量在整个程序运行的过程中一直存在。

值得注意的是,静态存储空间在声明时会默认的将所有位置零。

作用域

作用域指的是一个名称(函数、变量、类等)在文件中可使用的范围,或是有效作用范围。在该范围内,名称是有定义的,使用其是合法的;而在其作用域范围之外访问该名称将会造成错误。

例如,函数的形式参数的作用域为整个函数,此时在函数内部使用这个参数名称就是合法的,而在函数外部想要通过名称访问这个变量将会错误或是失败。(不考虑外部有同名全局变量)

C++中,作用域有很多种,下面列举几个常见的作用域:

局部作用域

在代码块或是函数参数列表中声明的变量的作用域是局部的。这些变量只在代码块中存在。

具体的说,是在代码块中、其声明语句之后起作用。

举个粟子:

void f(int a){
int b; int c;}

其中 a,b,c 的作用域都是局部的,但需要强调的是,变量 c 的作用域并不包含声明 b 的那条语句。也就是说,声明 b 时,c 是不存在的。

全局作用域

又叫做文件作用域。作用域为全局的变量在整个文件中可用,包括函数或是代码块之外的语句,都可以访问他们。

具体的,是在整个文件中、其声明语句之后可用。

举个粟子:

#include
int a;int main(){
std::cout << a << std::endl;}

其中 a 的作用域就是全局的。

类作用域

该作用域表明变量名称仅在类中是可用的,在类外是不可访问的。

连接性

变量的连接性表明其可以如何在各文件之间共享。而文件间对变量的共享,可以视作变量作用域的拓展。

例如,一个全局变量的连接性是外部的,他可以被其他文件共享,这也就意味着其可以将作用域从单个文件拓展到多个引用它的文件中。

外部链接性

正如上面例子说到的,外部连接性代表着一个名称可以在文件间共享。

使用 extern 关键字,可以在一个文件中使用另一个文件中定义的连接性为外部的全局变量。

详细示例请看下文全局变量有关内容。

内部链接性

内部链接性代表着变量的名称只能有一个文件的函数共享,不能同其他文件分享。

详细示例参考下文静态全局变量。


变量的存储与访问

下面就从上文说到的 存储持续性、作用域和连接性来分析一下我们常见的四种变量。

局部变量

定义在函数或是代码块中的变量。

  • 内存持续性:自动
  • 作用域:代码块内,声明语句之后
  • 连接性:不具有连续性

静态局部变量

使用 static 关键字声明的局部变量。

内存持续性:静态

作用域:代码块内
连接性:不具有连续性

这种变量的持续性是静态的,意味着其实伴随着程序存在的,不会因为包含它的代码块结束而销毁。如果声明在函数中,则该变量会在程序运行全过程中存在,但是外部无法访问该变量。因为其作用域是局部的,仅能在函数内部访问。

值得注意的是,该变量在函数内部声明并定义时,仅在第一次会进行初始化,后续的访问中便不再进行初始化。

看下面例子:

#include
using namespace std;int getStaticK(){
static int k = 10; k++; return k;}int main(){
for(int i = 0;i < 10;i++) {
cout << getStaticK() << endl; }}

在这个例子中,我们在 getStaticK 函数中定义了一个静态的局部变量,并将它初始化为10,最后返回这个变量的值。

在主函数中,我们循环十次调用这个函数,并将返回结果进行输出。
运行结果:在这里插入图片描述
从结果可以看到,每一次调用该函数并没有重新产生新的变量,也没有重新初始化 k 的值。

另外,读者可以尝试着在主函数数中直接使用 k 变量。你会发现这样的操作是不合法的。

全局变量

定义在代码块之外的变量。

内存持续性:静态

作用域:全局(文件)
连接性:外部连接性

例如,如下代码:

A.cpp:

extern int a = 0;

B.cpp

extern int a;

上面代码中,A B 两个文件是同属于一个工程的。我们在 A 中定义了一个全局变量,其连接性为外部(声明全局变量时)。B 是另一个文件,使用 extern 关键字声明引用全局变量 a 。

注:

这里需要注意的是,C++中的声明和定义并非同一个意思。声明表示告知编译器类型信息而不需要实际分配空间。定义却是需要编译器为其实际声明空间的。具体全局变量的声明与定义上。这里想强调的是:定义时可以省略 extern 关键字,而给全局变量初始化的操作将会被认为是定义。仅使用 extern 关键字声明变量代表改文件获得了这个全局变量的使用权,没有实际的定义一个新的变量并为其分配空间。

静态全局变量

使用 static 关键字修饰的全局变量。

内存持续性:静态

作用域:全局(文件)
连接性:内部连接性

这类变量虽然也是全局变量,但其使用 static 关键字修饰,将连接性更改为内部,代表着其不能被其他文件所共享。

也就是说,这个全局变量是专为本文件创造的,其他文件不可访问。或者说,它是内部的全局变量。
另外,也可以将这里的 static 看作是内部的含义而非静态的。将整句话看作是关键字的语意重载。

注:

值得注意的是,使用 const 定义的全局变量默认是被 static 修饰的。也就是说,全局常量默认的连接性为内部,不可以被其他文件所共享这时,如果想要将它定义为全局常量,就需要把缺省的 extern 关键字显式的写出来了。

啃书这个系列将会持续的更新,你的点赞和关注是对萌新博主莫大的鼓励。答应我,这次一定好吗QAQ

转载地址:https://wayne-lee-jlu.blog.csdn.net/article/details/105193973 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:中台化改造实践--Spring Boot 2.0 整合日志框架(一)
下一篇:企业中台建设中Maven Pom治理最佳实践

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月23日 07时08分32秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章