惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The Last Watchdog
The Last Watchdog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Troy Hunt's Blog
L
LINUX DO - 最新话题
C
Check Point Blog
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
大猫的无限游戏
大猫的无限游戏
S
Security @ Cisco Blogs
IT之家
IT之家
T
The Exploit Database - CXSecurity.com
The GitHub Blog
The GitHub Blog
D
Docker
Engineering at Meta
Engineering at Meta
AWS News Blog
AWS News Blog
S
Security Affairs
U
Unit 42
P
Palo Alto Networks Blog
V
Visual Studio Blog
Y
Y Combinator Blog
D
DataBreaches.Net
Forbes - Security
Forbes - Security
阮一峰的网络日志
阮一峰的网络日志
美团技术团队
Security Latest
Security Latest
aimingoo的专栏
aimingoo的专栏
Simon Willison's Weblog
Simon Willison's Weblog
A
Arctic Wolf
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hacker News: Front Page
博客园 - 司徒正美
博客园 - Franky
宝玉的分享
宝玉的分享
TaoSecurity Blog
TaoSecurity Blog
Latest news
Latest news
Scott Helme
Scott Helme
MongoDB | Blog
MongoDB | Blog
量子位
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
P
Privacy International News Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog

qlAD 的技术笔记

2026 年 6 月 7 日 -- 我有社交恐惧症,恐惧外面到处雷电风雨声(大学生活记录) 手把手带你实现哈希表(C/C++ 数据结构) 手把手带你实现图结构(C/C++ 数据结构) 手把手带你实现二叉树(C/C++ 数据结构) 手把手带你实现栈和队列(C/C++ 数据结构) 手把手带你实现单/双链表(C/C++ 数据结构) 手把手带你实现动态数组(C/C++ 数据结构) 2026 年 1 月 2 日 -- 我不怕冷,因为我内心炽热 【2025 年度总结】(大学生活记录) 2025 年 12 月 2 日 -- 省电模式:大一 100 天耗电日志(大学生活记录) 蓝桥杯备赛第一期 —— 枚举与模拟 2025 年 11 月 3 日 -- 用爱发电、大爱无私(大学生活记录) 使用循环嵌套输出规则图形找规律讲解(嵌套循环-图形输出) 2025 年 10 月 1 日 -- 二字开头第一年的生日记录及感悟随笔(大学生活记录) Python 初级 04 -- 目前为止 Python 中你们做题可能会遇到的问题以及七七八八 2025 年 9 月 24 日 -- 开学后的第一课及军训汇演(大学生活记录) Python 初级 03 -- 程序设计的三种结构之选择结构和循环结构 Python 初级 02 -- 基础的数据类型和输入输出 2025 年 9 月 17 日 -- 大连 2025 国际大体联足球世界杯(大学生活记录) Python 初级 01 -- 初识编程、理解计算机语言及程序运行方式 适合大一新生学习编程的前一课 -- 编程先导(编程的本质) 2025 年 9 月 12 日 -- 三天军训感受以及一些七七八八(大学生活记录) 2025 年 9 月 9 日 -- 体检、军训前的准备(大学生活记录) 适合大一新生的计算机基础知识 -- 个人认为足够版 2025 年 9 月 7 日 -- 既来之、则安之(大学生活记录) 联想小新 pro 16 2025 开箱记录:跳过联网、office 激活、更换 win 11 专业版 R 语言中的数学函数 计算机软件著作权申请过程记录 从聚类到回归:用 Python 解析鸢尾花数据集的完整数据科学流程 条件概率、全概率与贝叶斯公式 —— 一篇文章带你死磕概率公式 高中化学物质反应规则表 —— 方程式之间也有规律可循 集合元素性质、关系、子集公式、基本运算 —— 一篇文章带你全方面死磕集合 如何为你的项目选择合适的许可证 -- 全方位指南 解释 3:1 和 9:3:3:1 的详细来源 —— 建立减数分裂与孟德尔遗传学定律之间的联系 重新设置磁盘分区以及 Btrfs 子卷(subvolume)布局的无损方案 零基础实战:用 Docker 启动 dockermailserver 绕过 25 端口封锁搭建个人邮箱服务器 基于 GitHub Actions + OSS 的自托管 APT 仓库全面指南 如何在安装 Debian 时配置 Btrfs 子卷 | 详细分步指南 一个合格的个人网站站长应该知道哪些事? CSS 基础入门教程 —— 选择器、样式声明 HTML 基础入门 —— 基本结构和语法 给博客添加一个文章数据统计的页面 Git 学习笔记(命令备忘表) Anki 牌组选项详细解释与设置 手动编译带有 KernelSU 的 OnePlus 6 内核 安卓类原生系统补全计划 —— PixelExperience(一加 OnePlus) 如何使用 use-sound 为 React 应用添加声音效果 标准化项目的 GitHub Flow 工作流 如何搭建设计自己的博客网站? 使用安知鱼主题同款的 Twikoo 评论组件 配置 GitHub Actions 实现自动化提交百度收录 第00期 | 环境搭建 & 递归 (一) | 基本数列递归 逐字符讲解 C 语言 HelloWorld 程序 2023 年回顾 🤟 Anki:记忆神器,助你高效学习 Gentoo 学习笔记 美化你的 PixelExperience OS Markdown 语法指南 搭建 OneDrive 目录索引 为什么用 65 表示大写字母 A
C++ 程序的内存布局 —— 代码区、全局/静态区、栈区和堆区
qlAD · 2025-08-25 · via qlAD 的技术笔记

