9、C 语言内存管理知识点总结

C 进程内存布局、各内存区域(栈、数据段、代码段、堆)的存储内容与特性、静态数据及堆内存管理等方面

C 语言内存管理核心知识点总结

一、C 进程内存布局

C 语言程序运行时,其虚拟内存(从物理内存映射而来)具有固定的布局,不同区域存储不同类型的数据,且各区域特性各异。理解内存布局是掌握内存管理的基础。

1. 虚拟内存整体结构

  • 内核区(Kernel):位于虚拟内存高端(如 0xC0000000 以上),存储操作系统核心代码,应用程序不可访问。
  • 用户区:应用程序可访问的区域,从低地址到高地址依次为:
    • 代码段:存放程序代码(用户代码、系统初始化代码)。
    • 数据段:存放静态数据(初始化 / 未初始化的全局变量、常量等)。
    • 堆(Heap):动态内存区域,由开发者手动分配和释放。
    • 栈(Stack):自动管理的内存区域,存放局部变量、函数参数等。
  • 不可访问区:低地址端(如 0x0~0x08048000),防止空指针访问有效内存。

2. 内存区域查询命令

  • ls -l 文件名:查看文件详细信息(权限、大小、修改时间等)。
  • file 文件名:查看文件格式(如 ELF 64-bit)、架构等。
  • size 文件名:查看文件在内存中的段分布(text、data、bss 的大小)。

二、栈内存(Stack)

栈是系统自动管理的内存区域,遵循 “先进后出” 原则,主要用于临时数据存储。

1. 存储内容

  • 环境变量:程序运行所需的环境配置(如编译器路径、库文件位置)。
  • 命令行参数:通过main(int argc, char *argv[])传递的参数(argc为参数个数,argv为参数列表)。
  • 局部变量:函数内部定义的变量(包括函数形参),其作用域局限于函数内部。

2. 核心特性

  • 自动分配与释放:函数调用时,栈向下增长分配内存;函数退出时,栈向上缩减释放内存,无需开发者干预。
  • 空间有限:默认大小通常为 2~10MB(如 Ubuntu 默认 8MB),可通过ulimit -s查询或修改。若申请超大局部变量(如char buf[1024*1024*10]),会导致栈溢出(Segmentation fault)。
  • 增长方向:从高地址向低地址增长。

3. 示例解析

void func(int a) {  // 形参a存储在栈区

    int b = 10;     // 局部变量b存储在栈区

}

int main() {

    func(5);        // 调用func时,栈分配a和b的内存;函数退出后,内存自动释放

    return 0;

}

三、数据段与代码段

数据段和代码段属于静态内存区域,程序加载时分配,生命周期贯穿整个程序运行过程。

1. 数据段(存放静态数据)

数据段细分为 3 个部分,均在程序加载时分配内存:

段名称

存储内容

初始化特性

示例

.bss

未初始化的静态数据

系统自动初始化为 0

int num;(全局变量)、static int a;(静态局部变量)

.data

已初始化的静态数据

保留初始化值

int num = 100;(全局变量)、static int b = 20;(静态局部变量)

.rodata

常量数据(只读)

不可修改

整型常量(100)、字符常量('a')、字符串常量("hello")

2. 代码段(存放程序代码)

  • .text 段:用户编写的函数代码(如main函数、自定义函数)。
  • .init 段:系统初始化代码,在main函数执行前运行(如堆、栈的初始化)。
  • 特性:只读(防止意外修改程序指令),大小固定。

3. 示例解析

// .data段:已初始化全局变量

int g_data = 100;

// .bss段:未初始化全局变量

int g_bss;

int main() {

    // .rodata段:字符串常量

    char *str = "hello";  // str在栈区,"hello"在.rodata段(只读)

    

    // .data段:已初始化静态局部变量

    static int s_data = 200;

    

    // .bss段:未初始化静态局部变量

    static int s_bss;

    

    return 0;

}

四、静态数据与 static 关键字

静态数据包括全局变量和静态局部变量,其生命周期与程序一致,作用域受static修饰影响。

1. 静态数据分类

  • 全局变量:定义在函数外部的变量,默认对整个工程可见。
  • 静态局部变量:函数内部用static修饰的变量,作用域局限于函数,但生命周期贯穿程序。

