C++语言定义中说,每一种指针类型都有一个特殊值----"空指针"。
空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数;而未初始化的指针则可能指向任何地方。
空指针不是野指针。每种指针类型都有一个空指针,而不同类型的空指针内部表示可能不尽相同。尽管程序员不必知道内部值,但编译器必须时刻明确需要哪种类型的空指针,以便在需要时加以区分。
1)怎样在程序里获得一个空指针。
根据语言定义,在指针上下文中的常数0,会在编译时转换为空指针。也就是说,在初始化、赋值或者比较的时候,如果一边是指针类型的值或表达式,编译器可以确定另一边的常数0为空指针并生成正确的空指针值。如下所示:
char *p=0; if(p!=0)
然而,传入函数的参数不一定当做指针环境,因为编译器可能不能识别未加修饰的0表示“指针”。
在函数调用的上下文中,生成空指针需要明确的类型转换,强制把0看成指针。例如,UNIX系统可以调用execl接受变长的以空指针结束的字符指针参数,它应该采取如下的正确调用:
execl("/bin/sh","sh","-c","date",(char* )0);
如果省略最后一个参数的(char* )转换,则编译器无法知道这是一个空指针,而把它当做0。如果在作用域内有函数原型,则参数传递变为“赋值上下文”,从而可以省略类型转换;这是因为,函数原型会告知编译器这里需要指针,使之把未加修饰的0正确转换为适当的指针。
最好是在函数调用时,对所有的空指针进行类型转换。
2)使用“if(p)” 检查空指针是否可靠
如果空指针的内部表达不是0将会怎样?当C语言在表达式中要求布尔值时,如果表达式等于0则认为该值为假,否则为真。换言之,只要写出
if(expr)
无论expr是任何表达式,编译器本质上都会把它作如下处理:
if(expr != 0)
如果用指针p 代替expr,则if(p) 等价于 if(p != 0)。
在此比较上下文中,编译器可以看出 0实际上是一个空指针常数,并使用正确的空指针值。这里没有任何欺骗,编译器就是这样工作的,并为二者生成完全一样的代码。空指针的内部表达无关紧要。
所以,类似于if(p) 这样的“缩写”尽管完全合法,但被一些人认为是不好的风格。
3)NULL是什么,它是怎样定义的
作为一种风格,很多人不愿意在程序里看到到处出现未加修饰的 0,因此编译器在stdio.h 头文件中定义了预处理宏 NULL为空指针常数。
/* Define NULL pointer value */ #ifndef NULL #ifdef __cpluscplus #define NULL 0 #else #define NULL ((void *) 0) #endif #endif
通过定义可以看出,NULL和0 其实没有太大的差别。编译时预处理器会把所有的NULL 都还原为 0,而编译还是按照上下文的描述出来指针上下文的 0。特别是在函数调用里,NULL之前的类型转换还是需要的。
【延伸阅读】
在使用非全零为空指针内部表达的机器上,NULL是如何定义的?
(3.1)跟其他机器一样:定义为0 (或某种形式的 0)。
(3.2)当程序员请求一个空指针时,无论写"0"还是“NULL”,都由编译器生成适合机器的二进制表达形式。因此,在空指针的内部表达不为 0的机器上定义NULL 为 0 跟其他机器一样合法:编译器在指针上下文看到的未加修饰的 0 都会生成正确的空指针。
4)如果 NULL和 0作为空指针常数是等价的,到底该用哪一个?
许多程序员认为指针上下文都应该使用NULL,以表明该值应该被看做指针。另一些人认为用一个宏定义 0,只会把事情搞的更复杂,他们倾向于使用未加修饰的 0 。
C程序员应该明白,在指针上下文中 NULL 和 0完全等价,而未加修饰的 0也可以完全接受。任何使用 NULL的地方都应该看作一种温和的指针提示,然而程序员并不能依靠它来区分指针0 和 整数0。
【最佳实践】虽然 NULL和 0具有相同的功能,但建议使用NULL 替代 0。这种实践有两个好处:
(4.1)你可以认为 NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL会比 0 有更好的兼容性,但事实并非如此。
(4.2)尽管符号常量经常代替数字,以备数字的变化,但这不是NULL 替代 0 的原因。语言本身确保了源码中的 0(用于指针上下文)会生成空指针。NULL只是用作一种格式习惯。
5)NULL可以确保是零,但空指针却不一定零
空指针的内部(或者运行前)表达式可能不完全是零,而且对不用的指针类型可能不一样。真正的值只有编译器的开发者才关心。C++程序的作者永远看不到它们,这一点不用担心,明白就好。
【注意】
(5.1)空指针不一定是0,而NULL肯定是0.
(5.2)赋值为空指针的变量,可确保变量不指向任何对象或函数。合理地使用空指针可以有效地避免内存泄露,提高程序的执行效率。