看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《wtchen (沒有存在感的人)》之銘言: : 以下是板工的小小心得,準備補充13誡用。 : 若是有不對的地方,請各位鞭小力點 /(_ _)\ : (板工很懶得上色,以後會放一份在板工新blog) 你已經懂得查資料和規格書了,所以這次回文就不提規格書。 我把回文重點放在其它地方。 : ### 在C的情況(注意:C++不適用,C++的場合請看下一節) ### : 根據C11 standard 6.3.2.3 裡對空指標常數 (null pointer constant)的定義: : An integer constant expression with the value 0, or such an expression cast : to type void *, is called a null pointer constant. : 也就是說,(void*)0 是空指標常數(null pointer constant), : 而(int *)0 是型別為整數指標的空指標(null pointer),這兩者是不一樣的。 : 比較明顯的分別是,空指標常數可以被設成任何型別指標的左值, : 而空指標只能被設成同樣型別指標的左值。 : 以下是一個比較清楚的例子: : int *a = 0; // 沒問題,空指標常數 : int *b = (int*) 0; // 沒問題,左值是同型別的空指標 : int *c = (void*) 0; // C沒問題,左值是空指標常數,不過C++會掛 : int *d = (long*) 0; // 爆了,左值跟右值不同型態 上面這行在 C 不會爆炸,這也是 C 之所以被 C++ 爸爸討厭的原因之一。 C 本身對這種不相容指標是相當寬容的,只會給你 warning 而已。 clang 3.4.1: warning: incompatible pointer types initializing 'int *' with an expression of type 'long *' gcc 5.1.0: warning: initialization from incompatible pointer type 這種警告訊息就算不打開 -Wall 都一樣會跳出來,目的是提醒這寫法可能有問題。 在開發階段使用 -Wall -Werror 將警告訊息全部修掉,是軟體工程師該有的態度。 因為 -Werror 會將 warning 變成 error,因此每個 warning 都逃不掉。 只是因為 warning 這東西很看 compiler 跟版本,所以 release 出去會拿掉 -Werror。 畢竟有些 compiler 會實驗一些比較新的警告功能,然後就會產生誤報。 有些使用者會使用更嚴格的 compiler,會產生 warning 的條件更多,所以不能留著。 但是在開發期間,這個選項還是該被打開,這是避免程式出錯的最基本原則之一。 另外,左值 (lvalue) 和右值 (rvalue) 是有既定成俗的中英對照。 你這邊想表達的是 LHS 跟 RHS,使用左值和右值是不恰當的。 : 這邊有另一個例子: : typedef void (*func)(void); // func f = void (*f)(); : func a = 0; // 沒事,a是空指標常數 : func b = (void *)0; // C 沒問題,C++會掛 : func c = (void *)(void *)0; // C跟C++都看不懂 : ### 在C++的情況 ### : 根據C++14 standard 4.10 裡對空指標常數 (null pointer constant)的定義: : A null pointer constant is an integer literal (2.13.2) with value zero : **or**(以後為C++11後特有) a prvalue of type std::nullptr_t. : 意思是說,它可以是0或 nullptr,所以以下的情況: : int x = NULL; // C++ 可能沒問題(視版本而定),不過會造成誤解 可能沒問題就是有問題的,你應該明確地叫大家不要這樣寫。 就像你貼的規格書定義,它在不同的 compiler 有不同的可能性。 因此這行丟到 gcc 和 clang 去開 C++11 mode 就會爆炸了。 clang 3.4.1: error: cannot initialize a variable of type 'int' with an rvalue of type 'nullptr_t' gcc 5.1.0: error: cannot convert 'std::nullptr_t' to 'int' in initialization 我在用 C++11 模式編譯數百個 open source 專案的過程裡,這算是常踩到的錯誤之一。 所以我對這種寫法超級不爽,原本不該寫在 C++ 的東西就不該出現,沒有寬容的餘地。 實際上這在 C++11 以前也是不好的 code。 因為就算不用 C++11 mode,即使不開 -Wall 也會有警告訊息。 clang 3.4.1: warning: implicit conversion of NULL constant to 'int' gcc 5.1.0: warning: converting to non-pointer type 'int' from NULL : int* ptr = (void*)0; // C 沒問題,C++會掛 : int* ptr = nullptr; // C++11以後的正統用法, : 也就是上述C++14 standard裡的空指標常數 : **int x = NULL** 為啥會在C++裡造成誤解? : 因為C++有C沒有的函數重載(function overloading),舉例來說 : void DoSomething(int x); : void DoSomething(char* x); : - NULL = 0: DoSomething(NULL)會呼叫void DoSomething(int x),即 : DoSomething(0)。 : - NULL=nullptr: 會呼叫void DoSomething(char* x),即DoSomething(nullptr)。 : 結論就是,C++11以後還是儘量用nullptr取代NULL吧! 事實上在 C++11 以前,C++ 的爸爸就很不喜歡使用 NULL: http://www.stroustrup.com/bs_faq2.html#null C++ 的爸爸很早之前就叫大家直接在 C++ 寫 0 代表空指標,而不是寫成 NULL。 因此用 0 取代 NULL 這件事是要對從 C 轉到 C++11 以前的人說的。 用 nullptr 取代 0 則是要對習慣舊 C++ 標準要轉到 C++11 以後標準的人說的。 這個拆成兩部分來說明會比較好。 : ### 參考資料 ### : - [comp.lang.c FAQ list · Question 5.2 ](http://c-faq.com/null/null2.html) : - [Is (int *)0 a null pointer?] : (http://stackoverflow.com/questions/21386995/is-int-0-a-null-pointer) : - [Why are NULL pointers defined differently in C and C++?] : (http://stackoverflow.com/questions/7016861/ : why-are-null-pointers-defined-differently-in-c-and-c) -- Ling-hua Tseng ([email protected]) Andes Technology Corporation Compiler Group of RD/Software Division Homepage: http://blog.tinlans.org -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.132.55.117 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1461824349.A.B47.html
james732: 推 04/28 14:34
Frozenmouse: 受教 <(_ _)> 04/28 15:08
mabinogi805: 受教了!<(_ _)> 04/28 15:14
BlazarArc: 推 04/28 15:15
wtchen: 感謝,受教了!<(_ _)> 04/28 15:42
wtchen: 已經有修改版了,請繼續鞭 /(_ _)\ 04/28 18:58
Caesar08: 推 04/29 10:09