【C++】内存模型分析

在 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(); // 输出 10return 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(); // 输出 20return 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: 1func(); // 输出 Counter: 2func(); // 输出 Counter: 3return 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.rhkb.cn/news/41621.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于解决Ubuntu终端及系统字体大小的问题

在Ubuntu中调整终端和系统字体大小可以通过以下方法&#xff08;可能不仅仅只是这几种&#xff09;实现&#xff1a; 1. 调整系统字体大小 打开终端并输入以下命令&#xff0c;安装GNOME Tweaks&#xff0c;等待安装完成&#xff1a; sudo apt install gnome-tweaks 接着进行…

java8循环解压zip文件---实现Excel文件数据追加

java8循环追加Excel数据 实际遇到问题&#xff1a;定期获取zip文件&#xff0c;zip文件内有几个固定模板的Excel文件&#xff0c;有的Excel文件可能还包含多个sheet。 有段时间一次性获取到好几个zip包&#xff0c;需要将这些包都解压&#xff0c;并且按照不同的文件名、sheet进…

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

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

MTK平台 Android12-Android13 默认搜狗输入法

系统默认搜狗输入法功能实现 文章目录 需求&#xff1a;场景 参考资料需求实现内置搜狗输入法配置第三方apk .mk 和 搜狗安装包&#xff0c;不可卸载方式搜狗输入法module 配置到系统device.mk 中去 设置搜狗输入法为默认输入法给输入法授权&#xff0c;默认所有权限 总结思考 …

一周掌握Flutter开发--8. 调试与性能优化(上)

文章目录 8. 调试与性能优化核心技能8.1 使用 Flutter DevTools 分析性能8.2 检查 Widget 重绘&#xff08;debugPaintSizeEnabled&#xff09;8.3 解决 ListView 卡顿&#xff08;ListView.builder itemExtent&#xff09; 其他性能优化技巧8.4 减少 build 方法的调用8.5 使用…

【区块链安全 | 第一篇】密码学原理

文章目录 1.哈希函数1.1 哈希函数的性质1.2 常见哈希算法1.3 Merkle Tree&#xff08;默克尔树&#xff09;1.4 HMAC&#xff08;哈希消息认证码&#xff09; 2. 公钥密码学2.1 对称加密 vs 非对称加密2.2 RSA 算法2.3 ECC&#xff08;椭圆曲线密码学&#xff09;2.4 Diffie-He…

vim的一般操作(分屏操作) 和 Makefile 和 gdb

目录 一. vim的基本概念 二. vim基础操作 2.1 插入模式 aio 2.2 [插入模式]切换至[正常模式] Esc 2.3[正常模式]切换至[末行模式] shift ; 2.4 替换模式 Shift R 2.5 视图&#xff08;可视&#xff09;模式 (可以快速 删除//注释 或者 增加//注释) ctrl v 三&…

NFC 智能门锁全栈解决方案:移动端、服务器、Web 管理平台

目录 一、系统整体架构 二、移动端 APP 开发 2.1 开发环境与基础准备 2.2 主要功能模块 2.3 示例代码&#xff08;Android/Kotlin 简化示例&#xff09; 三、后台服务开发 3.1 环境准备 3.2 主要功能 3.3 示例代码&#xff08;Node.js Express 简化示例&#xff09; …

系统与网络安全------网络应用基础(5)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 虚拟化 虚拟化技术原理概述虚拟化虚拟化实现条件常见的虚拟化软件产品 VMware应用实战安装VMware Workstation创建新虚拟机虚拟机的硬件配置调整 虚拟化高级应用虚拟机备份虚拟机快照 虚拟化技术 原理概述 虚…

Postman 下载文件指南:如何请求 Excel/PDF 文件?

在 Postman 中进行 Excel/PDF 文件的请求下载和导出&#xff0c;以下是简明的步骤&#xff0c;帮助你轻松完成任务。首先&#xff0c;我们将从新建接口开始&#xff0c;逐步引导你完成整个过程。 Postman 请求下载/导出 excel/pdf 文件教程

华为HCIE学习指南,如何更好的学习HCIE?

