C++学习:数据的存储、作用域、链接

news2025/1/17 15:23:56

一、数据的存储方式

C++中使用3种不同的方案来存储数据,不同方案的区别在于数据在内存中保留的时间。

1、自动存储

函数定义中声明的变量,以及函数的参数,是自动存储的。在程序执行对应函数的时候创建这些变量,对应的函数执行完之后,它们使用的内存被释放。也就是所谓的局部变量。

2、静态存储

在函数定义外的变量、以及使用static关键字定义的变量都是静态存储的,它们在程序的整个运行过程中都是存在的。函数定义外的变量有时候可以理解为全局变量。

3、动态存储

使用new运算符分配的内存,在使用delete运算符将其释放之前或者程序结束之前,它一直都存在,这种存储方式就是动态存储,也称为自由存储(free store),或堆(heap)。

二、作用域(scope)

作用域用来描述某个名称在文件的可见范围,这个名称有时候可以理解成变量。

例如,函数中定义的变量可以在该函数中使用,但不能在其他函数中使用;

函数定义之前定义的变量,可以在所有的函数中使用;

C++变量的作用域有多种:

作用域为局部的变量只能在自定义它的代码块中使用。自动变量就是一种局部变量。所谓代码块就是用{ }括起来的一系列语句,比如,函数体就是一种代码块。代码块还可以嵌套代码块 { { } }。

作用域位全局的变量,从定义的位置开始,到文件结尾都是可以使用的。

静态变量的作用域是全局还是局部,要看它是怎么定义的。

C++的函数作用域可以是整个类,或者整个名称空间,但不能是局部的。

三、链接性(linkage)

链接性用来描述某个名称,是如何在不同的单元之间共享的。

自动变量的名称没有链接性,因此不能共享。自动变量更多的可以理解为局部变量,用完就没了,就像太监一样,不会有后代,他的一生过完之后就没人会记得他。

四、自动变量

默认情况下,函数中声明的函数参数,变量都是自动存储的,也叫做自动变量,作用域是局部,没有链接性。

比如,在main函数中声明一个变量texas;

在函数oil()函数中也声明一个变量texas;

这两个texas是独立的,不会相互影响,他们只能定义它们的函数中使用。就像湖北有个小明,浙江也有个小明,他们在不同的省,他们之间一点关系都没有。

程序示例1:自动存储

#include <iostream>

void oil(int x);

int main()
{

    using namespace std;

    int texas = 31; //在main函数中定义的texas
    int year = 2023;

    cout << "1:in main(), texas = " << texas << ", &texas = " << &texas << endl;
    cout << "2:in main(), year = " << year << ", &year = " << &year << endl;

    oil(texas);
    cout << "8:in main(), texas = " << texas << ", &texas = " << &texas << endl;
    cout << "9:in main(), year = " << year << ", &year = " << &year << endl;


    return 0;
}


void oil(int x)
{
    using namespace std;
    int texas = 5; //在oil函数中定义的texas

    cout << "3:in oil(), texas = " << texas << ", &texas = " << &texas << endl;
    cout << "4:in oil(), x = " << x << ", &x = " << &x << endl;

    //开启代码块
    {
        int texas = 113; //在代码块中定义的texas
        cout << "5:in block, texas = " << texas << ", &texas = " << &texas << endl;
        cout << "6:in block, x = " << x << ", &x = " << &x << endl;
    } //代码块结束

    cout << "7:out of block, texas = " << texas << ", &texas = " << &texas << endl;

}

 输出结果:

1:in main(), texas = 31, &texas = 0x61fe1c
2:in main(), year = 2023, &year = 0x61fe18
3:in oil(), texas = 5, &texas = 0x61fddc
4:in oil(), x = 31, &x = 0x61fdf0
5:in block, texas = 113, &texas = 0x61fdd8
6:in block, x = 31, &x = 0x61fdf0
7:out of block, texas = 5, &texas = 0x61fddc
8:in main(), texas = 31, &texas = 0x61fe1c
9:in main(), year = 2023, &year = 0x61fe18

结果分析:

 分别在3个不同的位置定义了3个texas变量,地址分别是0x61fe1c、0x61fddc、 0x61fdd8,说明他们名称相同,但是属于不同的东西,可以同时存在。

在执行到main的时候,程序为1号taxas和year分配空间,使这两个变量可见;

当程序调用oil()时,前面的1号taxas和year变量仍留在内存中,但是不可见,程序为两个新的变量x和2号taxas分配内存,使它们可见;

