Re: [心得] 空指标常数(null pointer constant)

楼主: tinlans ( )   2016-04-28 14:19:03
※ 引述《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)
作者: james732 (好人超)   2016-04-28 14:34:00
作者: Frozenmouse (*冰之鼠*)   2016-04-28 15:08:00
受教 <(_ _)>
作者: mabinogi805 (焚离)   2016-04-28 15:14:00
受教了!<(_ _)>
作者: BlazarArc (Midnight Sun)   2016-04-28 15:15:00
作者: wtchen (没有存在感的人)   2016-04-28 15:42:00
感谢,受教了!<(_ _)>已经有修改版了,请继续鞭 /(_ _)\
作者: Caesar08 (Caesar)   2016-04-29 10:09:00

Links booklink

Contact Us: admin [ a t ] ucptt.com