新盟教育 专注华为认证培训十余年 为你提供认证一线资讯&#xff01; 在竞争激烈的ICT行业&#xff0c;华为HCIE认证犹如一颗璀璨的明珠&#xff0c;散发着耀眼的光芒。它不仅是对个人技术能力的高度认可&#xff0c;更是开启高薪职业大门的钥匙。然而&#xff0c;华为HCIE学习…

贪心算法——c#

贪心算法通俗解释 贪心算法是一种"每一步都选择当前最优解"的算法策略。它不关心全局是否最优&#xff0c;而是通过局部最优的累积来逼近最终解。优点是简单高效&#xff0c;缺点是可能无法得到全局最优解。 一句话秒懂 自动售货机找零钱&#xff1a;用最少数量的…

架构思维:如何设计一个支持海量数据存储的高扩展性架构_数据分片、存储、复制与一致性的原理性问题

文章目录 PRE引言1. 数据分片策略Hash取模分片一致性Hash分片Range分片分片设计原理核心设计模块分片规则定义动态分片调整路由与负载均衡 应对热点的关键技术多级分片&#xff08;Hierarchical Sharding&#xff09;副本分散策略缓存层配合 典型应用场景优缺点分析 2. 应对热点…

Jenkins最新版,配置Gitee私人令牌和Gitee凭证

jenkins 配置Gitee私人令牌和凭证 jenkins 版本&#xff1a;Jenkins 2.492.2 Gitee配置 Jenkins配置gitee插件&#xff0c;需要先申请gitee私钥。 安装gitee插件 申请Gitee私人令牌&#xff0c;后面还需要添加凭证。 测试链接&#xff0c;并保存 配置凭证

ORACLE RAC ASM双存储架构下存储部分LUN异常的处理

早上接到用户电话&#xff0c;出现有表空间不足的告警&#xff0c;事实上此环境经常巡检并且有告警系统&#xff0c;一开始就带着有所疑惑的心理&#xff0c;结果同事在扩大表空间时&#xff0c;遇到报错 ORA-15401/ORA-17505,提示ASM空间满了&#xff1a; ALERT日志&#xff1…

Windows下docker使用教程

docker安装 镜像制作镜像加载容器创建更新镜像导出镜像 Windows10安装dockerdocker image制作docker 镜像加载docker 容器创建更新imageimage 导出为.tar文件 #以Windows10 、11为例 linux和Windows区别在于docker安装的程序是哪个操作系统的&#xff0c;后面的内容其实不变 …

9.4分漏洞!Next.js Middleware鉴权绕过漏洞安全风险通告

今日&#xff0c;亚信安全CERT监控到安全社区研究人员发布安全通告&#xff0c;Next.js 存在一个授权绕过漏洞&#xff0c;编号为 CVE-2025-29927。攻击者可能通过发送精心构造的 x-middleware-subrequest 请求头绕过中间件安全控制&#xff0c;从而在未授权的情况下访问受保护…

代码随想录算法训练营Day12 | Leetcode 226翻转二叉树、101对称二叉树、104二叉树的最大深度、111二叉树的最小深度

代码随想录算法训练营Day12 | Leetcode 226翻转二叉树、101对称二叉树、104二叉树的最大深度、111二叉树的最小深度 一、翻转二叉树 相关题目&#xff1a;Leetcode226 文档讲解&#xff1a;Leetcode226 视频讲解&#xff1a;Leetcode226 1. Leetcode226.翻转二叉树 给你一棵二…

3.26学习总结 做题

先初始化n1时&#xff0c;输出的图案。 观察可以得到&#xff0c;n每加1&#xff0c;则在原先图案的左下方和右下方重新打印一遍原先的图案&#xff0c;可以分为两步。 1.复制原先图案打印在其正下方和右下方&#xff0c;并将原先图案清空。 2.在现在图案的上方中间打印原先…

Linux学习笔记(应用篇二)

基于I.MX6ULL.MINI开发板 开发板与电脑相互通信电脑与开发板互传文件 开发板与电脑相互通信 用网线将电脑与开发板连接 本人使用的是Ubuntu系统&#xff0c;不是虚拟机 一般来说刚开始电脑和开发板是ping不通的 首先查看电脑的 IP WinR&#xff0c;cmd调出终端 我使用的是…