当程序执行到oil()内部的代码块的时候,2号taxas将不可见,它被一个更新的3号taxes代替,但是变量x依然可见,这是因为该代码块没有定义x变量。

当程序执行到离开代码块的时候,将释放3号taxes所使用的内存,2号taxes重见天日。

当oil()函数执行完之后,2号taxes和x都过期,1号taxes和year也重见天日了。

自动变量与栈

C++编译器如何实现自动变量

自动变量的数目随着函数的开始而增加,随着函数的结束而减少,因此程序必须对自动变量进行管理。常见的管理方法是流出一段内存来存储这些自动变量,这段内存就是常说的

这段内存为什么被称为栈?栈是先进后出的,LIFO,即最后加入到栈中的变量,最先被使用。就像弹夹里面压子弹一样,最后压入的子弹,最先被射出。

因为存储的时候,新数据被象征性地放在原有数据的上面,当程序使用完之后,将其从栈中删除。

程序使用两个指针来跟踪栈,

一个指针指向栈底----栈的开始位置;

一个指针指向栈顶----下一个可用的存储单元。

当函数被调用时,函数里面的自动变量被加入到栈中,栈顶指针指向变量后面的一个可用的存储单元。当函数结束时,栈顶指针被重置为函数被调用前的值,释放内存。

自动变量在栈中的变化示意图

假设函数fib(short ms, int mi)被调用,传递2个字节的short和4个字节的int

1、函数调用前的栈(每个方框代表2个字节)

2、函数调用后的栈,函数调用fib(18, 50),将参数压入栈

3、函数开始执行后的栈 fib(short ms, int mi),函数执行时,将形参ms,mi与栈中的值关联起来

4、函数结束后,栈顶的指针恢复到原来的位置,但是栈中的数据并没有消失,可以后续被覆盖。

 

 五、静态变量的链接性

与C语言一样,C++也为静态变量提供3种链接性:

外部链接性----可在其他文件中访问;

内部链接性----只能在当前文件中访问;

无链接性----只能在当前函数或者代码块中访问;

这3种链接性都在程序的整个执行期间存在,与自动变量相比,它们的寿命更长。

静态变量的数目在程序运行期间是不变的,不需要使用栈来管理它们,编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个执行期间一直存在。

如果没有显式的初始化静态变量,编译器会把它们设置为0。

要想创建链接性为外部的静态变量,必须在代码块的外面声明它,有点像全局变量;

要想创建链接性为内部的静态变量,必须在代码块的外面声明它,并使用static关键字;

要创建没有链接性的静态变量,必须在代码块内声明它,并使用static关键字

程序示例2:三种静态变量的创建

#include <iostream>

int global= 1000; //创建一个有外部链接性的静态变量,这个工程的所有文件都可以使用它
static int s = 50; //创建一个有内部链接性的静态变量,只有在当前源文件使用它

void func();

int main()
{

    using namespace std;

    cout << "global = " << global << " and &global = "<< &global <<endl;
    cout << "s = " << s << " and &s = "<< &s <<endl;

    func();

    return 0;
}

void func()
{
    using namespace std;
    static int count ; //创建一个无链接性的静态变量,编译器 默认赋值0,只有当前函数可以使用它
    cout << "count = " << count << " and &count = "<< &count <<endl;

}

六、外部链接性的静态变量

链接性为外部的变量,简称为外部变量,存储持续性为静态,作用域是整个文件。外部变量是在函数的外部定义,对所有的函数来说都是外部的,在它后面的函数都可以使用它,因此也称它为全局变量

外部变量(全局变量)的定义规则:

1、每个使用外部变量的文件,都必须声明它;

2、变量只能定义一次。

因此C++提供两种变量声明:

1、定义声明,简称定义,它给变量分配存储空间;

2、引用声明,简称声明,它不给变量分配存储空间,因为它只是引用已有的变量,使用关键字extern

double up; //这属于定义,默认初始值为0
extern int blem; //这属于引用,blem在别的地方定义了;
extern char gr = 'z'; //这属于定于,因为有初始化,虽然他有extern关键字

如果要在多文件中使用外部变量,只需要在一个文件中进行该变量的定义,但在使用该变量的其他所有文件中,都必须使用关键字extern来声明它。

//file1.cpp
extern int cat = 20; //在文件file1.cpp中定义
int dog = 22; //在文件file1.cpp中定义
int flees; //在文件file1.cpp中定义


//file2.cpp,file2中没有重新声明flees变量,所以在file2中不能使用flees
extern int cat;//在文件file2.cpp中使用file1.cpp中定义的cat,要加extern关键字,但不能重复定义
extern int dog;//在文件file2.cpp中使用file1.cpp中定义的dog,要加extern关键字,但不能重复定义


