本文通過具體代碼演示內存洩露的第三種常見場景:多個指針共享同一塊內存,因其中一個指針釋放該內存,導致其他指針成為懸空指針。這是鏈表等動態數據結構中極易犯的錯誤。
data 指釵指向同一塊動態分配的字符串內存 (mydata)。free(oldtmpnd->data) 釋放了共享內存。data 指針隨即指向已釋放內存,成為懸空指針。#include#include #include typedef struct listnode mynode; struct listnode { mynode *next; char *data; int number; int age; }; mynode *addnode(mynode *prevnd, int number, int age, char *data) { mynode *ndtemp = (mynode*)malloc(sizeof(mynode)); prevnd->next = ndtemp; ndtemp->number = number; ndtemp->age = age; ndtemp->data = data; // 共享同一 data 指針 ndtemp->next = NULL; return ndtemp; } mynode *initlist() { mynode *temp = (mynode*)malloc(sizeof(mynode)); temp->number = 0; temp->age = 0; temp->data = NULL; temp->next = NULL; return temp; } int main() { mynode *mylist = initlist(); mynode *mytempnd = mylist; char *mydata = (char*)malloc(100); const char *strsrc = "helloworld"; strcpy(mydata, strsrc); // 構建 3 個節點,共享 mydata for (int i = 0; i < 3; i++) { mytempnd = addnode(mytempnd, i, 20+i, mydata); } // 輸出驗證 for (mytempnd = mylist->next; mytempnd != NULL; mytempnd = mytempnd->next) { printf("id:%d, age:%d, data:%s\n", mytempnd->number, mytempnd->age, mytempnd->data); } printf("-------------------------\n"); // 錯誤刪除:僅刪除最後一個節點,但釋放了共享的 data mynode *oldtmpnd; for (mytempnd = mylist->next; mytempnd != NULL; ) { oldtmpnd = mytempnd; mytempnd = mytempnd->next; if (mytempnd == NULL) { // 最後一個節點 printf("delete id:%d\n", oldtmpnd->number); free(oldtmpnd->data); // 釋放共享內存! free(oldtmpnd); } } // 此時 mylist->next 和 mylist->next->next 的 data 均為懸空指針 return 0; }
id:0, age:20, data:helloworld id:1, age:21, data:helloworld id:2, age:22, data:helloworld ------------------------- delete id:2
data 會導致未定義行為(數據損壞、程序崩潰、安全漏洞)。free 僅標記內存可重用,數據可能暫時殘留,但隨時可能被覆蓋,不可依賴。free。data 的所有權,僅擁有者負責釋放,其他節點持有非所有權指針(或複製數據)。data 並 strcpy,各自管理生命周期(空間換安全)。共享內存是優化性能的常用手段,但必須建立清晰的所有權語義與生命周期管理機制。C 語言無 GC,程序員需自行確保「誰分配、誰釋放」或引入引用計數/智能指針模式,否則極易產生難以排查的懸空指針與內存錯誤。