對於C語言free()函數的一些反思

上週在解決一道課後習題的時候,偶然間發現了一個自己從未注意過的問題,問題描述如下:
在遍歷一個循環鏈表時,我發現在我調用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 晨
於教學實驗綜合樓

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章