不同於 PHP 等高階語言,C 函數無法直接回傳區域陣列內容:
// PHP 可直接回傳陣列
function refunc() { return ["foo" => "bar"]; }
// C 語言錯誤示範
char* str() {
char str2[10] = "askdfjk"; // 區域陣列在堆疊
return str2; // 回傳失效位址!
}
堆疊記憶體生命週期:
| 區域 | 用途 | 管理方式 | 生命週期 |
|---|---|---|---|
| 程式碼區 | 函數二進制碼 | 系統 | 程式結束 |
| 全域/靜態區 | 全域變數、static、常數 | 系統 | 程式結束 |
| 常數字串區 | 字串字面值 | 系統 | 程式結束 |
| 堆疊區 | 區域變數、參數、返回位址 | 編譯器自動 | 函數結束銷毀 |
| 堆積區 | malloc/new 配置 | 程式員手動 | free/delete 釋放 |
char* str() {
static char str2[10] = "askdfjk"; // 靜態儲存區
return str2; // 安全:記憶體持久存在
}
// 缺點:非執行緒安全、重入不安全、大小固定
// 呼叫端配置記憶體,函數填充
void get_string(char *buf, size_t size) {
snprintf(buf, size, "hello");
}
int main() {
char buf[100];
get_string(buf, sizeof(buf));
// 最安全、最彈性、無記憶體洩漏風險
}
char* str() {
char *str2 = malloc(10 * sizeof(char)); // 堆積區
strcpy(str2, "ababab");
return str2; // 安全:堆積記憶體持久
}
int main() {
char *p = str();
printf("%s", p);
free(p); // 必須手動釋放!
}
// 區域陣列版本組合碼片段
_str:
pushl %ebp
movl %esp, %ebp
subl $16, %esp // 配置堆疊空間
movl $1684763489, -10(%ebp) // "askd"
movl $7039590, -6(%ebp) // "fjk"
movw $0, -2(%ebp) // \0
leal -10(%ebp), %eax // 回傳堆疊位址!
leave
ret
關鍵點:leal -10(%ebp), %eax 取的是當前堆疊幀位址,函數結束後 leave 恢復堆疊指標,該記憶體即失效。
// 錯誤:指標指向字串常數,而非複製內容
char* str() {
char *str2 = malloc(10);
str2 = "ababab"; // 丟失 malloc 位址 → 記憶體洩漏!
return str2; // 回傳唯讀常數區位址
}
// 正確:複製內容到動態記憶體
char* str() {
char *str2 = malloc(10);
strcpy(str2, "ababab");
return str2;
}
struct String { char *data; size_t len; } 管理動態字串struct { size_t len; char data[]; }aligned_alloc、free 標準化bstrlib、sds (Redis 動態字串)