小于void指针的比较

时间:2014-07-17 15:15:50

标签: c pointers comparison

我想比较两个这样的void指针:

void foo(void* p1, void* p2) {

  if (p1 < p2) {
    void *tmp = p1;
    p1 = p2;
    p2 = tmp;
  }

  // do something with p1 and p2.
}

根据标准,这是否正确?我的意思是将void指针与一个明确定义的行为进行比较?

如果有人能够指出我所记录的C标准,我会很感激。

3 个答案:

答案 0 :(得分:8)

正如Drew McGowen在评论中指出的那样,但我会在这里发布引文:

  

6.5.8关系运算符

     

5比较两个指针时,结果取决于地址空间中的相对位置   对象指向。 如果两个指向对象类型的指针都指向   相同的对象,或者两者都指向同一个对象的最后一个元素   数组对象,他们比较相等。如果指向的对象是   同一聚合对象的成员,指向稍后声明的结构成员的指针比指定成员的指针要大   在结构的早期,以及指向更大的数组元素的指针   下标值比指向同一元素的指针大   具有较低下标值的数组。指向成员的所有指针   相同的联合对象比较等于。如果表达式P指向a   数组对象的元素和表达式Q指向最后一个   同一个数组对象的元素,指针表达式Q + 1进行比较   大于P. 在所有其他情况下,行为未定义

这是C11标准。 C99是一样的。

来自C++11,它或多或少相同。关于指针转换的一些调整,如果你愿意,我可以将它全部粘贴。更重要的是,行为是未指定的(正如Chris上面指出的那样)。

请注意,未定义的行为是致命的。如果你比较两个不相关的指针,你的机器可能会着火,发射核导弹,让恶魔飞出你的鼻子等等。

未指明行为必须做一些含糊不清的事情。编译器不必记录它,甚至不必为两个不同的程序做同样的事情,但它不能炸毁世界。您的计划仍然有效。

因此,在您的特定情况下,编译为C,用户可能会导致未定义的行为,具体取决于它们传递给函数的内容。这似乎很危险。

另外,与我对该问题的评论相反,您 只能在两个!=上使用void*C11

  

6.5.9平等运营商

     

2以下其中一项应持有:

     

- 两个操作数都有算术类型;

     

- 两个操作数都是指向兼容类型的限定或非限定版本的指针;

     

- 一个操作数是指向对象类型的指针,另一个是指向a的指针   无效的合格或不合格的版本;或

     

- 一个操作数是指针,另一个是空指针常量。

答案 1 :(得分:6)

如果两个指针指向同一个“对象”(结构,联合或数组)的部分(或者超过数组末尾的部分),则只能保证比较两个指针。

实际上,这是因为存在分段存储器模型计算机,其中仅比较段偏移比比较段偏移和段id快得多。如果所述段重叠,则具有相同段偏移的两个指针可以比较相等,即使它们指向不同的存储区域。

此类系统现在不像20年前那么常见。

通过@drawmcgowen,这是在C11 6.5.8。

虽然比较指向不相关对象(不在同一structunion或数组中)的指针的结果是未定义的,但我不知道未定义行为超过“不是”的平台按照你认为的顺序进行比较“。

如果您真的需要这个,并且愿意限制您的代码可移植到哪个平台,您可以获得合理的保证。但是请注意,由于这是未定义的行为,因此编译器的任何未来版本都可能使此代码无法正常运行。

更糟糕的是,某些编译器会利用未定义的行为来优化机会。例如,假设您有一个缓冲区,并且在缓冲区的开头有一个指针。

如果比较另一个指向它的指针,要么(A)它在缓冲区内,所以>=都在它之内,或者(B)它不在缓冲区内,所以结果是未定义的。 / p>

如果你可以证明一个向量位于缓冲区的前面或者一个结尾处,那么简单的优化就是放弃比较(前面是>=,后面是<= )并用常数替换它。

您的编译器可以在代码优化的任何时候解决这个问题,任何一点发布。

它甚至可以说“这是指向堆分配对象的指针”,并证明每个指针都等于指针或不相关 - 因此<>始终未定义的行为,并且可以从代码中完全消除执行此操作的分支。

依赖于未定义的行为意味着您现在必须审核代码生成的机器代码,以及将来的每次编译。


最初这个问题标有。在C ++中,std::less<void*>()( lhs, rhs )可以很好地命令所有指针。 (这是为了允许指针在各种std容器和算法中进行排序而添加的)如果您在混合C / C ++系统中工作,这可能会有用。

20.14.6 [比较] / 2

  

对于较少的模板,[...],任何指针类型的特化产生严格的总顺序,这些顺序在这些特化之间是一致的,并且也与内置运算符强加的部分顺序一致&lt; [...]对于模板特化,如果调用操作符调用比较​​指针的内置运算符,则调用运算符产生一个严格的总顺序,这些顺序在这些特化之间是一致的,并且与所施加的部分顺序一致由那些内置的运营商。

答案 2 :(得分:0)

从@BoBTFish开始......对于关系运算符(C11的6.5.8),事情进一步受到限制:

  

约束2

     

以下其中一项应成立:

     

- 两个操作数都有实际类型;

     

或 - 两个操作数都是指向兼容对象类型的限定或非限定版本的指针。

哪一个读为不包括 void*void不是类型,因此void*不是指向某个类型的指针)...所以你的如果您尝试<等两个void*指针或一个“真实”指针和void*,编译器可能会抱怨你。施放到(char *)通常可以完成工作!但是就像任何一个演员一样,如果它崩溃和烧伤,没有人会非常同情: - )

顺便说一句,这个标准不是一个简单的阅读,但是60美元它是一个有用的新成员http://webstore.ansi.org/RecordDetail.aspx?sku=INCITS%2FISO%2FIEC+9899-2012