【问题标题】:Casting NULL to void in C++在 C++ 中将 NULL 转换为 void
【发布时间】:2026-02-06 07:40:01
【问题描述】:

我遇到了一个令人费解的符号:

if(ptr != (void)(NULL)) {
    //some action
}

所以它扩展到

if(ptr != (void)((void *)0)) {
    //some action
}

这至少看起来很奇怪。 这背后真的有任何理由吗,还是根本没有意义,甚至是错误的?不过它编译得很好(在 linux / gcc 上,不记得版本)。

-编辑-

我今天检查了该代码,这是新信息: 首先,在宏中使用了强制转换,并且它扩展为

return (void)((void*)0);

在返回 void 的函数中。 该代码在(Red Hat 4.1.2-50)上使用 gcc (GCC) 4.1.2 20080704 编译。 那么,这个语句是否等同于

return void;

相当于

return;

或者还有更多的东西吗?

【问题讨论】:

  • 我很惊讶这甚至完全编译 - 你不应该能够比较 void 类型的值。你确定这不是void * 的演员表吗?
  • 绝对肯定,这就是我感到困惑的原因。
  • 是的,根本无法编译
  • 能否请您提供您使用的编译器?这在我的 gcc 版本上无法编译。
  • 是的,如果这不能编译,auto ptr = (void)((void *)0);那么你的问题中遗漏了一些东西

标签: c pointers null void


【解决方案1】:

我已经检查了这个案例的组件。

有这样的功能:

void x() {
    return (void)(NULL);
}

将预处理器运行为:gcc x.c -E -o x.E -Wall -Wextra 结果:

void x() {
    return (void)(((void *)0));
}

这似乎很奇怪。无论如何,用gcc x.c -o x -Wall -Wextra编译后,程序集是(来自objdump -d x):

0804841d <x>:
 804841d:   55                      push   %ebp
 804841e:   89 e5                   mov    %esp,%ebp
 8048420:   90                      nop
 8048421:   5d                      pop    %ebp
 8048422:   c3                      ret    

这和汇编完全一样:

void x() {
    return;
}

所以这两个表达式是等价的。

我正在使用 gcc version 4.8.2 (2013),用于 64 位 linux,所以它不是旧 gcc 版本的错误。

但是,将 -pedantic 添加到 gcc 会产生警告:

警告:ISO C 禁止在返回 void [-Wpedantic] 的函数中使用表达式“返回”

return (void)(NULL);

所有方言都会出现这种情况:c89c99gnu89gnu99

【讨论】:

    【解决方案2】:

    它可能应该是一个 void 指针,它与“指针”(即指向任何类型的指针)相同:

    if (ptr != (void *)(NULL)) {
        ...
    }
    

    虽然演员表是不必要的:

    if (ptr != NULL) {
        ...
    }
    

    您的代码甚至无法在我的系统(带有 GCC 4.2.1 的 Mac)上编译,GCC 报告的错误消息是 void value not ignored as it ought to be。这是意料之中的,因为void 意味着根本没有值/类型,你无法将其与其他任何东西进行比较。

    C99 标准是这样说的:

    §6.3.2.2 无效

    1 void 表达式(具有 void 类型的表达式)的(不存在的)值不应以任何方式使用,并且不应将隐式或显式转换(除了 void)应用于此类表达式。如果任何其他类型的表达式被评估为 void 表达式,则其值或指示符将被丢弃。 (计算 void 表达式的副作用。)

    §6.5.9 等式运算符

    约束

    2 应满足以下条件之一:

    • 两个操作数都有算术类型;
    • 两个操作数都是指向兼容类型的合格或不合格版本的指针;
    • 一个操作数是指向对象或不完整类型的指针,另一个是指向限定或非限定版本的 void 的指针;或
    • 一个操作数是一个指针,另一个是一个空指针常量。

    §6.3.2.2 已经禁止使用void,同样会违反§6.5.9 中的约束。

    因此,如果编译器允许ptr != (void)(NULL),那么它将违反 C99 标准。这可能只是旧 GCC 版本的错误或功能错误。

    我只找到了 C89 的草稿,这里的 void 部分与 C99 中的几乎相同。但是关于等式运算符的部分让我摸不着头脑。它似乎允许与void 进行比较,但这可能是草稿中的错误或措辞问题:

    §3.2.2.2 无效

    空表达式的(不存在的)值(一个表达式 类型为 void) 不得以任何方式使用,无论是隐式的还是显式的 转换(除了无效)不应适用于这样的 表达。如果任何其他类型的表达式出现在上下文中 在需要 void 表达式的情况下,其值或指示符为 丢弃。 (计算 void 表达式的副作用。)

    §3.3.9 等式运算符

    约束

    应满足以下条件之一:

    • 两个操作数都有算术类型;
    • 两个操作数都是指向兼容类型的合格或不合格版本的指针;
    • 一个操作数是指向对象或不完整类型的指针,另一个是 void 的限定或非限定版本;或
    • 一个操作数是一个指针,另一个是一个空指针常量。

    【讨论】:

    • 这并没有解释它是如何为 OP 编译的。
    • 这可能是早期 GCC 版本的错误/错误功能,但即便如此似乎也很可疑。 C99 标准 §6.3.2.2 说:不得以任何方式使用 void 表达式(具有 void 类型的表达式)的(不存在的)值,并且不得以任何方式使用隐式或显式转换(除了 void)应用于这样的表达。如果任何其他类型的表达式被评估为 void 表达式,则其值或指示符将被丢弃。 (评估 void 表达式的副作用。) 此外,void 类型将违反第 6.5.9(2) 节中相等运算符的约束。