//filen.cpp
extern int cat;//在文件filen.cpp中使用file1.cpp中定义的cat,要加extern关键字,但不能重复定义
extern int dog;//在文件filen.cpp中使用file1.cpp中定义的dog,要加extern关键字,但不能重复定义
extern int flees;//在文件filen.cpp中使用file1.cpp中定义的flees,要加extern关键字,但不能重复定义

七、内部链接性的静态变量

链接性为内部的静态变量,简称静态外部变量。这名字有点拗口,与前面的外部变量要加以区分,只要是定义在函数外部的就是外部变量,定义在函数外部的,又加了static关键的字就叫静态外部变量。内部链接性的静态变量与外部链接性的静态变量在单文件系统使用中是没有区别的,只有在多文件系统使用时才有区别。内部链接性的静态变量只能作用于当前文件,外部链接性的静态变量能作用于整个工程的全部文件。

程序示例3:链接性为外部和内部的静态变量交叉使用

file1.cpp

#include <iostream>

int tom = 3; //定义一个外部变量tom
int dick = 30; //定义一个外部变量dick

static int harry = 300; //定义一个静态外部变量harry,仅限于file1.cpp使用

void remote_access();


int main()
{

    using namespace std;

    cout << "main() reports the following address:\n";

    cout <<"&tom = "<< &tom <<", &dick = "<< &dick <<", &harry = "<<&harry << endl;

    cout << endl;

    remote_access();

    return 0;
}

file2.cpp

#include <iostream>

extern int tom;//属于声明,引用file1.cpp中定义的tom

static int dick = 10;//在file2.cpp中重新定义

int harry = 200;//重新定义一个外部变量

void remote_access()
{
    using namespace std;
    cout << "remote_access() reports the following address:\n";
    cout <<"&tom = "<< &tom <<", &dick = "<< &dick <<", &harry = "<<&harry << endl;
}

输出结果:

main() reports the following address:
&tom = 0x403020, &dick = 0x403024, &harry = 0x403028

remote_access() reports the following address:
&tom = 0x403020, &dick = 0x403010, &harry = 0x403014

从打印的地址就可以看出来,file1和file2使用的tom是同一个变量,dick和harry市不同的变量。

八、无链接性的静态变量

无链接性的静态变量,就是在函数内部,或者代码块内部,也称为静态局部变量。用static关键字定义的变量。我们知道在函数体内或者代码块内定义的变量就是局部变量,存在栈中,函数或代码块执行完,变量就没了,现在将这种变量前面加个static关键字,就是强行续命,即使函数或代码块不处于活动的时候,变量依然存在。

如果初始化了静态局部变量,程序只在启动的时候进行一次初始化,以后再调用函数时,不会想自动变量那样重新初始化。

#include <iostream>

const int ArSize = 10;
void strcount(const char* str);


int main()
{
    using namespace std;

    char input[ArSize];
    char next;

    cout << "enter a line:\n";
    cin.get(input, ArSize);

    while(cin)
    {
        cin.get(next);
        while(next !='\n')
            cin.get(next);
        strcount(input);
        cout <<"enter next line:\n";
        cin.get(input,ArSize);

    }

    cout << "Bye\n";

    return 0;
}

void strcount(const char* str)
{
    using namespace std;
    static int total = 0;
    int count = 0;

    cout<<"\"" << str <<"\"contains";
    while(*str++)
    {
        count++;
    }

    total += count;
    cout << count << " characters\n";
    cout << total << " characters total\n";
}

因为数组长度是10,因此程序从每行读取的字符串都不超过9个,每次函数调用时,自动变量count都被重置为0,但是静态变量total只在程序运行时被设置为0,以后再两次函数调用之间,其值保持不变,因此能记录输入字符的总数。

九、const关键字对存储类型的影响

在C++中,const限定符对默认存储类型稍有影响。在默认情况下,全局变量的链接性是外部的,但是const修饰的全局变量链接性是内部的。也就是说,在C++看来,const定义的全局变量与static说明符是一样的。也就是说下面的代码1和代码2是等效的。

代码1:

const int fingers = 10;

int main()
{
    ......
}

代码2:

static int fingers = 10;

int main()
{
    ......
}

十、函数的链接性

和变量一样,函数也有链接性,但是可选择的范围比变量小。

与C语言一样,C++也不允许在一个函数中再定义另一个函数,因此所有函数的存储持续性都是静态的,因为都是在函数外定义,在整个程序执行期间都是存在的。

