閱讀前輩代碼時發現:有時直接將字符串指針賦給另一指針,有時又 malloc 內存再拷貝內容。這其中門道何在?研究後發現其實很簡單,記錄如下。
若源字符串所在內存即將被釋放/覆蓋(如棧上局部數組),必須 malloc 新內存並拷貝內容;若源字符串生命週期足夠長(如靜態/全局/堆內存),直接賦值指針即可。
有時 malloc 也是為了避免修改源字符串影響副本(防止別名問題),但這屬於設計選擇,非必然。
#include <stdio.h>
#include <string.h>
typedef void(*str_cpy_cb)(char *name);
void test(str_cpy_cb cb);
void call_back(char *name);
char *test_name = NULL;
int main() {
test(call_back);
printf("name: %s\n", test_name); // 未定義行為:test_name 指向已銷毀的棧內存
return 0;
}
void test(str_cpy_cb cb) {
char myname[8] = {0};
snprintf(myname, sizeof(myname), "%s", "slk");
printf("myname: %s\n", myname);
cb(myname); // 傳遞局部數組地址
}
void call_back(char *name) {
test_name = name; // 直接賦值指針,危險!
printf("test_name: %s\n", test_name);
}
結果: main 中輸出亂碼或崩潰,因為 `myname` 是 `test` 的局部數組,函數返回後棧幀銷毀,內存無效。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef void(*str_cpy_cb)(char *name);
void test(str_cpy_cb cb);
void call_back(char *name);
char *test_name = NULL;
int main() {
test(call_back);
printf("name: %s\n", test_name); // 正確輸出 "slk"
free(test_name); // 記得釋放
return 0;
}
void test(str_cpy_cb cb) {
char myname[8] = {0};
snprintf(myname, sizeof(myname), "%s", "slk");
printf("myname: %s\n", myname);
cb(myname);
}
void call_back(char *name) {
test_name = (char *)malloc(strlen(name) + 1); // 分配新內存
snprintf(test_name, strlen(name) + 1, "%s", name); // 拷貝內容
printf("test_name: %s\n", test_name);
}
結果: 正確輸出 "slk",且 `test_name` 指向堆內存,生命週期由程序員控制。