在 C 语言中,只有一个上下文,必须将空指针常量显式转换为特定指针类型才能使程序正确运行。该上下文通过无类型函数参数列表传递空指针。在 modern C 中,只有当您需要将空指针传递给接受可变数量参数的函数时,才会发生这种情况。 (在传统 C 中,任何未使用原型声明的函数都会发生这种情况。)典型示例是 execl,其中最后一个参数必须是显式转换为 (char *) 的空指针:
execl("/bin/ls", "ls", "-l", (char *)0); // correct
execl("/bin/ls", "ls", "-l", (char *)NULL); // correct, but unnecessarily verbose
execl("/bin/ls", "ls", "-l", 0); // undefined behavior
execl("/bin/ls", "ls", "-l", NULL); // ALSO undefined behavior
是的,最后一个示例具有未定义的行为即使NULL 被定义为((void *)0),因为void * 和char * 在以下情况下是不隐式可相互转换的通过无类型的参数列表传递,即使它们在其他任何地方。 (C2011 中有一种语言使它们在通过 va_arg 传递时可以隐式地相互转换,但是他们忘记指定实现提供的库函数就像通过调用 va_arg 来访问可变参数,所以你只能依赖于可变参数作为程序一部分的函数。可能应该有人提交 DR。)
“幕后”,这里的问题不只是用于空指针的位模式,而是编译器可能需要知道每个参数的确切具体类型才能正确设置调用框架。 (考虑 MC68000,它具有单独的地址和数据寄存器;一些 ABI 指定要在地址寄存器中传递的指针参数,但在数据寄存器中传递整数参数。还要考虑 int 和 void * 大小不同的任何 ABI。现在它已经很少见了,但 C 仍然明确规定 void * 和 char * 大小不同。 [编辑:我不确定,但这可能不再被允许。] ) 如果有函数原型,编译器可以使用它,但非原型函数和可变参数不能提供这样的帮助。
C++ 比较复杂,我觉得没有资格解释。