C語言知識彙總 | 56-C語言NULL空指標以及void指標

Neutionwei發表於2020-10-01

一、NULL空指標

一個指標變數可以指向計算機中的任何一塊記憶體,不管該記憶體有沒有被分配,也不管該記憶體有沒有使用許可權,只要把地址給它,它就可以指向,C語言沒有一種機制來保證指向的記憶體的正確性,程式設計師必須自己提高警惕。

很多初學者會在無意間對沒有初始化的指標進行操作,這是非常危險的,請看下面的例子:

#include <stdio.h>

int main()
{
    char *str;
    gets(str);
    printf("%s\n", str);
    return 0;
}

這段程式沒有語法錯誤,能夠通過編譯和連結,但當使用者輸入完字串並按下Enter鍵時就會發生錯誤,在 Linux 下表現為段錯誤(Segment Fault),在 Windows 下程式直接崩潰。如果你足夠幸運,或者輸入的字串少,也可能不報錯,這都是未知的。

未初始化的區域性變數的值是不確定的,C語言並沒有對此作出規定,不同的編譯器有不同的實現,因此不要直接使用未初始化的區域性變數。上面的程式碼中,str 就是一個未初始化的區域性變數,它的值是不確定的,究竟指向哪塊記憶體也是未知的,大多數情況下這塊記憶體沒有被分配或者沒有讀寫許可權,使用 gets() 函式向它裡面寫入資料顯然是錯誤的。

建議對沒有初始化的指標賦值為 NULL,例如:

char *str = NULL;

NULL 是“零值、等於零”的意思,在C語言中表示空指標。從表面上理解,空指標是不指向任何資料的指標,是無效指標,程式使用它不會產生效果。

注意區分大小寫,null 沒有任何特殊含義,只是一個普通的識別符號。

很多庫函式都對傳入的指標做了判斷,如果是空指標就不做任何操作,或者給出提示資訊。更改上面的程式碼,給 str 賦值 NULL,看看會有什麼效果:

#include <stdio.h>

int main()
{
    char *str = NULL;
    gets(str);
    printf("%s\n", str);
    return 0;
}

執行程式後發現,還未等使用者輸入任何字元,printf() 就直接輸出了(null)。我們有理由據此推斷,gets() 和 printf() 都對空指標做了特殊處理:

  • gets() 不會讓使用者輸入字串,也不會向指標指向的記憶體中寫入資料;
  • printf() 不會讀取指標指向的內容,只是簡單地給出提示,讓程式設計師意識到使用了一個空指標。

我們在自己定義的函式中也可以進行類似的判斷,例如:

void func(char *p)
{
    if(p == NULL){
        printf("(null)\n");
    }else{
        printf("%s\n", p);
    }
}

這樣能夠從很大程度上增加程式的健壯性,防止對空指標進行無意義的操作。

其實,NULL 是在stdio.h中定義的一個巨集,它的具體內容為:

#define NULL ((void *)0)

(void *)0表示把數值 0 強制轉換為void *型別,最外層的( )把巨集定義的內容括起來,防止發生歧義。從整體上來看,NULL 指向了地址為 0 的記憶體,而不是前面說的不指向任何資料。

在程式的虛擬地址空間中,最低地址處有一段記憶體區域被稱為保留區,這個區域不儲存有效資料,也不能被使用者程式訪問,將 NULL 指向這塊區域很容易檢測到違規指標。

在大多數作業系統中,極小的地址通常不儲存資料,也不允許程式訪問,NULL 可以指向這段地址區間中的任何一個地址。

注意,C語言沒有規定 NULL 的指向,只是大部分標準庫約定成俗地將 NULL 指向 0,所以不要將 NULL 和 0 等同起來,例如下面的寫法是不專業的:

int *p = 0;

而應該堅持寫為:

int *p = NULL;

注意 NULL 和 NUL 的區別:NULL 表示空指標,是一個巨集定義,可以在程式碼中直接使用。而 NUL 表示字串的結束標誌 '\0',它是ASCII碼錶中的第 0 個字元。NUL 沒有在C語言中定義,僅僅是對 '\0' 的稱呼,不能在程式碼中直接使用。


二、void*指標

對於空指標 NULL 的巨集定義內容,上面只是對((void *)0)作了粗略的介紹,這裡重點說一下void *的含義。void 用在函式定義中可以表示函式沒有返回值或者沒有形式引數,用在這裡表示指標指向的資料的型別是未知的。

也就是說,void *表示一個有效指標,它確實指向實實在在的資料,只是資料的型別尚未確定,在後續使用過程中一般要進行強制型別轉換

C語言動態記憶體分配函式 malloc() 的返回值就是void *型別,在使用時要進行強制型別轉換,請看下面的例子:

#include <stdio.h>

int main()
{
    //分配可以儲存30個字元的記憶體,並把返回的指標轉換為 char *
    char *str = (char *)malloc(sizeof(char) * 30);
    gets(str);
    printf("%s\n", str);
    return 0;
}

執行結果:

https://blog.csdn.net↙
https://blog.csdn.net

 

相關文章