指针越界比较是否定义良好?

13

考虑以下代码:

char buffer[1024];
char * const begin = buffer;
char * const end = buffer + 1024;
char *p = begin + 2000;
if (p < begin || p > end)
    std::cout << "pointer is out of range\n";

比较操作(p < beginp > end)是否有定义良好?还是这段代码由于指针已经超过数组末尾而具有未定义的行为?

如果比较是定义良好的,那么它的定义是什么?

(额外加分项:计算begin + 2000本身是否具有未定义的行为?)


4
我认为答案只有指向结尾之后的指针和NULL,但我无法引用准确的章节和段落。 - Flexo
在一个分段内存的系统中,地址 begin + 2000 可能不存在。 - Bo Persson
这个问题涉及到两个不同的概念:指针比较和指针值的创建。在我们询问指针比较之前,我们必须确定 begin + 2000 是否被定义。我认为它没有被定义。一旦我们有了两个有效的指针,你就可以问如何比较它们。正如Mat的答案所指出的那样,一般情况下没有明确规定。但是,你可以使用 std::less<>(等等)来获得指针值的总排序,即使直接使用 < 是未指定的。 - GManNickG
3个回答

10
我会假设使用 C++11 标准。根据第5.7节(加法运算符)第5段,*p = begin + 2000 的行为是未定义的,甚至在进行比较之前就是这样:

如果指针操作数和结果都指向同一数组对象或该数组对象的最后一个元素之一,则评估不应产生溢出;否则,行为未定义。


6
begin + 2000的计算结果未定义,因为超出了数组的末尾 - 您可以一直到末尾之外,但不能再更远。
从C++11 §5.7/5 加法运算符
当将具有整数类型的表达式添加到指针或从中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且该数组足够大,则结果指向原始元素的偏移量处的元素,使得所得到的数组元素的下标与原始数组元素的下标之差等于整数表达式。[...] 如果指针操作数和结果都指向同一数组对象的元素或其末尾之一,则评估不应产生溢出;否则,行为是未定义的
要指定指针比较(假设您已经有有效的指针),它们基本上需要是指向同一数组(或末尾之一)的指针,或者是相同对象的非静态数据成员的指针控制访问权(除非它是一个union...)。
细节见§5.9/2 关系操作符
对象或函数的指针可以进行比较,其类型相同(经过指针转换) ,定义结果如下:
  • 如果两个指向相同对象或函数的类型相同(或都指向相同数组的末尾),或者都为null,则p<=q和p>=q都产生true,p<q和p>q都产生false。
  • 如果两个类型为p和q的不同对象指向不同的对象,这些对象不是同一对象的成员或同一数组的元素,或仅其中之一为null,则p<q,p>q,p<=q和p>=q的结果未指定。
  • 如果两个指针指向同一对象的非静态数据成员或该成员的子对象或数组元素(递归地),则在提供两个成员具有相同访问控制(第11条款)并且其类不是union的情况下,指向后声明的成员的指针比较更大。
  • 如果两个指针指向同一对象的非静态数据成员但具有不同的访问控制(第11条款),则结果未指定。- 如果两个指针指向同一联合对象的非静态数据成员,则它们相等(如有必要,转换为void*)。如果两个指针指向相同数组的元素或数组末尾之一,则具有更高下标的对象的指针比较更高。
  • 其他指针比较未指定。

  • 2
    为什么会这样呢?可能是因为buffer在一个只有2048字节的内存中被分配到了地址500。所以begin是500,而p是2500。但由于内存只有2048大(因此指针只有11位长),实际上得到的p是地址452——在buffer之前。(是的,这个例子是人为制造的,但有各种不同的实际分段内存方案。) - Hot Licks
    1
    请注意,您可以使用std::less<>(等等)来获得无关指针的完全排序(尽管它们当然仍必须具有有效值,这是完全不同的事情)。 - GManNickG
    但是当然,“无关指针”的“排序”在很大程度上是没有意义的。 - Hot Licks
    @Mat 它并没有指向任何东西。那里什么都没有; 它是无效的。我将 "那里" 用引号括起来,因为指针值一开始就无法合法地计算。甚至那里也没有一个位置。请参见其他答案。 - Potatoswatter

    3

    您的程序行为未定义,但并不是由于比较操作。

    表达式begin + 2000的计算结果具有未定义行为,因为该结果将指向超出1024元素数组末尾一个以上的元素。

    引用C++11(实际上是N3485草案),5.7p4 [expr.add]:

    当将整数类型的表达式添加到指针或从指针中减去时,结果具有指针操作数的类型。[...]如果指针操作数和结果都指向同一数组对象的元素或该数组对象的最后一个元素之一,则评估不应产生溢出;否则,行为未定义。

    简而言之,仅计算越界指针就具有未定义行为;在此之后对该指针执行任何操作都无关紧要。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接