上週在解決一道課後習題的時候,偶然間發現了一個自己從未注意過的問題,問題描述如下:
在遍歷一個循環鏈表時,我發現在我調用free()函數刪除了一個節點之後,仍然能用printf打印出原先的數據,起初我以爲是巧合,並未加以注意。今天我又嘗試free其他節點,之後仍然能夠通過printf打印出原先的值,這個問題立刻引起了我的注意。下面將出現問題的代碼附在下方,供各位查看:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int status;
typedef int elemtype;
typedef struct node{
elemtype data;
struct node * next;
}node;
typedef struct node * linkqueue;
int main(void){
linkqueue rear,pmove,head,qmove;
int i=0;
pmove=rear=(linkqueue)malloc(sizeof(node));
if(!pmove||!rear)
return ERROR;
head=pmove;
/*建立循環鏈表,並讓0~9入隊*/
for(i=0;i<10;i++){
qmove=(linkqueue)malloc(sizeof(node));
qmove->data=i;
rear->next=qmove;
rear=qmove;
}
rear->next=head;
pmove=head->next;
printf("將0~9入隊後\n");
while(pmove!=head){
printf("%d ",pmove->data);
pmove=pmove->next;
}
printf("\n");
/*出隊算法*/
pmove=head->next;
printf("出隊元素爲:%d\n",pmove->data);
linkqueue smove=pmove->next;
free(pmove);
***printf("%d ",pmove->data);/*注意這行代碼,這是問題之所在*/***
pmove=smove;
printf("出隊後,遍歷隊列\n");
while(pmove!=head){
printf("%d ",pmove->data);
pmove=pmove->next;
}
printf("\n");
}
從理論上看,free之後不應該打印出原數值,但事實是,我加粗的那行代碼成功地打印出了原數值,在我google之後,得到了這樣的解答:
“你在free(p)之後,最好加上p = NULL;
要不然容易導致野指針。你在free(p)之後,你只是使用
if(p != NULL)
你想的是用來進行防止誤用操作對吧。。
你進入了一個誤區,誤認爲free(p)之後,p就指向了NULL,而其實不然。
free(p)的言外之意就是告訴編譯器:大家注意啦哈,這塊內存我現在不用了,你們誰想用就拿去用哈。而p在這裏你可以完全理解成就是這塊內存的地址,也就是告訴編譯器,這塊內存現在不被佔用了,而裏面的內容此時就是我們所說的“垃圾”,因爲作爲主人的我已經丟棄它了,裏面的內容就是不可控的。
注意p是個地址,你沒有強行置爲空,那還是原來的那個值。只是裏面的內容不受控啦,有可能不會變,有可能會被改寫,而結果是未知的。”
之前在知乎上總是看到大牛們大書特書C語言內存管理機制的缺陷,野指針的種種危害。卻從未想過究竟什麼是“野指針”,今天遇到的這個問題就是野指針帶來的,看來自己對C指針的認識還不夠深刻,還需要更深層次的學習,特地寫下此文,加以反思。
看來水平離熟練使用C還差很遠,靜下心繼續反思吧。
2016-04-27 晨
於教學實驗綜合樓