堆棧與內存管理
堆棧(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
,復雜邏輯可通過注釋標記釋放位置。