在最近学习中,在网上偶然间看到了这样一段代码,大致意思如下:
for( i = 0; s[i] != NULL ; i++)
printf(“%c”,s[i]);
当时心中顿生疑惑, str 明明是一个字符数组,最后应该是以 ’\0’ 为结束标志才是,怎么可能来用 NULL 进行判断呢?加上最近看到的各种奇葩的类型,什么 0 , (void *)0 , NUL ,立感混乱。于是在网上查了一下这些 “ 兄弟们 ” ,整理出来。虽然自己还分得清它们各用在什么场合,但其中道理还真是不知道。
先从简单的开始入手吧。
一、’\0’与NULL
就是上面的例子,自己写了一段小程序来进行试验:
#include <stdio.h>
#include <string.h>
int main( int argc,char *argv[] )
{
int i = 0;
char str[10];
strcpy(str,"hello");
while(str[i] != NULL)
{
printf("%c",str[i]);
i++;
}
printf("\n");
return 0;
}
在用 gcc 进行编译时,会报出来一个警告:
警告: 比较指针和整数 [默认启用]
但是仍可以执行,而且运行结果完全正确:
hello
用clang编译器进行编译时,它给出了更为详细的警告:
warning: comparison between pointer and integer ('int' and 'void *')
比较指针与整数,不用说,其中的(void *)型肯定是NULL了。由此可见,NULL是一个无类型指针,并且其值为0。它的作用就是用于指示一个指针为空,即什么都不指。
二、0与NULL
在标准宏定义头文件stddef.h中,有关于NULL的如下定义:
#include <stdio.h>
int main( int argc,char *argv[] )
{
if( 0 == NULL)
printf("0 == NULL\n");
return 0;
}
本以为编译时会同样报出来一个不能比较指针与整数的警告,结果用gcc却顺利编译通过,加上-Wall也能编译通过,就连一向严谨的clang也顺利编译通过。而且,最重要的是,它竟然打印出下面的结果来:
0 == NULL
在百度百科中看到下面这段话,觉得还比较好来解释:
NULL与0有着扯不断理还乱的关系,其实不那么高深。0本身有着一些原生的特性诸如:起始,没有,正负的分界线,0/1中的0还可以表示否定。NULL正需要这些特性中的一部分。 我们只需要清楚NULL的本质,并在合理的地方才加以利用而非滥用。譬如我想做一个比较某个整数i是否为0,难道写成i==NULL会比i==0更酷一点吗?
的确,研究0与NULL的关系也没什么必要,估计也没有人会把i=0写成i=NULL。
三、NUL
不是在咬文嚼字,这货我确实见过,确定不是NULL。虽然只比NULL少了一个‘L’,但是意义却大不相同。第一次见到这个奇怪的东西是在《C和指针》一书中。原文是这样说的:
“字符串就是一串以NUL字节结尾的字符。NUL是作为字符串终止符,它本身并不是字符串的一部分”
字符串终止符?!那不就是'\0'嘛!这里怎么说成了NUL?
还好书下还有这样一段注释,看了之后恍然大悟:
“NUL是ASCII字符集中'\0'的名字,它的字节模式为全0。NULL指一个其值为0的指针。它们都是整型值,其值也相同。所以它们是可以互换使用。然而,你还是应该使用适当的常量,因为它能告诉阅读程序的人不仅使用0这个值,而且告诉他们使用这个值的目的。”
但是还是要注意一点,NUL在C/C++中是没有定义的,如果要使用还需要自己宏定义#define NUL '\0'。而这一点在《C与指针》一书竟没有提到,感觉会给读者造成一定的误解。不过个人觉得还是用'\0'来的更直接明白些,'\0'就是'\0',如果写成NUL,反倒更加难以理解了,弄不好还会和NULL不小心用混。
在google中还找到了一些关于这些符号的比较权威些的解释:
NULL is a macro defined in several standard headers, 0 is an integer constant, '\0' is a character constant, and nul is the name of the character constant. All of these are *not* interchangeable:
NULL is to be used for pointers only since it may be defined as ((void *)0)
, this would cause problems with anything but pointers.
0 can be used anywhere, it is the generic symbol for each type's zero value and the compiler will sort things out.
'\0' should be used only in a character context.
nul is not defined in C or C++, it shouldn't be used unless you define it yourself in a suitable manner, like:
#define nul '\0'
其中这句话,“All of these are not interchangeable”,看来也不能说的这样绝对,至少在本文开关的例子中,'\0'和NULL就成功地互用了。如果这样来说应该更准确些:不建议去互用这些符号——原因就像《C和指针》里那样说,它能告诉阅读程序的人不仅使用0这个值,而且告诉他们使用这个值的目的:
NULL就是使一个指针“空悬”;
'\0'就是用来标志字符串结束;
0就是0。
正所谓,到什么山,唱什么歌,毕竟用NULL来当字符串结束标志,用0来让指针空悬,不是一件看起来很舒服的事,虽然它们可以正常编译通过。