C++教程:NULL指針、零指針、野指針

C++教程:NULL 指針、零指針、野指針

創(chuàng)新互聯(lián)專注于新津縣網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供新津縣營銷型網(wǎng)站建設(shè),新津縣網(wǎng)站制作、新津縣網(wǎng)頁設(shè)計、新津縣網(wǎng)站官網(wǎng)定制、成都小程序開發(fā)服務(wù),打造新津縣網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供新津縣網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

1. 空指針、NULL指針、零指針

1.1什么是空指針常量

0、0L、”、3 – 3、0 * 17 (它們都是“integer constant expression”)以及 (void*)0 (我覺得(void*)0應(yīng)該算是一個空指針吧,更恰當一點)等都是空指針常量(注意 (char*) 0 不叫空指針常量,只是一個空指針值)。至于系統(tǒng)選取哪種形式作為空指針常量使用,則是實現(xiàn)相關(guān)的。一般的 C 系統(tǒng)選擇 (void*)0 或者 0 的居多(也有個別的選擇 0L);至于 C++ 系統(tǒng),由于存在嚴格的類型轉(zhuǎn)化的要求,void* 不能象 C 中那樣自由轉(zhuǎn)換為其它指針類型,所以通常選 0 作為空指針常量(C++標準推薦),而不選擇 (void*)0。

1.2 什么是空指針

如果 p 是一個指針變量,則 p = 0; p = 0L; p = ”; p = 3 – 3; p = 0 * 17; 中的任何一種賦值操作之后(對于 C 來說還可以是 p = (void*)0;), p 都成為一個空指針,由系統(tǒng)保證空指針不指向任何實際的對象或者函數(shù)。反過來說,任何對象或者函數(shù)的地址都不可能是空指針。(比如這里的(void*)0就是一個空指針。把它理解為null pointer還是null pointer constant會有微秒的不同,當然也不是緊要了)。其實空指針只是一種編程概念,就如一個容器可能有空和非空兩種基本狀態(tài)。

1.3 NULL 指針

NULL 是一個標準規(guī)定的宏定義,用來表示空指針常量。因此,除了上面的各種賦值方式之外,還可以用 p = NULL; 來使 p 成為一個空指針。

