在C/C++语言中,NULL是个宏。
在我的Windows机器上的stddef.h中,有如下的宏定义:
1 2 3 4 5 6 7 8 | /* Define NULL pointer value */ #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif |
在我的Linux虚拟机中的stddef.h中,有差不多的定义。
以上定义说明:在C++语言中,NULL是int型的0;在C++语言中,NULL是void *类型的0。表面上是这样,事实却不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdarg.h> #include <stdio.h> void read_string_from_va(int i, ...) { const char *p = NULL; va_list arg_ptr; va_start(arg_ptr, i); while ((p = va_arg(arg_ptr, const char *)) != NULL) { printf("%s\n", p); } va_end(arg_ptr); } int main() { read_string_from_va(10, "4", "5", "6", "7", "8", 0); } |
上面的代码在VS2010中编译运行(不论是64bit还是32bit,不论是Debug,还是release),都没问题。
但用64位的gcc/g++在64位的Linux上编译运行,会有segmentation fault. 有图为证:

我们看到,不管你把它当C代码编译,还是当然C++代码编译,运行时都会segmentation fault.
原因很简单: 0是int,而read_string_from_va()中取值的时候,把它当char *。在64bit的linux上,char *是8字节长,int是4字节长,存进去4字节却取出8字节,所以最后挂了。
对于Linux C,换成NULL就不挂了,道理很简单,在C中,NULL是指针,你存进去8字节,拿出来8字节,一切OK。
按理来说,对于Linux C++,换成NULL一样挂,因为在C++中,NULL是int型的0,但事实并非如此:

很诡异,在C++中,0就是NULL,这次为什么没挂呢?g++做了什么事?
如果运行
1 | g++ -E test0NULLL.c |
我们可以看到,g++把NULL换成了__null,也就是g++自己的一个东西。然后再看看sizeof(__null),是8,所以没挂。
看来stddef.h中的宏没起作用。
还有问题:
read_string_from_va(10, “4″, “5″, “6″, “7″, 0);
上面的代码会挂吗?为什么?这和64bit linux calling convention有关。
为什么windows上vs2010没挂?vs2010聪明还是gcc/g++更相信程序员还是什么其它的?以后再说。

这个“下回分解”真是分得好,调人胃口。
64位mac上运行结果是:
Fengs-MacBook-Pro:~ twinhorse$ gcc ttt.c
Fengs-MacBook-Pro:~ twinhorse$ ./a.out
4
5
6
7
8
Segmentation fault: 11
Fengs-MacBook-Pro:~ twinhorse$ g++ ttt.c
Fengs-MacBook-Pro:~ twinhorse$ ./a.out
4
5
6
7
8
Segmentation fault: 11
Fengs-MacBook-Pro:~ twinhorse$
第二段代码的上面一行有输入笔误
都挂了。
笔误在哪儿?