无条件创建指向向量最后一个元素的指针是否合法?

Is unconditionally creating a pointer to the last element of a vector legal?

提问人:eyelash 提问时间:9/6/2023 最后编辑:Vlad from Moscoweyelash 更新时间:9/6/2023 访问量:117

问:

我有以下 C++ 代码:

void bar(int&);
void baz();

void foo(std::vector<int>& v) {
    int* pointer_to_last = v.data() + (v.size() - 1);
    if (v.size() > 0 && *pointer_to_last == 42) {
        bar(*pointer_to_last);
    } else {
        baz();
    }
}

我无条件地创建指向向量最后一个元素的指针,但只有在向量不为空时才取消引用指针。根据标准,这是否合法,或者上面的代码是否包含未定义的行为?

如果上述程序是合法的,那么以下程序是否也合法?

void bar(int&);
void baz();

void foo(std::vector<int>& v) {
    int& reference_to_last = v.back();
    if (v.size() > 0 && reference_to_last == 42) {
        bar(reference_to_last);
    } else {
        baz();
    }
}

std::vector::back 的引用

回调空容器会导致未定义的行为。

所以我假设这个程序是不合法的。是否有任何用于 GCC 或 Clang 的编译器标志或任何静态分析器会针对上述代码向我发出警告?

C++ 引用 undefined-behavior 指针算

评论

0赞 Some programmer dude 9/6/2023
有一种更好的方法可以解决你的问题,并消除歧义:根本不定义变量!请直接使用向量中的数据:if (!v.empty() && v.back() == 42) { bar(v.back()); }
1赞 Oersted 9/6/2023
不会溢出空向量吗?无论如何,在空向量上,我认为我们几乎没有保证,我会@HolyBlackCat意见。v.size()-1v.data()
0赞 Daniel Langr 9/6/2023
@Oersted它会的,但溢出本身没有问题。
1赞 molbdnilo 9/6/2023
@eyelash只是索引到数组中。它的成本不会比通过指针访问高出一纳秒。back()
1赞 Some programmer dude 9/6/2023
我建议尝试一下,通过优化进行构建,并查看生成的汇编代码......我敢打赌你不会看到任何区别!此外,如果有任何差异,它将非常小,以至于几乎无法测量它。也许如果你在很短的时间内(几秒钟)在一个非常紧密的循环中做数百万个这样的工作。但是,您可能已经将其并行化,使该问题变得毫无意义。

答:

5赞 HolyBlackCat 9/6/2023 #1

两者都是非法的。前者被UBSAN(又名)拒绝(见[expr.add]/4-fsanitize=undefined)

runtime error: applying non-zero offset 18446744073709551612 to null pointer

后者-D_GLIBCXX_DEBUG 拒绝

Error: attempt to access an element in an empty container.

GCC 或 Clang 是否有任何编译器标志

以上两个,加上.-fsanitize=address

-D_GLIBCXX_DEBUG特定于 libstdc++。如果将 Clang 与 libc++ 而不是 libstdc++ 一起使用,请使用 .(它似乎需要 libc++ 17 或更高版本,它们的调试模式被破坏了一段时间。它似乎也缺乏迭代器验证。:c)-D_LIBCPP_ENABLE_DEBUG_MODE

评论

0赞 eyelash 9/6/2023
是否可以在编译时而不是在运行时发现这些错误?
0赞 HolyBlackCat 9/6/2023
@eyelash 很可能没有。您可以尝试一些静态分析器,看看它们是否能捕捉到它。
0赞 Vlad from Moscow 9/6/2023 #2

根据 C++20 标准,行为是未定义的(7.6.6 加法运算符)

(4.1) — 如果 P 的计算结果为空指针值,而 J 的计算结果为 0, 结果是 null 指针值。

(4.2) — 否则,如果 P 指向数组的数组元素 i 具有 n 个元素的对象 x (9.3.4.5),80 表达式 P + J 和 J + P (其中 J 的值为 j)指向(可能的假设)数组 x 的元素 i + j 如果 0 ≤ i + j ≤ n,表达式 P - J 指向 如果 0 ≤ i − j ≤ n,则 x 的(可能的假设)数组元素 i − j of x。

(4.3) - 否则,行为未定义