堆栈与内存管理
堆栈(Stack) :
后进先出(LIFO) 线性数据结构 包含压栈(Push) ,弹栈(Pop)
用途:临时存储数据(函数调用,局部变量)
管理:由系统自动分配和回收 速度快 ,容量有限!
堆栈代码示例:
//堆栈示例 :局部变量
void getText()
{int text=20;//储存在堆栈中
}
内存管理:
程序运行时内存的分配,使用和释放
静态内存(编译时分配)
堆栈内存(自动管理):高地址向低地址增长 生命周期与函数调用绑定
堆内存(动态分配) : 手动管理 ,灵活性高, 易引发内存泄漏或碎片 malloc ,free 等函数实现动态管理 低地址向高地址增长 手动控制生命周期 和 释放 适用于储存大小不确定或生命周期长的数据
堆内存代码示例:
//堆内存 示例
void getText()
{
int*text=(int*)malloc(sizeof(int));//手动分配
*text=20;//把指针指向内存地址中的值修改为20
free(text);//手动释放
text=NULL;//避免野指针}
小拓展(重点必看):
delete[] arr; // C++释放数组指针指向的是内存地址
*text=20;// 是把指针指向内存地址 的 值修改为20 地址并没改变!!!
text=20; //是把指针地址改为20,可能会导致错误(20地址可能未分配)详细说明:
分配后:
text → 0x1000 [未初始化]*text = 20 操作后:
text → 0x1000 [值为20]text = 20 操作后:
text → 0x0014 [非法地址] 程序崩溃特殊例子:
int *arr = malloc(10 * sizeof(int));
for (int i = 0; i < 10; i++) {arr[i] = i * 2; // 等价于 *(arr+i) = i*2
}防止指针误操作:
const int* ptr; // 指向的数据不可变
int* const ptr; // 指针本身不可变
常见问题
栈溢出:递归深度过大,局部变量占用过多空间 解决办法:优化递归或用堆内存 内存泄漏: 未释放堆内存,需确保 malloc 与 free 配对使用 或用(智能指针)C++ 堆内存 野指针: 释放后 未正确把指针设置为NULL
普通指针野指针:指针初始化时应明确指向有效内存或设为
nullptr
。在指针指向的对象被销毁后(如局部变量离开作用域),应避免继续使用该指针。避免返回局部变量的指针
C语言内存泄漏的检测与处理
危害:程序崩溃,性能下降
检测方法:
1.静态检测方法
使用工具分析代码 :Cppcheck、Clang Static Analyzer、Coverity
举例使用静态工具:
//控制台输入 使用 cppcheck 工具分析
cppcheck --enable=all ./your_code.c
2.动态检测方法
使用工具分析运行:Valgrind(Linux/macOS),AddressSanitizer(ASan)(GCC/Clang)
举例使用动态工具:
// 使用valgrind 输出泄漏内存的调用栈
valgrind --leak-check=full ./your_program
//使用AddressSanitizer 输出泄漏位置和代码行
gcc -fsanitize=address -g your_code.c -o output
./output
3.自定义检测封装函数(调试时使用!)
#include <stdio.h>
#include <stdlib.h>
#define MALLOC(size) _malloc_debug(size, __FILE__, __LINE__) void* _malloc_debug(size_t size, const char* file, int line) { void* p = malloc(size); //调用malloc进行内存分配,分配结果保存在指针 p 中printf("output %zu bytes at %s:%d\n", size, file, line); return p;
}
//这是一个指针函数的宏定义
//传递的参数为(请求分配的内存字节数,调用的源文件名,调用的源代码行号)
//使用方法:将所有使用 malloc 的地方替换为 MALLOC 宏:
//举例:int* arr = (int*)MALLOC(10 * sizeof(int));
处理方法:
构建统一清理函数,在可能发生异常的代码段前提前清理
void cleanup(int* a, FILE* f) {if (a) free(a);//清理堆内存if (f) fclose(f);//关闭文件
}void risky_operation() {int* data = malloc(100);FILE* file = fopen("test.txt", "r");if (file == NULL) {//如果文件打开失败 或者不存在 执行以下清除处理cleanup(data, NULL); //调用函数return; //返回函数调用时位置}cleanup(data, file); //不管前面是否被处理这里强制清理
}
总结:
确保每次
malloc
/calloc
后均有对应的free
,复杂逻辑可通过注释标记释放位置。