我想要比较两个空指针,代码如下:
void foo(void* p1, void* p2) {
if (p1 < p2) {
void *tmp = p1;
p1 = p2;
p2 = tmp;
}
// do something with p1 and p2.
}
根据标准,这样做是正确的吗?我的意思是,比较空指针是否属于定义行为?
如果有人能指出记录了这一点的C标准,我会非常感激。
我想要比较两个空指针,代码如下:
void foo(void* p1, void* p2) {
if (p1 < p2) {
void *tmp = p1;
p1 = p2;
p2 = tmp;
}
// do something with p1 and p2.
}
根据标准,这样做是正确的吗?我的意思是,比较空指针是否属于定义行为?
如果有人能指出记录了这一点的C标准,我会非常感激。
正如Drew McGowen在评论中指出的那样,但我将在此发布引用:
6.5.8 关系运算符
5 当比较两个指针时,结果取决于指向对象的地址空间中的相对位置。如果两个指向对象类型的指针都指向同一对象或都指向同一数组对象的最后一个元素之后,它们将相等。如果所指的对象是同一聚合对象的成员,则后声明的结构成员的指针与先声明的结构成员的指针进行比较,具有更大的值,并且带有更大下标值的数组元素的指针与相同数组的较低下标值的元素的指针进行比较,具有更大的值。所有指向同一联合对象的成员的指针将相等。如果表达式P指向数组对象的一个元素,并且表达式Q指向同一数组对象的最后一个元素,则指针表达式Q + 1将比P大。 在所有其他情况下,行为未定义。
这是来自C11标准的内容。 C99
也一样。
从C++11开始,它基本上是相同的。一些关于指针转换的微调,如果您想要的话,我可以全部粘贴。更重要的是,行为是未确定的(正如Chris在上面指出的那样)。
请注意,未定义的行为是致命的。如果您比较两个不相关的指针,则可能会导致您的机器着火,发射核导弹,使恶魔从您的鼻子中飞出等。
未指定的行为必须做出某种含糊有余的事情。编译器不必对其进行记录,甚至不必为两个不同的程序执行相同的操作,但它不能摧毁世界。您的程序仍然被视为有效。
因此,在编译为C的特定情况下,用户可能会根据将传递给函数的参数造成未定义的行为。这似乎非常危险。
另外,与我在问题评论中所说的相反,您不能仅对两个任意的void*
使用!=
:
6.5.9 相等运算符
2 必须满足以下一种情况:
— 两个操作数都具有算术类型;
— 两个操作数是限定或不限定版本的兼容类型的指针;
— 一个操作数是对象类型的指针,另一个是限定或不限定版本的void指针;或者
— 一个操作数是指针,另一个是空指针常量。
void
不是和 void
兼容吗? - mafsovoid
不是一个对象类型,我认为。 - BoBTFishvoid
类型包含一组空值;它是一个不完整的对象类型,无法完成。现在我完全不确定了。 - BoBTFishstruct
、union
或数组中)的指针的结果是未定义的,但我不知道有哪个平台的未定义行为比“不按您认为应该的顺序进行比较”更多。>=
,或者(B)它不在缓冲区内,因此结果是未定义的。>=
,如果是后面,则为<=
),并将其替换为常量。<
和>
始终是未定义的行为,并且执行此操作的分支可以从代码中完全消除。最初这个问题标记为c++。在C ++中,std :: less<void *>()(lhs,rhs)
保证对所有指针进行良好排序。(这是为了允许将指针排序在各种std
容器和算法中使用)。如果您在混合的C / C ++系统中工作,则可能会有用。
对于模板less,[...]任何指针类型的特化都产生严格的全序,并且在那些特化之间是一致的,并且与由内置运算符< [...]强制实施的偏序也是一致的。 对于模板专业化较少[...],如果调用运算符调用比较指针的内置运算符,则调用运算符产生严格的总序,该序列在这些专业化之间是一致的,并且还与由那些内置的操作符强制实施的偏序一致。
<
对指针施加的顺序会随时间保持一致(假设比较段偏移量是错误的原因),但我不会拿你的生命来打赌。 - Yakk - Adam Nevraumontvoid*
和指向int
的void*
可能会相等。 - Yakk - Adam Nevraumont继 @BoBTFish 之后,对于关系运算符 (C11 的 6.5.8),一些限制进一步减少:
限制2
以下两者之一必须成立:
— 两个操作数都具有实数类型;
或 — 两个操作数是指向相容对象类型的限定或未限定版本的指针。
其中一个是排除void*
(void
不是一种类型,因此void*
不是指向类型的指针)...因此如果您尝试将两个void*
指针或一个 "real" 指针和一个 void*
进行<
等比较,则您的编译器可能会发出警告。通常将其转换为(char*)即可完成工作!但像任何转换一样,如果崩溃和错误发生,没有人会很同情 :-)
顺便说一句,这个标准不是易读的,但以60美元的价格购买它是你图书馆中有用的补充http://webstore.ansi.org/RecordDetail.aspx?sku=INCITS%2FISO%2FIEC+9899-2012
C
还是C++
?当然,它们在这方面可能是相同的,我还没有检查过,但它们确实是两种非常不同的语言。我的感觉是只有在类或数组中才允许这样使用,否则必须使用!=
,但我会尝试确认一下。 - BoBTFish