【C++】内存模型分析

news2025/3/31 18:14:54

在 C++ 语言中,程序运行时的内存通常被划分为以下几个区域:

  1. 代码区(Text Segment)
  2. 常量区(Constant Segment)
  3. 全局/静态区(Data Segment,包含静态数据段和 BSS 段)
  4. 堆区(Heap)
  5. 栈区(Stack)

分布图例如下:

1. 代码区(Text Segment)

  • 该区域存放的是程序的机器指令,即代码本身。
  • 代码区通常是只读的(read-only),防止程序在运行过程中修改自身代码,提高安全性。
  • 在程序启动时由操作系统加载到内存,并且通常所有线程共享这部分内存

2. 常量区(Constant Segment)

  • 该区域用于存储只读的常量,例如字符串字面量和 const 关键字修饰的全局常量。
  • 常量区通常和代码区一样是只读的,防止意外修改。
  • 例如:
const int x = 10;   // 存储在常量区
const char* str = "Hello";  // "Hello" 字符串存储在常量区
  • 但要注意,const 变量并不一定存储在常量区,例如:
void func() {
    const int local_const = 5;  // 该常量存储在栈区
}

这里 local_const 是局部 const 变量,它仍然存储在栈区,而不是常量区。


3. 全局/静态区(Data Segment)

  • 该区域存储全局变量静态变量static 修饰的变量)。
  • 进一步细分为:
    • 已初始化数据段(Data Segment):存放已初始化的全局变量和静态变量。
    • 未初始化数据段(BSS Segment):存放未初始化的全局变量和静态变量,程序运行时会自动初始化为 0。
示例:
int global_var = 42;    // 存在已初始化数据段
static int static_var = 10; // 存在已初始化数据段
int uninitialized_global;  // 存在 BSS 段,默认值为 0
static int uninitialized_static;  // 存在 BSS 段,默认值为 0

区别:

  • 全局/静态变量的生命周期贯穿整个程序运行时间,直到程序退出才被释放。
  • 这些变量存储在可读写的内存区域,不同于代码区和常量区的只读特性。

4. 堆区(Heap)

  • 堆区用于动态分配的内存,newmalloc 分配的对象存储在这里。
  • 由程序员手动管理,如果 new 了对象,必须 delete,否则会造成内存泄漏
  • 例如:
int* p = new int(5); // 在堆区分配一个 int
delete p;            // 释放堆内存
  • 注意
    • 堆的管理通常由 C++ 运行时库(Runtime Library)和操作系统负责。
    • 堆区不像栈区那样自动释放,程序员需要自己管理内存的分配和释放。

5. 栈区(Stack)

  • 栈区存储函数调用时的局部变量、函数参数、返回地址等数据。
  • 由系统自动管理,函数调用时分配,函数返回时自动释放。
  • 栈的分配和回收速度比堆快,因此局部变量的存取效率更高。
  • 例如:
void func() {
    int a = 10; // 局部变量 a 存在栈区
}
  • 注意
    • 栈的空间有限,过度使用递归或分配过大的局部数组可能导致栈溢出(Stack Overflow)

常量区 vs. 全局/静态区

两者的主要区别如下:

类别

存储内容

是否可修改

存储位置

生命周期

常量区

字符串字面量、全局 const

变量

只读

代码段的一部分

程序运行期间

全局/静态区

全局变量、静态变量

可读写

Data Segment(已初始化/未初始化数据段)

程序运行期间

关键点:

  1. 常量区是只读的,而全局/静态区是可修改的
  2. 字符串字面量存储在常量区,而普通全局/静态变量存储在全局/静态区
  3. 局部 const 变量存储在栈区,而不是常量区

全局变量和静态变量的区别

在 C++ 中,全局变量和静态变量的存储位置都属于**“全局/静态存储区”**(Data Segment),但它们之间还是有一些区别的。我们通常把这部分内存区域分成两个概念:

  1. 全局区(Global Segment)——存储全局变量
  2. 静态区(Static Segment)——存储静态变量

不过在实际实现上,这两个变量通常都位于全局/静态存储区(Data Segment),只是它们的作用域和访问方式不同。


