A. *_* K. 4 c comparison pointers
我想比较两个这样的void指针:
void foo(void* p1, void* p2) {
if (p1 < p2) {
void *tmp = p1;
p1 = p2;
p2 = tmp;
}
// do something with p1 and p2.
}
Run Code Online (Sandbox Code Playgroud)
根据标准,这是正确的吗?我的意思是void指针的比较是一个明确定义的行为?
如果有人能指出我所记录的C标准,我将不胜感激.
正如Drew McGowen在评论中指出的那样,但我会在这里发布引文:
6.5.8关系运算符
5比较两个指针时,结果取决于指向的对象的地址空间中的相对位置.如果两个指向对象类型的指针都指向同一个对象,或者两个指针都指向同一个数组对象的最后一个元素,则它们相等.如果指向的对象是 同一聚合对象的成员,则指向稍后声明的结构成员的指针比指向结构中先前声明的成员的指针大,指向具有较大下标值的数组元素的指针比指向同一数组的元素的指针大.具有较低的下标值.指向同一个union对象的成员的所有指针都比较相等.如果表达式P指向数组对象的元素,并且表达式Q指向同一数组对象的最后一个元素,则指针表达式Q + 1比较大于P. 在所有其他情况下,行为是未定义的.
这是C11标准的.C99是一样的.
从C++11,它或多或少相同.关于指针转换的一些调整,如果你愿意,我可以将它全部粘贴.更重要的是,行为是未指定的(正如Chris上面指出的那样).
请注意,未定义的行为是致命的.如果你比较两个不相关的指针,你的机器可能会着火,发射核导弹,让恶魔飞出你的鼻子等等.
未指明的行为必须做一些含糊不清的事情.编译器不必记录它,甚至不必为两个不同的程序做同样的事情,但它不能炸毁世界.您的计划仍被视为有效.
因此,在您的特定情况下,编译为C,用户可能会导致未定义的行为,具体取决于它们传递给函数的内容.这看起来很危险.
另外,与我对这个问题的评论相反,你不能只使用!=两个任意void*的:
C11
6.5.9平等运营商
2下列之一应持有:
- 两个操作数都有算术类型;
- 两个操作数都是兼容类型的限定或非限定版本的指针;
- 一个操作数是指向对象类型的指针,另一个是指向合格或非限定版本的void的指针; 要么
- 一个操作数是指针,另一个是空指针常量.
如果两个指针指向相同"对象"(结构,联合或数组)的部分(或者超过数组末尾的部分),则只能保证比较两个指针.
在实践中,这是因为存在分段存储器模型计算机,其中仅比较段偏移比比较段偏移和段id快得多.如果所述段重叠,则具有相同段偏移的两个指针可以比较相等,即使它们指向不同的存储区域.
现在这种系统比20年前更不常见.
通过@drawmcgowen,这是在C11 6.5.8.
当指针比较不相关的对象(不在同一个结果struct,union或阵列)是不确定的,我不知道一个平台,让未定义行为是不是"不,你认为它应该顺序比较"更多.
如果您真的需要这个,并且愿意限制您的代码可以移植到哪个平台,那么您可以获得合理的保证.但是请注意,由于这是未定义的行为,因此编译器的任何未来版本都可能使此代码无法正常运行.
更糟糕的是,一些编译器利用未定义的行为来优化机会.例如,假设您有一个缓冲区,并且在缓冲区的开头有一个指针.
如果比较另一个指向它的指针,则(A)它位于缓冲区内,因此>=比它大,或者(B)它不在缓冲区内,因此结果是未定义的.
如果你可以证明一个向量位于缓冲区的前面或者一个接一个末端,那么简单的优化就是放弃比较(如果>=是前面的,或者<=后面的),并用常量替换它.
您的编译器可以在代码优化的任何时候解决这个问题,任何一点发布.
它甚至可以说:"这是一个指向堆分配的对象",并证明了每个指针或者是等于指针,或不相关的-因此<和>是总是不确定的行为,和树枝是这样做可以完全消除您的码.
依赖于未定义的行为意味着您现在必须审核代码生成的机器代码,以及将来的每次编译.
最初这个问题用c ++标记.在C++中,std::less<void*>()( lhs, rhs )保证能很好地排序所有指针.(这是为了允许指针在各种std容器和算法中进行排序而添加的)如果您在混合C/C++系统中工作,这可能会有用.
对于较少的模板,[...],任何指针类型的特化都会产生严格的总顺序,这些顺序在这些特化之间是一致的,并且与内置运算符强加的部分顺序一致<[...]对于模板如果调用操作符调用比较指针的内置运算符,则调用运算符会产生一个严格的总顺序,这些顺序在这些特化之间是一致的,并且与这些内置运算符强加的部分顺序一致.