【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(); // 输出 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. 栈区用于局部变量和函数调用数据,系统自动分配和回收,但栈的空间有限,可能会发生栈溢出
相关推荐
拖孩4 分钟前
【Nova UI】十、打造组件库第一个组件-图标组件(下):从.svg 到 SVG Vue 组件的高效蜕变✨
前端·javascript·vue.js
柠石榴11 分钟前
【python编程从入门到到实践】第四章 操作列表
开发语言·python
独立开阀者_FwtCoder11 分钟前
一张图讲清楚:Manus的技术架构
前端·javascript·面试
小鱼人爱编程13 分钟前
AI🔥助我!三分钟实现丐版前后端注册登录需求
前端·后端·deepseek
独立开阀者_FwtCoder14 分钟前
【CSS】2327- CSS view():JavaScript 滚动动画的终结
前端·javascript·github
盛夏绽放15 分钟前
uni-app 状态管理深度解析:Vuex 与全局方案实战指南
前端·javascript·uni-app
Mr_兔子先生16 分钟前
2025盛夏版:Next.js15+Antd5开发部署SSR网站速通教程
前端·react.js·next.js
Sherlock Ma23 分钟前
CSS零基础入门笔记:狂神版
前端·css·程序人生·跳槽·css3·学习方法·改行学it
化作晚霞25 分钟前
JVM有什么调优参数?
java·开发语言·jvm
浩哲Zhe33 分钟前
Java Web 之 Tomcat 100问
java·前端·tomcat