1. 全局变量(Global Variables)

  • 存储位置:存放在全局区(Data Segment 的一部分)
  • 作用域:在整个程序范围内可访问,即在定义它的文件及其他文件中(如果使用 extern 声明)。
  • 生命周期:从程序启动到程序终止,一直存在,不会被销毁。
  • 初始化
    • 显式初始化:按照程序员指定的值初始化。
    • 默认初始化:如果未初始化,全局变量会被自动初始化为 0(整数)、nullptr(指针)、0.0(浮点数)。
  • 示例
#include <iostream>

int globalVar = 10; // 全局变量,存储在全局区

void func() {
    std::cout << globalVar << std::endl; // 在任何地方都可以访问
}

int main() {
    func(); // 输出 10
    return 0;
}
  • 可被 extern 关键字在其他文件中引用
// file1.cpp
int globalVar = 42;

// file2.cpp
extern int globalVar;  // 在其他文件中声明

2. 静态变量(Static Variables)

静态变量可以分为静态局部变量静态全局变量

(1) 静态全局变量(Static Global Variables)
  • 存储位置:存放在静态区(Data Segment 的一部分)
  • 作用域:仅限于定义它的文件内部文件作用域),不能被其他文件 extern 访问。
  • 生命周期:从程序启动到程序终止,始终存在。
  • 初始化
    • 显式初始化:按照程序员指定的值初始化。
    • 默认初始化:如果未初始化,静态全局变量会被自动初始化为 0、nullptr0.0
  • 示例
#include <iostream>

static int staticGlobalVar = 20; // 静态全局变量

void func() {
    std::cout << staticGlobalVar << std::endl; // 可以访问
}

int main() {
    func(); // 输出 20
    return 0;
}
  • 不能被 extern 关键字访问
// file1.cpp
static int staticVar = 42;  // 只能在 file1.cpp 内部访问

// file2.cpp
extern int staticVar;  // ❌ 错误,无法访问

(2) 静态局部变量(Static Local Variables)
  • 存储位置:存放在静态区(Data Segment 的一部分)不会存放在栈上
  • 作用域仅限于函数内部,但不会在函数调用结束后销毁,下一次调用仍然能访问原来的值。
  • 生命周期:在程序运行期间一直存在,直到程序结束。
  • 初始化
    • 只在函数首次调用时初始化一次,后续调用不会重新初始化。
  • 示例
#include <iostream>

void func() {
    static int counter = 0; // 静态局部变量,初始化仅执行一次
    counter++;
    std::cout << "Counter: " << counter << std::endl;
}

int main() {
    func(); // 输出 Counter: 1
    func(); // 输出 Counter: 2
    func(); // 输出 Counter: 3
    return 0;
}
    • counter 变量即使 func() 结束了,也不会被销毁。
    • 普通局部变量(非 static) 每次调用都会重新初始化,而静态局部变量的值会被保留。

3. 静态区和全局区的区别

(1)存储位置
  • 全局变量 存储在全局区(Data Segment 的一部分)
  • 静态变量 存储在静态区(Data Segment 的一部分)

实际上,全局变量和静态变量都在数据段(Data Segment),但它们的作用域不同,因此有时被称为“全局区”和“静态区”。

(2)作用域

变量类型

作用域(访问范围)

可否用 extern 访问

全局变量

整个程序都可以访问

✅ 可以使用 extern

静态全局变量