默认情况下,函数的链接性是外部的,即可以再文件间共享。

在实际使用过程中,也可以在函数原型中使用关键字extern来指出函数是在另一个文件中定义的;

还可以使用static关键字将函数的链接性设置为内部的,使它只能在一个文件中使用,这种操作必须在函数原型和函数定义中都加static关键字。

static int private(double x);//函数原型前加static

static int private(double x) //函数定义前加static
{
    ......
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1107645.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

别再卷组件库了,Vue 拖拽库都断代了!

前言 最近在测试 Tailwind CSS 和 Uno CSS 这两种原子化 CSS 工具是否能够有效减少打包后的文件体积时&#xff0c;先开始分析这些工具的优缺点&#xff0c;然后再直接上数据&#xff0c;最后做了一款经典的 TodoList 来进行测试&#xff0c;文章都写好了就差最后的数据了。 …

中国人民大学与加拿大女王大学金融硕士项目:开启你的金融精英之路

在全球化的今天&#xff0c;金融行业的发展日新月异&#xff0c;对金融人才的需求也日益增长。为了满足这一需求&#xff0c;中国人民大学与加拿大女王大学联合推出了金融硕士项目&#xff0c;旨在培养具有国际视野、专业素养和创新能力的金融精英。 这一开创性的项目将两大世…

VR全景图片如何拍摄制作,拍摄制作过程中要注意什么?

引言&#xff1a; VR全景图片就是通过专业的相机设备捕捉到的一个空间的高清图像&#xff0c;再经过专业工具进行拼合&#xff0c;呈现出一种环绕式的视觉效果。想象一下&#xff0c;当你站在一个完全真实的环境中&#xff0c;可以自由地转动视角&#xff0c;看到四周的景色&a…

2023.10(u盘刻录iso)主机,vmware,virtualbox安装linux/ubuntu/kali/centos stream9/arch

download 1 kali官网 2 ubuntu官网 3vmware workstation pro(最新版17pro) 4 virtualbox for linux sudo apt install virtualbox-ext-pack 5 win32 disk imger linux dd 刻录iso到u盘 #查看U盘路径 fdisk -l #图形界面 以kali为例会在桌面出现挂载图标 点开之后输入pwd寻…

基于svg+js实现简单动态时钟

实现思路 创建SVG容器&#xff1a;首先&#xff0c;创建一个SVG容器元素&#xff0c;用于容纳时钟的各个部分。指定SVG的宽度、高度以及命名空间。 <svg width"200" height"200" xmlns"http://www.w3.org/2000/svg"><!-- 在此添加时钟…

森海塞尔EW-DP SKP直插式发射机:真正的无失真录制

韦德马克&#xff0c;2023年9月15日 – 10月底&#xff0c;EW-DP系列将再添新品——EW-DP SKP直插式发射机&#xff0c;面向摄像师、广播电视公司和电影制作人。板载32位浮点录制&#xff0c;结合该系列领先的134 dB发射机动态范围&#xff0c;让音频失真不复存在。无论要在现场…

将 windows 控制台编码修改成UTF-8

将 windows 控制台编码修改成UTF-8 临时修改&#xff0c;在控制台输入以下命令&#xff0c;65001代表utf-8编码 chcp 65001重新打开控制台后&#xff0c;又会变成默认GBK编码 永久修改&#xff0c;设置>时间和语言>语言>管理语言设置 打开更改系统区域设置&#xf…

【Spring Boot项目】根据用户的角色控制数据库访问权限

文章目录 简介方法一添加数据库依赖配置数据库连接创建用户角色表创建Spring Data JPA实体和仓库实现自定义的网关过滤器配置网关过滤器几个简单的测试API 方法二创建数据库访问接口实现数据库访问接口创建用户角色判断逻辑创建网关过滤器配置网关过滤器 总结 简介 在一些特定…

0基础学习PyFlink——Map和Reduce函数处理单词统计

在很多讲解大数据的案例中&#xff0c;往往都会以一个单词统计例子来抛砖引玉。本文也不免俗&#xff0c;例子来源于PyFlink的《Table API Tutorial》&#xff0c;我们会通过几种方式统计不同的单词出现的个数&#xff0c;从而达到循序渐进的学习效果。 常规方法 # input.py …

《Python入门核心技术》专栏总目录

❤️ 专栏名称&#xff1a;《Python入门核心技术》 &#x1f338; 内容介绍&#xff1a;基础篇、进阶篇、Web篇、网络爬虫、数据分析、数据可视化、自动化等&#xff0c;适合零基础和进阶的同学。 &#x1f680; 订阅专栏&#xff1a;订阅后可阅读专栏内所有内容&#xff0c;专…

易点易动设备管理系统:提升生产企业设备保养效率的利器

在现代生产企业中&#xff0c;设备保养是确保生产线稳定运行和产品质量的关键环节。然而&#xff0c;传统的设备保养方式往往面临效率低下、数据不准确等问题&#xff0c;影响了生产效率和竞争力。随着科技的进步&#xff0c;易点易动设备管理系统应运而生&#xff0c;以其智能…

【Netty专题】【网络编程】从OSI、TCP/IP网络模型开始到BIO、NIO(Netty前置知识)

目录 前言前置知识一、计算机网络体系结构二、TCP/IP协议族2.1 简介*2.2 TCP/IP网络传输中的数据2.3 地址和端口号2.4 小总结 三、TCP/UDP特性3.1 TCP特性TCP 3次握手TCP 4次挥手TCP头部结构体 3.2 UDP特性 四、总结 课程内容一、网络通信编程基础知识1.1 什么是Socket1.2 长连…

kkFileView源码编译并发布详细教程

文章目录 概述为啥要自己进行源码编译我不懂Java代码&#xff0c;可以编译吗为什么写这篇教程 废话不多说&#xff0c;下面是详细操作教程安装JDK安装Git安装Maven编译kkFileView源码 kkFileView安装和使用编译后获得安装包&#xff0c;进行解压修改配置文件执行在线安装&#…

开源贡献难吗?

本文整理自字节跳动 Flink SQL 技术负责人李本超在 CommunityOverCode Asia 2023 上的 Keynote 演讲&#xff0c;李本超根据自己在开源社区的贡献经历&#xff0c;基于他在贡献开源社区过程中的一些小故事和思考&#xff0c;如何克服困难&#xff0c;在开源社区取得突破&#x…

DNS压测工具-dnsperf的安装和使用(centos)

系统调优 系统调优脚本&#xff0c;保存为sh文件&#xff0c;chmod提权后执行即可 #!/bin/sh #系统全局允许分配的最大文件句柄数&#xff1a; sysctl -w fs.file-max2097152 sysctl -w fs.nr_open2097152 echo 2097152 > /proc/sys/fs/nr_open #允许当前会话 / 进程打开文…

1.JDK的安装方法以及环境变量的配置

学习Java的第一步应该从配置环境开始&#xff0c;这篇博文介绍了在哪下载安装包以及如何在windows电脑中配置&#xff0c;希望大家看完后可以独立安装 ~ 文章目录 一、下载安装包二、 安装路径配置三、 环境变量配置四、 验证是否配置成功 一、下载安装包 安装包可以从官网下载…

第三方软件测评单位可为企业带来哪些收益?

随着信息科技的发展&#xff0c;软件市场竞争也越来越大&#xff0c;软件企业为了更好的专注于产品开发&#xff0c;以及保障软件质量&#xff0c;会将软件测试交由第三方软件测评单位进行。 第三方软件测评&#xff0c;顾名思义&#xff0c;是由独立的、与软件开发商无关的专业…

Unity中Shader的XRay透视效果

文章目录 前言一、模拟菲涅尔效果1、获取 V 向量2、获取 N 向量3、点积输出效果4、模拟出菲涅尔效果(中间暗&#xff0c;周围亮) 二、实现 &#xff38;Ray 效果1、使用半透明排序、修改混合模式、加点颜色2、增加分层效果&#xff08;使用 frac 函数&#xff0c;只取小数部分&…

CAPL如何实现27服务解锁

在文章《CANoe-如何实现27服务解锁》里,我们介绍了诊断控制台中如何实现27解锁,如果我想在CANoe中使用CAPL程序解锁的话,又要如何实现呢? CAPL脚本也是通过模拟手动操作来实现27解锁,所以步骤为: 发送10 03发送27 01接收67 01,获取seed值根据seed值和算法,计算出key值…

自5月以来,俄罗斯Sandworm黑客侵入了11家乌克兰电信公司

导语&#xff1a;据乌克兰计算机应急响应团队&#xff08;CERT-UA&#xff09;的最新报告称&#xff0c;自2023年5月至9月&#xff0c;俄罗斯政府支持的黑客组织Sandworm成功侵入了乌克兰的11家电信服务提供商。这一组织被认为与俄罗斯武装部队的GRU有关。 简介 根据乌克兰计算…