結構體作為函數參數傳遞,是 C 語言日常開發高頻操作。但「值傳遞還是指針傳遞?指針是否初始化?誰負責分配內存?」這幾個問題若不清晰,輕則邏輯錯誤,重則段錯誤崩潰。本文通過三個版本的迭代重構,演示從錯誤到正確的完整思考路徑。
typedef struct {
INT8 szEmployeeName[100];
UINT16 iEmployeeAge;
UINT32 iEmployeeNo;
} TEmployeeInfo;
// 目標函數:填充員工信息
INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo);
INT32 main(void) {
TEmployeeInfo *ptEmployeeInfo = NULL; // 只有指針變量,無實體內存!
INT32 iRetValue = GetEmployeeInfo(ptEmployeeInfo); // 傳入 NULL
// ...
}
INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo) {
if (ptEmployeeInfo == NULL) return -1; // 防護生效,返回錯誤
// 填充邏輯...
}
結果:函數內部檢測到 NULL,返回 -1,主函數打印錯誤。

圖1 版本一:指針為空,無法賦值
教訓:聲明指針 ≠ 分配內存。TEmployeeInfo *p 只是在棧上分配了 4/8 字節存地址,指向何處未知 (此處顯式置 NULL)。
嘗試在調用前對指針指向的內容賦值:
INT32 main(void) {
TEmployeeInfo *ptEmployeeInfo = NULL;
// 直接解引用 NULL 指針寫入!未分配內存!
strncpy((char*)ptEmployeeInfo->szEmployeeName, "Di Renjie", ...);
ptEmployeeInfo->iEmployeeAge = 150;
// ...
GetEmployeeInfo(ptEmployeeInfo); // 傳入同一指針
}
結果:程式運行時觸發段錯誤或內存訪問違規。

圖2 版本二:出現內存問題
教訓:ptEmployeeInfo 仍是野指針/NULL,解引用寫入屬未定義行為。同一指針不能同時指向兩塊不同內存 (這裡邏輯混淆:主函數想預填,子函數想覆寫,實則都無合法內存)。
INT32 main(void) {
TEmployeeInfo tEmployeeInfo = {0}; // 棧上分配實體結構體,自動初始化為 0
INT32 iRetValue = GetEmployeeInfo(&tEmployeeInfo); // 取地址傳遞
if (iRetValue != 0) return -1;
printf("員工信息: 姓名=%s, 年齡=%d, 工號=%d\n",
tEmployeeInfo.szEmployeeName,
tEmployeeInfo.iEmployeeAge,
tEmployeeInfo.iEmployeeNo);
return 0;
}
結果:正確輸出函數填充的數據。

圖3 版本三:執行結果正確
T t; func(&t) 或堆上 malloc),所有權清晰。if (ptr == NULL) return ERR,防禦性編程鐵律。T t = {0}; 或 memset(&t, 0, sizeof(t)),避免未初始化臨時值干擾邏輯。若結構體生命週期超出函數棧框,需堆分配:
TEmployeeInfo *p = malloc(sizeof(TEmployeeInfo));
if (!p) return -1;
GetEmployeeInfo(p);
// ... 使用 p ...
free(p); // 調用方負責釋放,所有權不轉移
若函數內部分配並返回 (工廠模式),需文檔明確「調用方擁有所有權,需 free」。