我在@jsantander评论中链接到的论坛帖子中读到了这个说法:
请记住,当您将指针指定或比较为零时,会在幕后发生一些特殊的魔法,以便为给定指针使用正确的模式(实际上可能不为零).这就是为什么之类的东西的原因之一
#define NULL (void*)0是邪恶的-如果比较char*到NULL那个魔法已明确(可能不知不觉)关闭,无效结果可能发生.只是为了清楚:Run Code Online (Sandbox Code Playgroud)(my_char_ptr == 0) != (my_char_ptr == (void*)0)
所以我理解它的方式,对于一个架构,其中NULL指针,比如0xffff代码if (ptr),将比较ptr 0xffff而不是0.
这是真的吗?它是用C++标准描述的吗?
如果为true,则意味着即使对于具有非零NULL指针值的体系结构,也可以安全地使用0.
编辑
作为额外的澄清,请考虑以下代码:
char *ptr;
memset(&ptr, 0, sizeof(ptr));
if ((ptr == (void*)0) && (ptr != 0)) {
printf("It can happen.\n");
}
Run Code Online (Sandbox Code Playgroud)
这就是我理解这个论坛帖子的主张.
M.M*_*M.M 34
你的问题有两个部分.我将从:
如果为true,则意味着即使对于具有非零NULL指针值的体系结构,也可以安全地使用0.
你正在混淆"价值"和"代表".该值一个空指针就是所谓的空指针值.该表示法是被用来存储该值在存储器中的比特.空指针的表示可以是任何东西,不要求它是全位零.
在代码中:
char *p = 0;
Run Code Online (Sandbox Code Playgroud)
p保证是一个空指针.它可能没有全位零.
这不再是代码的"魔力":
float f = 5;
Run Code Online (Sandbox Code Playgroud)
f没有与int相同的表示形式(内存中的位模式)5,但没有问题.
C++标准定义了这一点.在C++ 11中,文字有所改变,增加了nullptr; 但是在C和C++的所有版本中,0转换为指针类型时的整数文字会生成空指针.
从C++ 11开始:
甲空指针常数是整数类型的整数表达式prvalue计算结果为零或类型的std的prvalue :: nullptr_t.空指针常量可以转换为指针类型; 结果是该类型的空指针值,并且可以与对象指针或函数指针类型的每个其他值区分开.这种转换称为空指针转换.
0是一个空指针常量,(char *)0例如是类型的空指针值char *.
空指针是否具有全零位零是无关紧要的.重要的是,当将值的整数constexpr转换0为指针类型时,保证生成空指针.
转到问题的另一部分. 你引用的文字是彻头彻尾的垃圾.如上所述,在类型之间的转换导致不同的表示形式的想法中没有"魔力".
my_char_ptr == NULL保证代码测试是否my_char_ptr为空指针.
如果你用自己的源代码编写,那将是邪恶的#define NULL (void*)0.这是因为定义可能由标准头定义的任何宏是未定义的行为.
但是,标准头文件可以编写任何他们喜欢的内容,以满足空指针的标准要求.编译器可以在标准头代码中"做魔术"; 例如,不必iostream在文件系统上调用文件; 编译器可以查看#include <iostream>然后硬编码标准要求iostream发布的所有信息.但是出于显而易见的实际原因,编译器通常不会这样做; 它们允许独立团队开发标准库.
无论如何,如果C++编译器#define NULL (void *)0在其自己的头中包含,并且因此发生了不符合的情况,那么编译器显然是不符合的.如果没有任何不符合要求,那么就没有问题.
我不知道你引用的文字是谁会引导它的"是邪恶的"评论.如果是针对编译器供应商告诉他们不要"邪恶"并且推出不合格的编译器,我想我们不能与之争论.
Ste*_*sop 12
我认为您链接的论坛帖子不正确(或者我们误解了它的含义!=).这两个子表达式具有不同的语义,但结果相同.假设my_char_ptr确实有类型char*或类似,并且有效值:
my_char_ptr == 0转换0为.的类型my_char_ptr.这产生了一个空指针,因为它0是所谓的"空指针常量"的一个例子,它在标准中定义.然后比较两者.当且仅当my_char_ptr是空指针时,比较为真,因为只有空指针比较等于其他空指针.
my_char_ptr == (void*)0转换my_char_ptr为void*,然后将其与转换0为void*(即空指针)的结果进行比较.当且仅当my_char_ptr是一个空指针时,比较结果为真,因为当您将指针转换为void*结果时,如果且仅当源是空指针时,该指针是空指针.
空指针是否用0位表示的问题很有意思,但与代码的分析无关.
认为NULL是空指针(而不仅仅是空指针常量)的实际危险是您可能认为printf("%p", NULL)已经定义了行为,或者foo(NULL)将调用void*重载foo而不是int重载,依此类推.
不,因为他们无意中使用了唯一可以保证作为例子的情况.
否则,是的.
尽管从某种程度上说,你可能永远不会看到差异,但严格地说,关注是正确的.
C++标准要求(4.10):
0,或者是一个类型的prvalue std::nullptr_t)转换为任何类型的空指针.这意味着,如果你是迂腐有关的措辞,认为的空指针void和char和foo_bar,不仅不一定零种模式,但也有不一定相同.只有相同类型的空指针必然是相同的(实际上,甚至不是这样,它只表示它们必须比较相等,这不是同一个东西).
它明确地说"将空指针值转换为目标类型的空指针值"这一事实表明这不仅是措辞的荒谬理论扭曲,而且确实是作为实现的合法特征.
这与同一文字0将转换为每种类型的空指针的事实无关.
在他们的例子中,他们比较了void*,由于上述转换规则,它将起作用.此外,在实践中,每种类型的空指针都是您生活中可能遇到的每个架构上的零位模式(当然,这不是保证).
首先,(charPtr == 0) != (charPtr == (void*)0)即使在C++中,我也不确定是否允许这样做.在这两种情况下,您都将空指针constant(0)转换为指针,从而产生空指针.并且所有空指针都应该相等.
其次,虽然我不知道你举通道的情况下,你真的不担心NULL之中(void*)0:用户代码不能合法地界定NULL(至少不是它是否包括任何标准的头),而C++标准要求
NULL,以被定义为空指针常量; 即一个常量积分表达式,求值为0.(注意,尽管它的名称,空指针常量不能有指针类型.)所以它可能是0(或多或少的标准定义,从C的最开始),或者可能0L,甚至(1-1),但不是((void*)0).(当然,它也可能类似于__nullptr编译器内置常量,它计算为整数0,但如果不立即转换为空指针则触发警告.
最后:没有要求空指针具有全0位,并且肯定存在不是这种情况的情况.另一方面,要求将空指针与空指针常量进行比较将评估为true; 它取决于编译器使其工作.并且因为NULL需要被定义为空指针常量,无论您使用NULL还是0纯粹是个人偏好和约定的问题.
编辑:
只是为了澄清一点:关键点涉及转换"空指针常量",一个评估为0的整数常量表达式.可以让人惊讶的是:
int zero = 0; // NOT a constant expression.
void* p1 = reinterpret_cast<void*>( zero );
void* p2 = 0;
if ( p1 == p2 ) // NOT guaranteed!
Run Code Online (Sandbox Code Playgroud)
将计算结果为零的非常量表达式转换为指针的结果不能保证为空指针.