Posted in C/C++,未分类 2 条留言

在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++更相信程序员还是什么其它的?以后再说。

January 17, 2012