2. static 关键字的作用

  • 修饰局部变量:将变量从栈区(临时变量)移至数据段(静态数据),函数退出后值保留(仅初始化一次)。

示例:

int count() {

    static int c = 0;  // 静态局部变量,仅初始化一次

    c++;

    return c;

}

// 调用count()多次,返回值依次为1,2,3...(普通局部变量则始终返回1)

  • 修饰全局变量 / 函数:限制其仅在本文件可见(避免工程内命名冲突)。

示例:

// a.c文件

static int g_var = 100;  // 仅a.c可见

static void func() {}    // 仅a.c可见

// main.c文件

extern int g_var;  // 报错:无法访问a.c的static变量

extern void func(); // 报错:无法访问a.c的static函数

3. 注意事项

  • 静态数据初始化语句仅执行一次(程序加载时)。
  • 未初始化的静态数据会被系统自动置为 0。
  • 过度使用静态数据会增加程序内存占用(直至程序退出才释放)。

五、堆内存(Heap)

堆是开发者可手动管理的动态内存区域,大小仅受物理内存限制,是内存管理的核心难点。

1. 核心特性

  • 动态分配与释放:需通过 API 手动申请(malloc/calloc)和释放(free),系统不干预。
  • 增长方向:从低地址向高地址增长(与栈相反)。
  • 匿名性:无变量名,需通过指针访问。
  • 持久性:若不手动释放,会一直占用内存(直至程序退出),可能导致内存泄漏。

2. 堆内存操作 API

函数

功能

特点

示例

malloc

申请指定大小的堆内存

未初始化(内容随机),需手动清零

int *p = malloc(5 * sizeof(int));

calloc

申请指定数量和大小的堆内存

自动清零(初始化全 0)

int *p = calloc(5, sizeof(int));

bzero

清零指定内存区域

需配合malloc使用(calloc无需)

bzero(p, 5 * sizeof(int));

free

释放堆内存

仅能释放整块堆内存,释放后指针需置空

free(p); p = NULL;

3. 注意事项

  • 内存泄漏:未调用free释放不再使用的堆内存,导致内存耗尽(尤其在循环或长期运行的程序中)。
  • 野指针free后指针未置空,继续访问会导致不可预期的错误(如修改已释放的内存)。
  • 重复释放:同一堆内存被free多次,会触发段错误。
  • 越界访问:访问超出申请大小的内存(如p[5]当申请了 5 个 int 时),可能破坏堆结构。

4. 示例解析

#include <stdlib.h>

#include <strings.h>

int main() {

    // 用malloc申请堆内存(需手动清零)

    int *p1 = malloc(5 * sizeof(int));

    bzero(p1, 5 * sizeof(int));  // 清零

    for (int i = 0; i < 5; i++) {

        p1[i] = i;  // 数组形式访问

    }

    free(p1);       // 释放内存

    p1 = NULL;      // 避免野指针

    // 用calloc申请堆内存(自动清零)

    int *p2 = calloc(5, sizeof(int));

    for (int i = 0; i < 5; i++) {

        *(p2 + i) = i;  // 指针形式访问

    }

    free(p2);

    p2 = NULL;

    return 0;

}

六、各内存区域对比

内存区域

管理方式

存储内容

生命周期

大小限制

增长方向

系统自动

局部变量、函数参数

函数调用期间

较小(2~10MB)

高地址→低地址

数据段

系统自动

静态数据、常量

程序运行期间

较大(受物理内存)

固定

代码段

系统自动

程序代码

程序运行期间

固定

固定

开发者手动

动态数据

手动释放前 / 程序退出

极大(受物理内存)

低地址→高地址

总结

C 语言内存管理的核心是理解各区域的特性与边界:栈适合临时数据(自动管理),数据段和代码段适合静态数据(生命周期长),堆适合动态数据(灵活但需手动管理)。开发者需重点关注堆内存的申请 / 释放,避免内存泄漏、野指针等问题,同时合理使用static关键字控制静态数据的作用域。掌握内存布局与管理规则,是编写高效、健壮 C 程序的基础。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

長琹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值