仅限于当前文件(文件作用域

❌ 不能跨文件访问

静态局部变量

仅限于当前函数(局部作用域

❌ 不能跨函数访问

(3)生命周期

变量类型

生命周期

全局变量

程序运行期间一直存在

静态全局变量

程序运行期间一直存在

静态局部变量

程序运行期间一直存在(不会随着函数结束而销毁)

普通局部变量

函数调用时创建,调用结束后销毁

(4)初始化

  • 全局变量静态变量(全局或局部)如果未手动初始化,会自动初始化为 0 或 nullptr
  • 普通局部变量(非 static) 未初始化时,值是未定义的(随机值)

4. 总结

  1. 全局变量存放在全局区(Data Segment),静态变量存放在静态区(Data Segment),但它们都属于“全局/静态存储区”
  2. 静态变量包括静态全局变量和静态局部变量
    • 静态全局变量 作用域仅限当前文件(不能用 extern)。
    • 静态局部变量 作用域仅限当前函数,但生命周期贯穿整个程序运行。
  1. 静态变量的生命周期比普通局部变量长,即使函数调用结束,静态局部变量的值也不会被销毁,而普通局部变量会被销毁。
  2. 全局变量可以被 extern 访问,静态全局变量不行
  3. 静态变量初始化只执行一次,而普通局部变量每次函数调用都会重新初始化。

总结

  1. C++ 的内存布局分为 代码区、常量区、全局/静态区、堆区和栈区,各有不同的作用和生命周期。
  2. 常量区与全局/静态区不同,常量区通常是只读的,而全局/静态区可以修改
  3. 全局变量和静态变量的生命周期与程序一致,而局部变量在栈上,函数返回时就会被销毁
  4. 堆区用于动态分配的内存,需要手动释放,否则可能会发生内存泄漏
  5. 栈区用于局部变量和函数调用数据,系统自动分配和回收,但栈的空间有限,可能会发生栈溢出

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

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

相关文章

Vue2+OpenLayers携带请求头加载第三方瓦片数据

目录 一、案例截图 二、安装OpenLayers库 三、代码实现 一、案例截图 在对接一些第三方GIS地图的时候,需要携带请求头来验证身份,从而获取相应的瓦片数据,这时候我们需要改造一下WMTS服务的调用方式,效果如图所示: 二、安装OpenLayers库 npm install ol 三、代码实现…

智能汽车图像及视频处理方案,支持视频实时拍摄特效能力

在智能汽车日新月异的今天&#xff0c;美摄科技作为智能汽车图像及视频处理领域的先行者&#xff0c;凭借其卓越的技术实力和前瞻性的设计理念&#xff0c;为全球智能汽车制造商带来了一场视觉盛宴的革新。美摄科技推出智能汽车图像及视频处理方案&#xff0c;一个集高效性、智…

数据结构--顺序表(实现增删改查)

三个文件&#xff08;Mytest.c 、MySeqList.c 、 MySeqList.h&#xff09; Mytest.c测试函数 MySeqList.c 函数定义 MySeqList.h函数声明 增删改查的步骤&#xff1a; 初始化 增加元素 • 尾插&#xff1a;先检查顺序表空间是否足够&#xff0c;若不足则进行扩容&#x…

【android】补充

3.3 常用布局 本节介绍常见的几种布局用法&#xff0c;包括在某个方向上顺序排列的线性布局&#xff0c;参照其他视图的位置相对排列的相对布局&#xff0c;像表格那样分行分列显示的网格布局&#xff0c;以及支持通过滑动操作拉出更多内容的滚动视图。 3.3.1 线性布局Linea…

说说MyBatis一、二级缓存和Spring一二级缓存有什么关系?

大家好&#xff0c;我是锋哥。今天分享关于【说说MyBatis一、二级缓存和Spring一二级缓存有什么关系&#xff1f;】面试题。希望对大家有帮助&#xff1b; 说说MyBatis一、二级缓存和Spring一二级缓存有什么关系&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源…

蓝桥杯题型分布2

蓝桥杯 蓝桥杯题型分类2素数孪生素数素数个数朴素筛法求素数线性筛法求素数 因数分解试除法分解质因数 等差素数列梅森素数组素数素数环找素数(分段筛&#xff09;连续素数和小明的素数对疑似素数质数拆分纯质数超级质数质数日期质数游戏2魔法阵的能量阿坤老师切割年糕阶乘分解…

vue响应式原理剖析

一、什么是响应式? 我们先来看一下响应式意味着什么?我们来看一段代码: m有一个初始化的值,有一段代码使用了这个值; 那么在m有一个新的值时,这段代码可以自动重新执行; let m = 20 console.log(m) console.log(m * 2)m = 40上面的这样一种可以自动响应数据变量的代码机…

Element UI实现表格全选、半选

制作如图所示的表格全选、半选&#xff1a; 父组件 <template><div id"app"><SelectHost :hostArray"hostArray" /></div> </template><script> import SelectHost from ./components/SelectHost.vue export default…

如何使用动作捕捉系统训练人形机器人

随着人形机器人变得越来越先进&#xff0c;使用动作捕捉系统教会它们如何像人类一样移动成为了人形机器人领域正在研究的全新方向。本文探讨了如何使用Xsens技术捕捉精确的人类运动数据&#xff0c;使机器人能够通过人工智能和机器学习安全高效地学习、适应和执行复杂任务。 近…

内网渗透技术 Docker逃逸技术(提权)研究 CSMSF

目录 如何通过上传的webshell判断当前环境是否是物理环境还是Docker环境 方法一&#xff1a;检查文件系统 方法二&#xff1a;查看进程 方法三&#xff1a;检查网络配置 方法四&#xff1a;检查环境变量 方法五&#xff1a;检查挂载点 总结 2. 如果是Docker环境&#x…

生活电子常识——cmd不能使用anaconda的python环境,导致输入python打开应用商店

前言 电脑已经安装了anaconda,从自带的Anaconda Prompt (Anaconda3)中是可以识别python环境的&#xff0c;然而切换到cmd时&#xff0c;突然发现cmd中无法识别anaconda的python环境&#xff0c;竟然打开了应用商店让我安装Python&#xff0c;这当然是不对的。 解决 这是因为…

如何在linux中部署dns服务 主备dns (详细全过程)

环境centos 7.9 主DNS&#xff1a;192.168.60.131 备DNS&#xff1a;192.168.60.134 我以 chenxingyu0.com 指向 192.168.60.200为例 首先是主dns #!/bin/bash# 检查是否为 root 用户 if [ "$(id -u)" ! "0" ]; thenecho "请使用…

word写latex-Mathtype安装成功-方法

MathType安装报错 想在word写latexMathtype, 网上搜教程安装&#xff0c; 结果一直报错一直删重来&#xff0c; 一直报错一直删了重来 一直报错一直删了重来来来&#xff0c; 就这么反反复复一直不好 网上的教程都是教你不是删mathtype, 就是删office 时代变了啊&#x…

【踩坑日记】springboot 打包后实现类无法找到

试过了所有改什么目录 依赖 clean都以失败告终 最后将实现类的文件名从Impl改成impl宣布成功 记得使用idea自带的重构

deepseek(2)——deepseek 关键技术

1 Multi-Head Latent Attention (MLA) MLA的核心在于通过低秩联合压缩来减少注意力键&#xff08;keys&#xff09;和值&#xff08;values&#xff09;在推理过程中的缓存&#xff0c;从而提高推理效率&#xff1a; c t K V W D K V h t c_t^{KV} W^{DKV}h_t ctKV​WDKVht​…

Linux (Centos7)安装Mongodb4.0.28

一、官网下载安装包上传到服务器系统 官网&#xff1a;https://www.mongodb.com/try/download/community 放在/opt/software目录下&#xff1a; 二、解压至/usr/local目录下&#xff0c;并重新命名为mongodb [rootlocalhost software]# tar -zxvf mongodb-linux-x86_64-rhel7…

数据库设计-笔记4

1.操作词汇简介 insert&#xff1a;用于向表中插入新记录。 delete&#xff1a;用于从表中删除记录。 update&#xff1a;用于修改表中已有的记录。 select&#xff1a;用于从表中检索数据。 2.代码基础(增删改&#xff09; -- 修改表中的信息 -- 修改表名 alter table s…

基于python的图书管理系统设计与实现

摘要 21世纪的今天&#xff0c;随着计算机技术和网络技术的的不断推广发展和应用&#xff0c;图书馆管理方式也应该随之而更新&#xff0c;借由人力进行繁杂重复的图书管理工作已经不再可取&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0…

RAG专栏:向量数据库

一、数据库分类 键值数据库&#xff08;Key-Value&#xff09;&#xff1a;通常用于简单的数据存储&#xff0c;通过键来快速访问数据。文档数据库&#xff08;Document&#xff09;&#xff1a;用于存储文档结构的数据&#xff0c;如 JSON 格式。图数据库&#xff08;Graph&a…

【GPUStack】【dify】【RAGflow】:本地部署GPUStack并集成到dify和RAGflow

目录 Nvidia-Driver CUDA NVIDIA Container Toolkit&#xff08;新版本的docker不用安装&#xff0c;自带&#xff09; Docker 部署GPUStack Text Embeddings 部署模型库模型 测试 部署开源模型&#xff08;modelscope&#xff09; dify 集成 RAGflow集成 Nvidia-Dri…