18

等式运算符具有关系运算符对指针的语义限制:

==(等于)和 !=(不等于)运算符与关系运算符具有相同的语义限制、转换和结果类型,但它们的优先级较低和真值结果除外。[C++03 §5.10p2]

并且关系运算符对比较指针有限制:

如果相同类型的两个指针 p 和 q 指向不同的对象,这些对象不是同一对象的成员或同一数组的元素或指向不同的函数,或者如果其中只有一个为空,则 p<q, p 的结果>q、p<=q 和 p>=q 未指定。[§5.9p2]

这是相等运算符“继承”的语义限制吗?

具体来说,给定:

int a[42];
int b[42];

很明显 (a + 3) < (b + 3) 是未指定的,但 (a + 3) == (b + 3) 是否也未指定?

4

3 回答 3

15

op==和的语义op!=明确表示映射除了它们的真值结果之外。因此,您需要查看为它们的真值结果定义的内容。如果他们说结果是未指定的,那么它是未指定的。如果他们定义了特定的规则,那么它不是。它特别说

相同类型的两个指针比较相等当且仅当它们都为空,都指向相同的函数,或都表示相同的地址

于 2011-02-05T21:31:17.433 回答
12

只要指针指向相同类型的对象,等式运算符 (==和) 的结果就会产生指定的结果。!=给定两个指向同一类型的指针,以下情况恰好有一个为真:

  1. 两者都是空指针,并且它们彼此比较相等。
  2. 两者都是指向同一个对象的指针,并且它们彼此比较相等。
  3. 它们是指向不同对象的指针,并且它们彼此比较不相等。
  4. 至少有一个没有初始化,并且比较的结果没有定义(事实上,比较本身可能永远不会发生 - 只是试图读取指针进行比较会给出未定义的行为)。

在相同的约束下(两个指针都指向相同类型的对象),排序运算符(<, <=, >, >=)的结果仅在它们都是指向同一对象的指针或指向同一数组中的不同对象时才被指定(并且为此,分配有mallocnew等的内存“块”可称为数组)。如果指针指向不属于同一数组的单独对象,则结果未指定。如果一个或两个指针没有被初始化,你有未定义的行为。

尽管如此,标准库(、、和std::lessstd::greater中的比较模板都会产生有意义的结果,即使/如果内置运算符没有。特别是,它们需要产生总排序。因此,您可以根据需要进行排序,而不是使用内置的比较运算符(当然,如果其中一个或两个指针未初始化,则行为仍然未定义)。std::less_equalstd::greater_equal

于 2011-02-05T21:52:22.253 回答
6

由于一致性语义存在混淆,因此这些是 C++ 的规则。C 使用完全不同的一致性模型。

  1. 未定义的行为是一个矛盾的术语,它意味着翻译者不是你的程序,可以随心所欲。这通常意味着它可以生成代码,这些代码也可以做任何它喜欢的事情(但这是一个推论)。如果标准说行为未定义,则文本实际上对用户没有意义,因为删除该文本不会改变标准对翻译人员的要求。

  2. 格式错误的程序意味着除非另有说明,否则翻译程序的行为是严格定义的:需要拒绝您的程序并发出诊断消息。这里的主要特例是单一定义规则,如果您违反了您的程序格式错误但不需要诊断的情况。

  3. 定义的实现对翻译器提出了一个要求,即它包含明确指定行为的文档。在这种特殊情况下,结果可能是未定义的行为,但必须明确说明。

  4. 未指定是一个愚蠢的术语,这意味着行为来自一个集合。从这个意义上说,良好定义只是一种特殊情况,其中允许的行为集仅包含一个元素。未指定不需要文档,因此在某种意义上它也意味着与没有文档定义的实现相同。

一般来说,C++ 标准不是语言标准,而是语言标准的模型。要生成实际标准,您必须插入各种参数。其中最容易识别的是实现定义的限制。

标准中有几个愚蠢的冲突,例如,一个合法的翻译器可以拒绝每个表面上好的 C++ 程序,因为你需要提供一个main()函数,但翻译器只支持 1 个字符的标识符。这个问题可以通过QOI或实施质量的概念来解决。它基本上说,谁在乎,没有人会仅仅因为它符合要求而购买该编译器。

从技术上讲,指针指向不相关对象时的未指定性质operator <可能意味着:您将获得某种结果,它是真或假,但您的程序不会崩溃,但这不是未指定的正确含义,因此是一个缺陷:未指定给标准编写者带来了记录允许行为集的负担,因为如果该集是开放的,那么它相当于未定义的行为。

我实际上提出std::less了一些数据结构要求键完全排序但指针不完全排序的问题的解决方案operator <。在大多数使用线性寻址less的机器上与 相同<,但在lessx86 处理器上的操作可能更昂贵。

于 2011-02-06T02:59:28.227 回答