理解子表达式溢出推理

Understanding the Sub-expression overflow reasoning

我试图理解 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.
}

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

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

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

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

found[value - 1]

这样做非常好,

int a = value - 1;
found[a];

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

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

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

uint64_t x = 0xffffffffu << 16;

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

uint64_t x = 0xffff0000u;

但是,它 看起来 就像有人打算写这个:

uint64_t x = (uint64_t)0xffff0000 << 16;

这就是警告试图捕捉到的内容。这就是这条规则背后的推理。在你的情况下,编译器做得不好。

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

幽灵与它无关。

完全没问题...

为此:

int a = value - 1;
found[a];

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

解决方案

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

  • 保持警告开启(嘈杂,可能会分散您对真正警告的注意力)

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

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

  • 使用 #pragma warning(非常详细)

    禁用此位置周围的警告