(很多系統(tǒng)中的實現(xiàn):#define NULL (void*)0,與這里的“a null pointer constant”并不是完全一致的)

C++標準庫定義的NULL指針

// Define NULL pointer value

#ifndef NULL

# ifdef __cplusplus

# define NULL 0

# else

# define NULL ((void *)0)

# endif

#endif // NULL

NULL是一個宏,在C++里面被直接被定義成了整數(shù)立即數(shù)類型的0,而在沒有__cplusplus定義的前提下,就被定義成一個值是0的void *類型指針常量。

1.4 零指針

零值指針,是值為0的指針,可以是任何一種指針類型,可以是通用變體類型void*,也可以是char*,int*等等。

在C++里面,任何一個概念都要以一種語言內(nèi)存公認的形式表現(xiàn)出來,例如std::vector會提供一個empty()子函數(shù)來返回容器是否為空,然而對于一個基本數(shù)值類型(或者說只是一個類似整數(shù)類型的類型)我們不可能將其抽象成一個類(當然除了auto_ptr等只能指針)來提供其詳細的狀態(tài)說明,所以我們需要一個特殊值來做為這種狀態(tài)的表現(xiàn)。

C++標準規(guī)定,當一個指針類型的數(shù)值是0時,認為這個指針是空的。(我們在其他的標準下或許可以使用其他的特殊值來定義我們需要的NULL實現(xiàn),可以是1,可以是2,是隨實現(xiàn)要求而定的,但是在標準C++下面我們用0來實現(xiàn)NULL指針)

1.5 空指針向了內(nèi)存的什么地方(空指針的內(nèi)部實現(xiàn))?

標準并沒有對空指針指向內(nèi)存中的什么地方這一個問題作出規(guī)定,也就是說用哪個具體的地址值(0x0 地址還是某一特定地址)表示空指針取決于系統(tǒng)的實現(xiàn)。我們常見的空指針一般指向 0 地址,即空指針的內(nèi)部用全 0 來表示(zero null pointer,零空指針);也有一些系統(tǒng)用一些特殊的地址值或者特殊的方式表示空指針(nonzero null pointer,非零空指針),具體請參見C FAQ。

在實際編程中不需要了解在我們的系統(tǒng)上空指針到底是一個 zero null pointer 還是 nonzero null pointer,我們只需要了解一個指針是否是空指針就可以了——編譯器會自動實現(xiàn)其中的轉(zhuǎn)換,為我們屏蔽其中的實現(xiàn)細節(jié)。注意:不要把空指針的內(nèi)部表示等同于整數(shù) 0 的對象表示——如上所述,有時它們是不同的。

1.6 對空指針實現(xiàn)的保護政策

既然我們選擇了0作為空的概念,在非法訪問空的時候我們需要保護以及報錯。因此,編譯器和系統(tǒng)提供了很好的政策。

我們程序中的指針其實是WINDOWS內(nèi)存段偏移后的地址,而不是實際的物理地址,所以不同的程序中的零值指針指向的同一個0地址,其實在內(nèi)存中都不是物理內(nèi)存的開端的0,而是分段的內(nèi)存的開端,這里我們需要簡單介紹一下WINDOWS下的內(nèi)存分配和管理制度:

WINDOWS下,執(zhí)行文件(PE文件)在被調(diào)用后,系統(tǒng)會分配給它一個額定大小的內(nèi)存段用于映射這個程序的所有內(nèi)容(就是磁盤上的內(nèi)容)并且為這個段進行新的偏移計算,也就是說我們的程序中訪問的所有NEAR指針都是在我們“自家”的段里面的,當我們要訪問FAR指針的時候,我們其實是跳出了“自家的院子”到了他人的地方,我們需要一個段偏移地址來完成新的偏移(人家家里的偏移)所以我們的指針可能是OE02:0045就是告訴系統(tǒng)我們要訪問0E02個內(nèi)存段的0045好偏移,然后WINDOWS會自動給我們找到0E02段的開始偏移,然后為我們計算真實的物理地址。

所以程序A中的零值指針和程序B中的零值指針指向的地方可能是完全不同的。

保護政策:

我們的程序在使用的是系統(tǒng)給定的一個段,程序中的零值指針指向這個段的開端,為了保證NULL概念,系統(tǒng)為我們這個段的開頭64K內(nèi)存做了苛刻的規(guī)定,根據(jù)虛擬內(nèi)存訪問權(quán)限控制,我們程序中(低訪問權(quán)限)訪問要求高訪問權(quán)限的這64K內(nèi)存被視作是不容許的,所以會必然引發(fā)Access Volitation 錯誤,而這高權(quán)限的64K內(nèi)存是一塊保留內(nèi)存(即不能被程序動態(tài)內(nèi)存分配器分配,不能被訪問,也不能被使用),就是簡單的保留,不作任何使用。

我們在直接定義一個指針后并不知道這個指針指向何處(而不是有些程序員認為的如同JAVA等語言會自動零值初始化),所以我們一旦非法地直接訪問這些未知地內(nèi)容時,極其有可能會觸碰到程序所不能觸碰地內(nèi)存(這時類似64K限制地保護政策又會起效,就如同你不僅隨意闖入了陌生人的家(野指針),而且拿著刀子要問他要錢(訪問),警察(WINDOWS內(nèi)存訪問保護政策)當然請你去警察局(報錯)談?wù)?,所以養(yǎng)成良好的指針初始化(賦值為NULL)以及使用FREE(或者時DELETE)之后立即再初始化為空是十分必要的!

1.7 為什么通過空指針讀寫的時候就會出現(xiàn)異常?

NULL指針分配的分區(qū):其范圍是從 0x00000000到0x0000FFFF。這段空間是空閑的,對于空閑的空間而言,沒有相應(yīng)的物理存儲器與之相對應(yīng),所以對這段空間來說,任何讀寫操作都是會引起異常的??罩羔樖浅绦驘o論在何時都沒有物理存儲器與之對應(yīng)的地址。為了保障“無論何時”這個條件,需要人為劃分一個空指針的區(qū)域,固有上面NULL指針分區(qū)。

1.8 是否可以定義自己的 NULL 的實現(xiàn)?

NULL 是標準庫中的一個reserved identifier (保留標識符)。所以,如果包含了相應(yīng)的標準頭文件而引入了 NULL 的話,則再在程序中重新定義 NULL 為不同的內(nèi)容是非法的,其行為是未定義的。也就是說,如果是符合標準的程序,其 NULL 的值只能是 0,不可能是除 0 之外的其它值,比如 1、2、3 等。

1.9 malloc 函數(shù)在分配內(nèi)存失敗時返回 0 還是 NULL?

malloc 函數(shù)是標準 C 規(guī)定的庫函數(shù)。在標準中明確規(guī)定了在其內(nèi)存分配失敗時返回的是一個 “null pointer”(空指針)。對于空指針值,一般的文檔(比如 man)中傾向于用 NULL 表示,而沒有直接說成 0。但是我們應(yīng)該清楚:對于指針類型來說,返回 NULL 和 返回 0 是完全等價的,因為 NULL 和 0 都表示 “null pointer”(空指針)。(一般系統(tǒng)中手冊中都返回NULL)

C++里面的NEW再內(nèi)存失敗是會拋出一個BAD_ALLOC異常。

2. 野指針

“野指針”不是NULL指針,是指向“垃圾”內(nèi)存的指針。

2.1 “野指針”的成因主要有兩種

1)指針變量沒有被初始化。任何指針變量剛被創(chuàng)建時不會自動成為NULL指針,它的缺省值是隨機的,它會亂指一氣。所以,指針變量在創(chuàng)建的同時應(yīng)當被初始化,要么將指針設(shè)置為NULL,要么讓它指向合法的內(nèi)存。例如:

