64位环境0和NULL的区别

本文探讨了C语言中NULL与0的区别,特别是在64位系统中,它们可能引发的问题。详细介绍了在使用可变参数列表时,若将0误用为NULL可能导致的程序错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0 & NULL

    在C语言中将值为0的指针作为NULL,NULL通常被定义为0或((void *)0);

有很多应该使用NULL的地方写0代替的程序,通常这样的写法也不会发生问题,如:

  char *p = 0;

  if (p != 0) ...

此时p为char *,进行运算前,会将操作数类型转换为与之有互换性的某个类型上,所以默认int型的0转换为char *型的NULL,这样一来,下面的书写方法也没有问题:

  if (p)

在此时if会用布尔值来判断,用表达式的布尔值是否为0来判断。如下所示:

  if (p != 0)

相反的条件

  if (!p)

也跟

 if(p == 0)

等价

象这样在多数情况下,用0代替NULL的处理方式也可以。但是也存在不通用的地方。

 

0和NULL不同的情况

  如前所述,NULL并不是int型的0值,而是指针型的0值(值为0的指针);也就是说编译器必须要明白是用指针理性来解释,

在x86那样的ILP32环境中由于int long指针都是32位,所以int型的0可以等同于指针的NULL。但在IA64那样的LP64环境中int仍然是32位的,但long和指针都是64位的,int的0便不等同于NULL了。使用可变长参数时,这个问题很明显。示例代码:

struct s *foo(const char *name, ...) 
{
    va_list va;
    struct s *sp;
    char *p;
    va_start(va, fmt);
    sp = (struct s *)malloc(*sp);
    if (!sp) {
        return 0;
    }
    memset(sp, 0,sizeof(*sp));
    set_name(sp, name);
    while ((p = va_arg(va, char *))) {
        set_item(sp, p);
    }
    return sp;
}

...
sp = foo("foo", "bar", 0);
...

这里的foo()是取得可变长参数后,利用name这个名称和剩下的参数表初始化item结构体的函数。这个代码在LP64环境下不能正常运行。

调用foo("foo", "bar", 0)之后,参数会像下面这样 堆积在栈上:

    为0的int

    指向"bar"的const char *

    指向"foo"的const char *

在foo()运行时,形参const char *name指向第一个参数"foo", 其他变参通过va_list来访问,在foo()中通过如下形式取va_list内容:

p = va_arg(va, char *);

也就是参数作为char *来取出,第一个是指向"bar"的指针,所以没问题。而第二个变参传的是int型(32位)的0,但读取时是按64位的char *来取的,多读取了4字节为初始化的内存数据,如果这四个字节不是全0,而导致进入下一次while循环,会发生什么后果呢?

这就是将0和NULL混用而导致的bug,正确调用foo()的方法:

sp = foo("foo", "bar", NULL);

也有自己编程使用类似函数的情况,例如:使用类似机制的execl(3)时,必须用NULL来终止参数列表(见man手册提示):

int execl(const char *path, const char *arg, .../*, (char *)0*/);

字面量 0 ,是int型,在64位系统里是32位。
宏 NULL, 定义为((void*)0),在64位系统里是64位。

在foo函数定义之后使用该函数,或在使用foo函数之前声明一下foo函数,编译器就会自动将int型的0转为void*型的NULL。

如果没有定义或声明foo函数就直接使用foo函数,编译器会根据你传入的参数产生一个隐含的声明。
调用foo("foo", "bar", 0),编译器会认为你的函数声明为int foo(char*, char*, int),并且根据此声明压栈,共20字节。

而函数实际定义为foo(char*, char *, void*),函数会把传入的第三个参数当做void*解析,16~19字节是整数0,20~23字节数据未定义,函数会取到一个错误的指针(在big-endian系统里,指针低位部分为0,高位部分未定义,即0x********00000000;在little-endian系统里,指针低位部分未定义,高位部分位0,即0x00000000********)

C++中还可以用nullptr。

 

转自:https://www.phpfans.net/ask/MTEwNjM3OQ.html

### 解决Visual Studio 64应用程序错误0xc000007b的方法 #### 修改项目配置以匹配目标架构 确保整个项目的构建配置完全针对所需的平台(即x64)。这不仅涉及更改解决方案平台,还需要验证所有依赖项是否都已正确转换为相应的64版本。如果存在任何残留的32组件,则可能导致加载失败并显示此特定错误消息[^1]。 #### 更新或调整清单文件 有时即使所有的库都已经切换到了正确的体系结构上,由于某些原因,编译器可能会尝试访问旧版资源。此时可以考虑编辑应用的`.manifest`文件来强制指定要使用的DLL路径或其他必要的参数。具体操作取决于实际开发环境以及所遇到的具体情形。 #### 验证调试与发行模式下的差异 值得注意的是,在一些案例中只有Debug版本才会遭遇这个问题而Release则能正常工作。因此建议分别测试这两种不同的构建设定,并对比两者之间的区别找出潜在的原因所在[^2]。 ```cpp // 示例:检查当前进程是否为64 #include <windows.h> #include <stdio.h> int main() { BOOL isWow64; typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); if(NULL != fnIsWow64Process){ if (!fnIsWow64Process(GetCurrentProcess(), &isWow64)) { printf("Failed to get process information\n"); return 1; } if(isWow64){ printf("The current process is running under WOW64.\n"); }else{ printf("The current process is not running under WOW64.\n"); } } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值