理解內存分配機制是編寫高質量、高性能、無 Bug C/C++ 代碼的基石。本文系統梳理程序內存五大區域、棧堆差異、以及常被忽視但至關重要的內存對齊規則。
malloc/free, new/delete)。空間大、靈活,但易產生內存碎片、洩漏,分配效率較低。static)。程序啟動分配,程序結束釋放。未初始化的默認為 0。const 修飾的全局常量。通常為只讀,防止意外修改。#include <stdio.h>
#include <stdlib.h>
int global_var; // 全局區/靜態區
void func(int param); // 代碼區
int main() {
int local_var; // 棧區
static int static_var; // 全局區/靜態區
char *str_literal = "hello"; // 指針在棧,字符串在常量區
char *heap_ptr = (char*)malloc(10); // 指針在棟,內容在堆區
printf("global: %p\n", &global_var);
printf("local: %p\n", &local_var);
printf("static: %p\n", &static_var);
printf("str_literal: %p\n", str_literal);
printf("heap_ptr: %p\n", heap_ptr);
free(heap_ptr);
return 0;
}
| 維度 | 棧 | 堆 |
|---|---|---|
| 分配方式 | 系統自動 | 程序員手動 |
| 空間大小 | 小、固定 (1~2MB) | 大、受限於虛擬內存 |
| 生長方向 | 向低地址 | 向高地址 |
| 碎片問題 | 無 (連續) | 易產生碎片 |
| 效率 | 極高 (指針移動) | 較低 (鏈表遍歷/系統調用) |
| 生命週期 | 作用域結束自動銷毀 | 手動釋放,否則洩漏 |
硬件平台要求:某些 CPU 只能從特定地址讀取特定類型(如 ARM 早期不支持非對齊訪問會拋異常)。
性能優化:即使支持非對齊訪問(x86),跨邊界讀取需兩次總線週期,拼湊高低字節,效率大幅下降。對齊是空間換時間的經典博弈。
char=1, short=2, int/float=4, double=8 (視平台/編譯器)。#pragma pack(n) 指定 (n=1,2,4,8,16)。min(自身對齊值, 指定對齊值)。成員按此值對齊。// 案例 1: 成員順序影響大小
struct A { int a; char b; short c; }; // 大小 8 (4+1+1填充+2)
struct B { char b; int a; short c; }; // 大小 12 (1+3填充+4+2+2填充)
// 案例 2: #pragma pack(2) 強制 2 字節對齊
#pragma pack(2)
struct C { char b; int a; short c; }; // 大小 8 (1+1填充+4+2, 整體對齊 2)
#pragma pack()
// 案例 3: #pragma pack(1) 緊湊模式
#pragma pack(1)
struct D { char b; int a; short c; }; // 大小 7 (無填充)
#pragma pack()
// 案例 4: 聯合體對齊以最大成員為準,但受 pack 影響
union E { int a[5]; char b; double c; }; // 大小 24 (double 8 字節對齊,5*4=20 -> 24)
offset % 有效對齊值 == 0。#pragma pack(n) 影響範圍直到下一個 #pragma pack() 恢復默認。掌握內存佈局與對齊,能有效避免「訪問非法內存」、「結構體大小預期不符」、「網絡協議/文件格式解析錯誤」等棘手問題。