cover

所有程序都存储在内存中。当一个程序开始运行时,操作系统会为其创建一个进程。进程是系统进行资源分配和调度的基本单位,它拥有独立的虚拟地址空间。

拿一个 4GB 内存的电脑来说,在 32 位进程的虚拟地址空间中,可寻址范围从 0x00000000 到 0xFFFFFFFF。通常低地址存放代码段与数据段,高地址附近为栈,堆则用于满足程序运行时动态分配内存的需求。

每个区域在运行时都有自己的用途,了解内存布局有助于有效管理内存并避免泄漏和堆栈溢出等问题。

内存四区

C++ 程序的内存布局主要分为四个区域:代码区、全局/静态区、栈区和堆区。

内存四区

1. 代码区(Code Segment)

代码区存储程序编译的二进制代码,由操作系统进行管理,通常为只读以提高安全性,防止程序意外修改自身代码。

比如你学习 C++ 时写的所有代码,包括英文字母、数字、符号、中文注释等等,这些代码在编译后会被转换为二进制机器码(010101),存储在代码区中。

程序的可执行机器指令越多,代码区占用的空间就越大;而注释、模板元编程产生的未实例化代码并不会进入最终的可执行文件,因此不会增加代码区体积。

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

全局/静态区存储程序员定义的全局变量和静态变量。此段位于代码区的正上方,分为两部分:

A. 已初始化数据段(.data)

存储在声明时分配值的全局变量和静态变量。

// Global variable
int a = 50;

// Static variable
static int b = 100;

a 和 b 都 存储在初始化的数据段中

B. 未初始化数据段(.bss)

保存尚未显式初始化的全局变量和静态变量。系统在运行时自动将这些值设置为零。

// Global variable
int c;
// Static variable
static int d;

c 和 d 放置在 BSS 段中。

3. 堆区(Heap Segment)

堆是用于运行时动态分配的内存区域。堆中的内存使用 new/delete 等运算符或 malloc()/free() 等函数手动管理。

#include <iostream>
using namespace std;

int main() {

    // Dynamically allocate array on heap
    int* arr = new int[10];
    delete[] arr;
    return 0;
}

使用 new[] 分配的数组,必须对应使用 delete[] 释放。arr 指向的内存驻留在堆中。

4. 栈区(Stack Segment)

栈区用于:局部变量、函数参数、返回地址

void func()
{
    int a = 10; // a 是一个在函数中声明的变量
    cout << a << endl;
}

每个函数调用都会创建一个栈帧(Stack Frame),该帧被推送到栈。当函数完成时,他会自动释放

存储持续性

了解了内存四区后,你可能对于不同类型的数据有了更深入地理解,C++ 使用三种存储持续性(不讨论并行编程)

  1. 自动存储持续性(Automatic Storage Duration)
  2. 静态存储持续性(Static Storage Duration)
  3. 动态存储持续性(Dynamic Storage Duration)

这些方案的区别顾名思义就在于数据存储的持续时间,再来回顾下内存四区,从下到上依次为代码区、全局/静态区、堆区和栈区

其中对应的存储持续性为

