臭名昭著的空指標到底是什麼?
語言定義中說明, 每一種指標型別都有乙個特殊值 --- "空指標" --- 它與同型別的其它所有指標值都不相同, 它 "與任何物件或函式的指標值都不相等"。也就是說, 取位址操作符 & 永遠也不能得到空指標, 同樣對 malloc() 的成功呼叫也不會返回空指標, 如果失敗, malloc() 的確返回空指標, 這是空指標的典型用法: 表示 "未分配" 或者 "尚未指向任何地方" 的指標。
空指標在概念上不同於未初始化的指標。空指標可以確保不指向任何物件或函式; 而未初始化指標則可能指向任何地方。
如上所述, 每種指標型別都有乙個空指標, 而不同型別的空指標的內部表示可能不盡相同。儘管程式設計師不必知道內部值, 但編譯器必須時刻明確需要那種空指標, 以便在需要的時候加以區分
怎樣在程式裡獲得乙個空指標?
根據語言定義, 在指標上下文中的常數 0 會在編譯時轉換為空指標。也就是說, 在初始化、賦值或比較的時候, 如果一邊是指標型別的值或表示式, 編譯器可以確定另一邊的常數 0 為空指標並生成正確的空指標值。因此下邊的**段完全合法:
char *p = 0;if(p != 0)
然而, 傳入函式的引數不一定被當作指標環境, 因而編譯器可能不能識別未加修飾的 0 「表示" 指標。在函式呼叫的上下文中生成空指標需要明確的型別轉換, 強制把 0 看作指標。例如, unix 系統呼叫 execl 接受變長的以空指標結束的字元指標引數。它應該如下正確呼叫:
execl("/bin/sh", "sh", "-c", "date", (char *)0);如果省略最後乙個引數的 (char *) 轉換, 則編譯器無從知道這是乙個空指標, 從而當作乙個 0 傳入。(注意很多 unix 手冊在這個例子上都弄錯了。)
如果範圍內有函式原型, 則引數傳遞變為 "賦值上下文", 從而可以安全省略多數型別轉換, 因為原型告知編譯器需要指標, 使之把未加修飾的 0 正確轉換為適當的指標。函式原型不能為變長引數列表中的可變引數提供型別。
在函式呼叫時對所有的空指標進行型別轉換可能是預防可變引數和無原型函式出問題的最安全的辦法。
摘要:
可以使用未加修飾的 0:
需要顯示的型別轉換:
初始化函式呼叫, 作用域內無原型
賦值變參函式呼叫中的可變引數
比較固定引數的函式呼叫且在作用域內有原型
用縮寫的指標比較 "if(p)" 檢查空指標是否可靠?
如果空指標的內部表達不是 0 會怎麼樣? 當 c 在表示式中要求布林值時, 如果表示式等於 0 則認為該值為假, 否則為真。換言之, 只要寫出
if(expr)無論 "expr" 是任何表示式, 編譯器本質上都會把它當
if((expr) != 0)處理。
如果用指標 p 代替 "expr" 則
if(p) 等價於 if(p != 0)。而這是乙個比較上下文, 因此編譯器可以看出 0 實際上是乙個空指標常數, 並使用正確的空指標值。這裡沒有任何欺騙; 編譯器就是這樣工作的, 並為、二者生成完全一樣的**。空指標的內部表達無關緊要。
布林否操作符 ! 可如下描述:
!expr
本質上等價於
(expr)?0:1
或等價於
((expr) == 0)
從而得出結論
if(!p)
等價於if(p == 0)
類似 if(p) 這樣的 "縮寫", 儘管完全合法, 但被一些人認為是不好的風格
null 是什麼, 它是怎麼定義的?
作為一種風格, 很多人不願意在程式中到處出現未加修飾的 0。因此定義了預處理巨集 null (在 和其它幾個標頭檔案中) 為空指標常數, 通常是 0 或者 ((void *)0) 。希望區別整數 0 和空指標 0 的人可以在需要空指標的地方使用 null。
使用 null 只是一種風格習慣; 預處理器把所有的 null 都還原回 0, 而編譯還是依照 上文的描述處理指標上下文的 0。特別是, 在函式呼叫的引數裡, null 之前 (正如在 0 之前) 的型別轉換還是需要。
摘要:
可以使用未加修飾的 0:
需要顯示的型別轉換:
初始化函式呼叫, 作用域內無原型
賦值變參函式呼叫中的可變引數
比較固定引數的函式呼叫且在作用域內有原型
的**對 0 和 null 都有效 (帶修飾的 null 和帶修飾的 0 完全等價)。
null 只能用作指標常數;
在使用非全零作為空指標內部表達的機器上, null 是如何定義的?
跟其它機器一樣: 定義為 0 (或某種形式的 0;
當程式設計師請求乙個空指標時, 無論寫 "0" 還是 "null", 都是有編譯器來生成適合機器的空指標的二進位制表達形式。因此, 在空指標的內部表達不為 0 的機器上定義 null 為 0 跟在其它機器上一樣合法:編譯器在指標上下文看到的未加修飾的 0 都會被生成正確的空指標。
如果 null 定義成 #define null ((char *)0) 難道不就可以向函式傳入不加轉換的 null 了嗎?
一般情況下, 不行。複雜之處在於, 有的機器不同型別資料的指標有不同的內部表達。這樣的 null 定義對於接受字元指標的的函式沒有問題, 但對於其它型別的指標引數仍然有問題 (在缺少原型的情況下), 而合法的構造如
file *fp = null;則會失敗。
不過, ansi c 允許 null 的可選定義
#define null ((void *)0)除了潛在地幫助錯誤程式執行 (僅限於使用同樣型別指標的機器, 因此幫助有限) 以外, 這樣的定義還可以發現錯誤使用 null 的程式 (例如, 在實際需要使用 ascii nul 字元的地方)。
無論如何, ansi 函式原型確保大多數 (儘管不是全部; )指標參 數在傳入函式時正確轉換。 因此, 這個問題有些多餘。
如果 null 和 0 作為空指標常數是等價的, 那我到底該用哪乙個呢?
許多程式設計師認為在所有的指標上下文中都應該使用 null, 以表明該值應該被看作指標。另一些人則認為用乙個巨集來定義 0, 只不過把事情搞得更複雜, 反而令人困惑。因而傾向於使用未加修飾的 0。沒有正確的答案。
c 程式設計師應該明白, 在指標上下文中 null 和 0 是完全等價的, 而未加修飾的 0 也完全可以接受。任何使用 null (跟 0 相對) 的地方都應該看作一種溫和的提示, 是在使用指標; 程式設計師 (和編譯器都) 不能依靠它來區別指標 0 和整數 0。
在需要其它型別的 0 的時候, 即便它可能工作也不能使用 null, 因為這樣做發出了錯誤的格式資訊。(而且, ansi 允許把 null 定義為 ((void *)0), 這在非指標的上下文中完全無效。特別是, 不能在需要 ascii 空字元 (nul) 的地方用 null。如果有必要, 提供你自己的定義
#define nul '/0'但是如果 null 的值改變了, 比如在使用非零內部空指標的機器上, 難道用 null (而不是 0) 不是更好嗎?
不。(用 null 可能更好, 但不是這個原因。) 儘管符號常量經常代替數字使用以備 數字的改變, 但這不是用 null 代替 0 的原因。語言本身確保了原始碼中的 0 (用於指標上下文) 會生成空指標。null 只是用作一種格式習慣。
這有點奇怪。null 可以確保是 0, 但空 (null) 指標卻不一定?
隨便使用術語 "null" 或 "null" 時, 可能意味著以下一種或幾種含義:
概念上的空指標, 定義的抽象語言概念。它使用以下的東西實現的 ……
空指標的內部 (或執行期) 表達形式, 這可能並不是全零, 而且對不用的指標型別可能不一樣。真正的值只有編譯器開發者才關心。c 程式的作者永遠看不到它們, 因為他們使用 ……
空指標常數, 這是乙個常整數 0 。它通常隱藏在 ……
null 巨集, 它被定義為 0 ()。最後轉移我們注意力到 ……
ascii 空字元 (nul), 它的確是全零, 但它和空指標除了在名稱上以外, 沒有任何必然關係; 而……
"空串" (null string), 它是內容為空的字串 (""
)。在 c 中使用空串這個術語可能令人困惑, 因為空串包括空字元 ('/0'
),但 不包括空指標, 這讓我們繞了乙個完整的圈子 ……
本文用短語 ``空指標" (``null pointer", 小寫) 表示第一種含義, 標識 ``0" 或短語 ``空指標常數" 表示含義 3, 用大寫 null 表示含義 4。
關於空指標
空指標是什麼 c中空意味著它與任何物件與函式的指標值都不相等也就是說我們並不能通過 來得到空位址 其實這在數學邏輯上是矛盾的,但是我們暫且這麼認為 所以空指標在概念上不同於未初始化的指標,因為未初始化的指標可以指向任何地方。每種指標型別都有其空指標,不同空指標的內部表示也不盡相同,儘管程式設計師並不...
關於空指標異常問題
一直都覺得空指標異常很好改,但是今天竟然調了一上午,鬱悶吶,因此開始總結教訓 首先svo是前端所傳的物件,判斷是否為空,如果為空則進行例項化 if svo null string project num svo.getprojectnum if project num.equals root nod...
c 判斷指標為空 關於C中指標為空的判斷
在看c中指標是否為空的判斷說明時,出現了分歧。if ptr 如果 p 非空,則完成 if ptr 如果 p 為空,則完成 而我在林銳博士 c程式設計規範 一文中看到,為了和bool型別的判斷區分,他建議 判斷指標使用 if null ptr or if null ptr 這種方式。但是,按照 c 之...