【一天一个计算机知识】—— 【 C/C++ 内存管理与分布】

【一天一个计算机知识】—— 【C/C++ 内存管理与分布】

C/C++程序运行时,内存不是一整块随便用的,而是被操作系统和编译器严格划分成几个固定区域,每个区域有明确用途、生命周期、访问权限和增长方向。

掌握这个分布,能帮你:

  • 理解为什么栈溢出、野指针、内存泄漏那么常见
  • 知道new/delete、malloc/free、static、const、全局变量到底放哪里
  • 调试段错误(Segmentation Fault)时快速定位
  • 写高性能代码时优化内存访问

经典的C/C++进程虚拟内存布局(从高地址到低地址)

现代系统(Linux/Windows/macOS x64)典型布局如下(地址从上到下递减):

高地址
┌───────────────────────────────────────┐
│          内核空间(用户不可访问)      │
├───────────────────────────────────────┤   ← 栈顶(向下增长)
│                栈 (Stack)              │   存放:局部变量、函数参数、返回地址
│                                       │
│         (栈溢出就撞到这里了)        │
├───────────────────────────────────────┤
│                                       │
│                堆 (Heap)               │   存放:动态分配内存(new/malloc)
│         (向上增长,可能不连续)      │
│                                       │
├───────────────────────────────────────┤
│                                       │
│         未使用 / 空闲空间             │
│                                       │
├───────────────────────────────────────┤
│          BSS 段(未初始化数据)       │   未初始化的全局/静态变量 → 自动清0
│                                       │
├───────────────────────────────────────┤
│          数据段(Data / .data)       │   已初始化的全局/静态变量
│                                       │
├───────────────────────────────────────┤
│          只读数据段(.rodata)        │   字符串常量、"hello",const全局变量(部分编译器放这里)
│                                       │
├───────────────────────────────────────┤
│          代码段(Text / .text)        │   可执行指令(函数代码),只读
└───────────────────────────────────────┘   ← 低地址

一句话记忆口诀(从低到高):
代码 → 只读数据 → 数据 → BSS → 堆(向上长) → 栈(向下长) → 内核

各大区域详细对比(2025-2026主流理解)

区域存储内容生命周期管理方式增长方向典型例子权限大小限制
代码段 (Text)编译后的机器指令、函数体程序整个生命周期编译器/链接器main()、foo() 的代码只读+执行固定
只读数据 (.rodata)字符串字面量、const全局/静态变量(部分)程序整个生命周期编译器const char* s = “hello”;只读固定
数据段 (.data)已初始化的全局变量、静态变量程序整个生命周期编译时确定int global = 10; static int x = 5;读写固定
BSS段未初始化的全局/静态变量(自动清0)程序整个生命周期编译时只记大小,运行时清0int global; static int y;读写固定(文件不占空间)
堆 (Heap)动态分配的内存(new/malloc)程序员手动控制程序员(new/delete, malloc/free)向上new int[100]; malloc(1024)读写很大(受虚拟内存限制)
栈 (Stack)局部变量、函数参数、返回地址、临时对象函数调用周期(作用域结束销毁)编译器/OS 自动向下int a = 1; 在函数里读写有限(默认1-8MB,ulimit可查)

代码示例 + 位置标注

#include <iostream>
using namespace std;

const int const_global = 100;           // 可能放 .rodata(只读)
int global_init = 10;                   // 数据段 (.data)
int global_uninit;                      // BSS段(自动=0)
static int static_init = 20;            // 数据段
static int static_uninit;               // BSS段

int main() {
    int local = 30;                     // 栈
    static int local_static = 40;       // 数据段(注意!局部static放全局区)
    const char* str = "hello world";    // "hello world" → .rodata,指针本身在栈

    int* p = new int(50);               // 堆

    cout << "地址示例(实际运行看):" << endl;
    cout << "&global_init    = " << &global_init << "   // 数据段" << endl;
    cout << "&global_uninit  = " << &global_uninit << "   // BSS" << endl;
    cout << "&local          = " << &local << "   // 栈(高地址)" << endl;
    cout << "p               = " << p << "   // 堆(中间地址)" << endl;
    cout << "main            = " << (void*)main << "   // 代码段(低地址)" << endl;

    delete p;
    return 0;
}

运行后地址规律(典型Linux x64):

  • 代码段:0x400000 ~ 0x4xxxxx(最低)
  • .rodata / 数据 / BSS:0x600000 ~ 0x6xxxxx
  • 堆:从0x555xxxxx开始向上
  • 栈:从0x7ffffffexxxx向下

常见内存问题与对应区域

  • 栈溢出(Stack Overflow):递归太深、超大局部数组 → 栈空间用尽
  • 内存泄漏:new/malloc 后忘记 delete/free → 堆内存没释放
  • 野指针/悬垂指针:delete 后继续用指针 → 堆上已释放内存
  • 段错误:访问NULL、空指针、越界 → 可能踩到代码段/只读区
  • double free:同一块堆内存free两次 → 堆管理崩溃

小结一句话

C/C++内存管理的本质
静态部分(代码+数据+BSS)由编译器/链接器决定大小和位置,一劳永逸;动态部分(栈自动、堆手动)由运行时灵活控制,但也最容易出问题。

今天get了吗?
明天想看哪个?

  • new/delete vs malloc/free 底层区别
  • 智能指针如何防泄漏
  • 虚拟内存、页表、mmap
  • 栈帧详细结构(EBP/RBP、返回地址)

告诉我,继续一天一个!

文章已创建 4799

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部