内存区域存储持续性
栈区自动存储持续性
全局/静态区静态存储持续性
堆区动态存储持续性

1. 自动存储持续性(栈区)

栈区存放的是在函数中声明的变量(包括函数参数)。这些变量在函数调用时创建,并在函数返回时销毁,存储持续性为自动的。

#include <iostream>
using namespace std;

void func()
{
    int a = 10; // a 是一个自动变量
    cout << a << endl;
}

int main()
{
    func(); // 函数调用时,a 被创建,函数返回时,a 被销毁
    cout << a << endl; // 错误:'a' 未在此作用域内声明 (其生命周期也在 func 函数结束时终结)
    return 0;
}

2. 静态存储持续性(全局/静态区)

在全局/静态区中存放的是在函数定义外定义的变量和使用 static 关键字声明的变量。他们在程序运行期间一直存在,存储持续性为静态的。

#include <iostream>
using namespace std;

int a = 10;
static int b = 20;

void func()
{
    cout << a << endl; // 正确,a 在这里可访问
    cout << b << endl; // 正确,b 在这里可访问
}

int main()
{
    func();
    cout << a << endl; // 正确,a 在这里可访问
    cout << b << endl; // 正确,b 在这里可访问
    return 0;
}

代码区(Code Segment)通常被视为具有静态存储持续性,因为它的内容在程序的整个生命周期中都存在且不变。

3. 动态存储持续性(堆区)

堆区存放的是使用 new 运算符创建的变量,分配的内存将会一直存在,直到使用 delete 运算符删除该变量将其释放或者程序结束为止。这种存储持续性为动态的。

#include <iostream>
using namespace std;

int * func()
{
    int* p = new int(10); // p 指向一个动态分配的整数
    return p;
}

int main()
{
    int* p = func();
    cout << *p << endl; // 输出 10
    delete p;
    cout << *p << endl; // 出错

    return 0;
}

程序验证四区分布

#include <iostream>
#include <iomanip>
#include <cstdint>

/* ================= 全局/静态区 ================= */
int                 global_a = 1;
const int           const_global_a = 2;
static int          static_global_a = 3;
static const int    static_const_global_a = 4;

/* ================= 代码区示例函数 ================= */
void code_sample() {}

int main()
{
    /* ================= 栈区 ================= */
    int              local_a = 10;
    const int        const_local_a = 11;

    /* ================= 全局/静态区 ================= */
    static int       static_local_a = 12;
    static const int static_const_local_a = 13;

    /* ================= 堆区 ================= */
    int* heap_p = new int(20);

    /* ================= 统一打印 ================= */
    auto print = [](const char* region, const char* name, const void* addr) {
        std::cout << std::left << std::setw(10) << region
            << std::setw(30) << name
            << reinterpret_cast<uintptr_t>(addr) << '\n';
        };

    std::cout << "区域      变量名                        十进制地址\n";
    std::cout << "---------------------------------------------\n";

    /* 栈区 */
    print("栈区", "local_a", &local_a);
    print("栈区", "const_local_a", &const_local_a);

    /* 堆区 */
    print("堆区", "heap_p", heap_p);

    /* 全局/静态区 */
    print("数据段", "global_a", &global_a);
    print("数据段", "const_global_a", &const_global_a);
    print("数据段", "static_global_a", &static_global_a);
    print("数据段", "static_const_global_a", &static_const_global_a);
    print("数据段", "static_local_a", &static_local_a);
    print("数据段", "static_const_local_a", &static_const_local_a);

    /* 代码区 */
    print("代码段", "code_sample 函数入口",
        reinterpret_cast<void*>(&code_sample));

    delete heap_p;
    return 0;
}

运行结果

可以看出,同一区域的地址挨着很近,但是前文说过,代码区是低地址,栈区是高地址,但是这里的地址按照 10 进制来看,代码区的地址比栈区的还大,这是什么原因呢?

这是因为现代操作系统使用了虚拟内存技术,程序看到的地址是虚拟地址,并不是真实的物理地址。操作系统会将虚拟地址映射到物理地址上,这个映射关系是动态变化的。

所以我们看到的地址并不一定反映内存的实际布局。

结语

了解了不同的存储类别后,相信你对 C++ 程序的内存布局有了更深入的理解。合理使用不同的存储类别,可以帮助你编写出更高效、更安全的代码。