当前位置: 首页 > news >正文

C语言动态内存管理(下)

欢迎拜访:雾里看山-CSDN博客
本篇主题:C语言动态内存管理(下)
发布时间:2025.3.18
隶属专栏:C语言

在这里插入图片描述

目录

  • 动态内存常见错误
    • 内存泄漏(Memory Leak)
      • 典型场景
      • 后果
      • 解决方案
    • 悬挂指针(Dangling Pointer)
      • 错误示例
      • 深层原理
      • 修复方案
    • 双重释放(Double Free)
      • 危险操作
      • 系统表现
      • 防护措施:
    • 越界访问(Out-of-Bounds)
      • 典型错误
      • 内存影响
    • 未初始化访问
      • 问题代码
      • 随机性危害
      • 防御性编程
    • 释放栈区内容
      • 错误示例
      • 解决方案
    • 未全部释放
      • 错误示例
      • 解决方案
  • C/C++程序的内存开辟

动态内存常见错误

内存泄漏(Memory Leak)

典型场景

void process_data() {
    int* buffer = malloc(1024 * sizeof(int));
    // 忘记调用 free(buffer)
}

后果

  • 程序持续运行时会不断消耗内存
  • 长期运行的服务可能因此崩溃

解决方案

  • 遵循申请与释放成对出现原则
  • 使用RAII模式(C可用__attribute__((cleanup))扩展)

悬挂指针(Dangling Pointer)

错误示例

int* create_int() {
    int value = 42;
    return &value; // 返回局部变量地址
}

int main() {
    int* ptr = create_int();
    printf("%d", *ptr); // 不可预测结果
}

深层原理

  • 栈帧销毁后,原地址可能被其他数据覆盖
  • 堆内存释放后若未置空,指针仍保存失效地址

修复方案

及时释放,释放后立即置空

free(ptr);
ptr = NULL; // 立即置空

双重释放(Double Free)

危险操作

对同一地址空间进行多次释放

char* str = malloc(64);
free(str);
free(str);

系统表现

  • 可能立即引发segmentation fault
  • 可能破坏堆管理结构导致后续malloc失败

防护措施:

  • 使用释放后置空的编程规范
  • 在复杂逻辑中明确资源所有权

越界访问(Out-of-Bounds)

典型错误

数组只开辟了[0,4]的空间,访问时却访问了下标为5的空间。

int* arr = malloc(5 * sizeof(int));
for(int i=0; i<=5; i++) { // 索引0-4有效
    arr[i] = i; // i=5时越界
}

内存影响

  • 可能覆盖相邻内存的控制信息
  • 可能修改其他变量值导致逻辑错误

未初始化访问

问题代码

内存开辟出来未进行初始化就直接访问。

int* p = malloc(sizeof(int));
printf("%d", *p);

随机性危害

  • 可能意外修改关键内存区域
  • 在不同运行环境中表现不一致

防御性编程

指针定义时显示初始化, 指针使用时进判空。

int* p = malloc(sizeof(int));
*p = 10;
printf("%d", *p);

释放栈区内容

错误示例

对于非动态开辟出来的内存进行释放

void test()
{
 int a = 10;
 int *p = &a;
 free(p);
}

解决方案

  • 遵循申请与释放成对出现原则
  • 使用RAII模式(C可用__attribute__((cleanup))扩展)

未全部释放

错误示例

在释放内存的时候,创建时的指针和原来的指针已经不在同一个位置,直接释放则会有内存错误。

void test()
{
 int *p = (int *)malloc(100);
 p++;
 free(p);
}

解决方案

在创建的时候,使用一个指针记录起始位置,

void test()
{
 int *p = (int *)malloc(100);
 int *ptr=p;
 p++;
 free(ptr);
}

C/C++程序的内存开辟

在这里插入图片描述
C/C++程序内存分配的几个区域:

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
  3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁所以生命周期变长。

⚠️ 写在最后:以上内容是我在学习以后得一些总结和概括,如有错误或者需要补充的地方欢迎各位大佬评论或者私信我交流!!!

相关文章:

  • 18.封装红黑树实现mymap和myset
  • luogu「EZEC-10」打分 --- Python3 解法
  • libaom 源码分析:scalable_decoder.c 文件
  • 村民信息管理系统
  • SpringBoot可以同时处理多少请求?
  • 使用 Arduino 的基于物联网的空气污染监测系统
  • 群体智能优化算法-爱情进化算法 (Love Evolution Algorithm, LEA,含Matlab源代码)
  • 51c自动驾驶~合集24
  • RCore学习记录002
  • AI时代API安全挑战加剧,解读API防护的最佳方案
  • 浅谈鸿蒙跨平台开发框架ArkUI-X
  • Nginx 代理访问一个 Web 界面时缺少内容
  • S32K144入门笔记(十六):ADC的API函数解读
  • 洛谷 P3986 斐波那契数列
  • 【SoC基础】单片机常用总线
  • 银行数字化转型
  • Superagent 异步请求:如何处理复杂的 HTTP 场景
  • 【ARM】Linux内核驱动之ADC驱动开发,MAX11617驱动示例
  • 【技术】浏览器自动化框架的演变洞察
  • 安卓开发调用本地接口以及设置base_url思路
  • 国家统计局:中美大幅降低关税有利于双方贸易增长,也有利于世界经济复苏
  • 全球前瞻|特朗普19日将与俄乌总统分别通话,英国脱欧后首开英欧峰会
  • 读懂城市|成都高新区:打造“人尽其才”的“理想之城”
  • 国宝归来!子弹库帛书二、三卷抵达北京
  • 新修订的《餐饮业促进和经营管理办法》公布,商务部解读
  • 广西壮族自治区党委副书记、自治区政府主席蓝天立接受审查调查