理解子表达式溢出推理

The*_*der 9 c++ 64-bit

我试图理解 Visual Studio 2022 中这一特定建议背后的原因,因为它对我来说似乎没有意义。这是简单的代码:

static constexpr uint32_t MAX_ELEMENTS = 100000;
const std::vector<int> A{ some random ints };


std::vector<bool> found(MAX_ELEMENTS);
for (int value : A)
{
    if (value > 0 && value <= MAX_ELEMENTS)
        found[value - 1] = true;             // Problem it complains about is the value - 1.
}
Run Code Online (Sandbox Code Playgroud)

它表明“子表达式在添加到更广泛的类型之前可能会溢出”。现在显然条件 if 阻止了这种情况的发生,但是这里的推理是什么?

现在,如果这是 Spectre 的事情,我可以理解编译器会添加内存栅栏来停止 if 之后语句的任何推测执行,但我不认为这就是答案。

我唯一的想法是它与向量上的下标运算符有关;它的索引是大于 int 的类型并且隐式转换为 size_t?

下面的问题似乎有点不直观:

found[value - 1]
Run Code Online (Sandbox Code Playgroud)

这样做完全没问题,

int a = value - 1;
found[a];
Run Code Online (Sandbox Code Playgroud)

当最终结果相同时。就理解而言,我在这里缺少什么?

Die*_*Epp 5

在这种情况下,正如您所怀疑的那样,这是误报。这是有时在更严格的代码库中使用的规则。(例如,此特定警告是 MISRA 中的错误。)

很多警告都是这样的......编译器编写者试图检测程序行为是意外或无意的情况,但警告并不总是正确的。例如,

uint64_t x = 0xffffffffu << 16;
Run Code Online (Sandbox Code Playgroud)

在具有 32 位 int 的系统上,标准非常清楚this 的值应该是什么......它应该是:

uint64_t x = 0xffff0000u;
Run Code Online (Sandbox Code Playgroud)

然而,看起来确实有人打算写这个:

uint64_t x = (uint64_t)0xffff0000 << 16;
Run Code Online (Sandbox Code Playgroud)

这就是警告试图捕捉到的内容。这就是这条规则背后的原因。就您而言,编译器做得很糟糕。

您可以在编译器文档中查看该警告的更详细理由:警告 C26451

幽灵与此无关。

完全没问题...

去做这个:

int a = value - 1;
found[a];
Run Code Online (Sandbox Code Playgroud)

在这种情况下,“意图”更加明显,因为程序员已明确写出“int”作为类型。

解决方案

这是一个代码风格问题,因此有几种不同的解决方案,您可以选择您觉得舒服的解决方案。

  • 保持警告打开(噪音,可能会分散您对真正警告的注意力)

  • 首先将表达式分配给 int(详细)

  • 禁用整个代码库的警告(如果您认为它没有帮助)

  • #pragma warning使用(非常详细)禁用此位置周围的警告

  • @Sebastian虽然我完全同意你的观点,但上面的代码足够简单,可以理解,无需重写它来绕过不应该出现的警告。 (2认同)