P1 和 P2 是指向整数的指针,如果 P2>P1 有效,则 P2-P1 有效吗?

p1 and p2 are pointers to ints, if p2>p1 is valid, is p2-p1 valid?

提问人:doug 提问时间:8/22/2023 最后编辑:Jan Schultkedoug 更新时间:8/26/2023 访问量:196

问:

可以比较指向类成员变量的指针,结果取决于声明序列。例如,在编译器资源管理器中的此示例是有效的并返回 true (1):

struct A {
    int a0 = 1;
    int a1 = 2;
};

consteval int foo()
{
    A a;
    int* p1 = &a.a0;
    int* p2 = &a.a1;
    return p2 > p1;
}

int main()
{
    return foo();
}

因此,人们会期望这将返回指针之间的距离(以 int 对象为单位)。它在运行时确实如此。编译器资源管理器p2-p1

struct A {
    int a0 = 1;
    int a1 = 2;
};

int foo()
{
    A a;
    int* p1 = &a.a0;
    int* p2 = &a.a1;
    return p2 - p1;
}

int main()
{
    return foo();
}

但不是在编译时。GCC、CLANG 和 MSVC 的行为都不同。GCC 编译并返回预期值,CLANG 抱怨 UB,MSVC 编译但返回 4,而不是 1,以字节为单位的距离!编译器资源管理器

CLANG:注意:减去的指针不是同一数组的元素

哪个是正确的?

C++ language-lawyer 指针-算术

评论

1赞 Vivick 8/22/2023
突出显示的规格要求它们更大,但没有说明任何关于放置的信息
0赞 Patrick 8/22/2023
afaik 结构可以向其条目添加不同数量的填充,因此就像 Vivick 所说,MSVC 编译器可能只是添加不同的填充
0赞 doug 8/22/2023
@eerorika运行时确定。但是在没有库调用的编译时,在没有诊断的情况下允许 UB 吗?
1赞 Jesper Juhl 8/22/2023
“所以人们会期望 p2-p1 会返回距离,” - 我不相信你能得出结论
0赞 doug 8/22/2023
@eerorika 我的理解是,如果在编译时在编译时 const 函数中执行,则某些(但不是全部)UB 需要诊断。例如,signed int 溢出。这不是真的吗?与库代码相关的 UB 可能会导致也可能不会导致诊断。

答:

15赞 Ryan Haining 8/22/2023 #1

不能减去指向对象的指针,除非它们是同一数组的元素。这是未定义的行为,因此您可以获得不同的结果。

如果 P 和 Q 分别指向同一数组对象 x 的数组元素 i 和 j,则表达式 P - Q 的值为 i − j。

否则,行为是未定义的。

http://eel.is/c++draft/expr.add#5.2

Clang 将其标记为未定义的行为是正确的,GCC 和 MSVC 在做任何他们想做的事情时都是正确的,因为它是未定义的行为。如果函数是,那么只有 clang 是正确的,其他的应该发出诊断consteval

评论

0赞 doug 8/22/2023
是的,这也是我的想法。我发现奇怪的是,即使指针指向不同的对象,也可以合法地进行比较。我唯一知道第一个是合法的,但不是第二个,所以我想我会测试它。
4赞 Peter 8/22/2023
从技术上讲,如果行为未定义,所有编译器都是正确的,因为该标准没有施加任何要求,并且根本不需要诊断未定义的行为。更公平地说,clang 提供了一种诊断,以一种人类更容易理解的方式识别实际问题,即编译器之间的区别在于实现的质量,而不是正确性。
0赞 Ryan Haining 8/23/2023
@Peter是的,说得好,我编辑了最后一行
0赞 doug 8/23/2023
@Peter 编译器在编译期间计算包含 UB 的常量表达式(不包括库调用中出现的 UB)时,必须失败/发出诊断。这就是 in foo 的原因。它在编译时强制 eval。请参见:[常量表达式 ([expr.const]) 的计算从不表现出在 [intro] 到 [cpp] 中明确指定为未定义的行为。(eel.is/c++draft/defns.undefinedconsteval)
1赞 doug 8/24/2023
@Peter UB 通常允许编译器和程序执行任何操作或不执行任何操作。但有一个例外。许多 UB 行为禁止表达式成为核心常量。当这种情况发生在编译时评估中时,它们无法进行常量评估,需要诊断。请参阅此处关于需要检测什么以及何时需要检测 UB 的相当详细的讨论 评论中也有很多很好的背景。