char *p = NULL;

char *str = (char *) malloc(100);

2)指針p被free或者delete之后,沒有置為NULL,讓人誤以為p是個合法的指針。

free和delete只是把指針所指的內(nèi)存給釋放掉,但并沒有把指針本身干掉。free以后其地址仍然不變(非NULL),只是該地址對應(yīng)的內(nèi)存是垃圾,p成了“野指針”。如果此時不把p設(shè)置為NULL,會讓人誤以為p是個合法的指針。如果程序比較長,我們有時記不住p所指的內(nèi)存是否已經(jīng)被釋放,在繼續(xù)使用p之前,通常會用語句if (p != NULL)進行防錯處理。很遺憾,此時if語句起不到防錯作用,因為即便p不是NULL指針,它也不指向合法的內(nèi)存塊。

char *p = (char *) malloc(100);

strcpy(p, “hello”);

free(p); // p 所指的內(nèi)存被釋放,但是p所指的地址仍然不變

if(p != NULL) // 沒有起到防錯作用

{

strcpy(p, “world”); // 出錯

}

3)指針操作超越了變量的作用范圍。這種情況讓人防不勝防,示例程序如下:

class A

{

public:

void Func(void){ cout << “Func of class A” << endl; }

};

void Test(void)

{

A *p;

{

A a;

p = &a; // 注意 a 的生命期 ,只在這個程序塊中(花括號里面的兩行),而不是整個test函數(shù)

}

p->Func(); // p是“野指針”

}

函數(shù)Test在執(zhí)行語句p->Func()時,對象a已經(jīng)消失,而p是指向a的,所以p就成了“野指針”。

網(wǎng)站欄目:C++教程:NULL指針、零指針、野指針
網(wǎng)站路徑:http://www.muchs.cn/article44/gcephe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)面包屑導(dǎo)航、商城網(wǎng)站、動態(tài)網(wǎng)站、App開發(fā)、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)