警告 C26451:算术溢出

28

我该如何解决这些警告?

// midiNote is a double as it is used in floating point equation
// v is int because that's informative that the function wants whole numbers
void setMidiNote(int v) { midiNote = v-48;  }

警告 C26451 算术溢出:对一个4字节值使用运算符“-”,然后将结果转换为8字节值。在调用运算符“-”之前,将该值转换为更宽的类型以避免溢出 (io.2)。

// input should be 0 to 10 integer, and dank will be odd integers only
// dank is a double, it is ultimately used in a floating point equation
void setDarkIntensity(int v) { dank = v * 2 + 1; }

警告 C26451 算术溢出:在4字节值上使用运算符'*',然后将结果转换为8字节值。在调用运算符'*'之前,将该值转换为更宽的类型以避免溢出 (io.2)。

警告 C26451 算术溢出:在4字节值上使用运算符'+',然后将结果转换为8字节值。在调用运算符'+'之前,将该值转换为更宽的类型以避免溢出 (io.2)。

7个回答

20

我认为这是VS2019中的一个错误,而在VS2022中它不再被标记。

例如,这会产生警告:

double test2(int n)
{
     return 4.0 * (n - 1);
}

但是这并不

int test2a(int n)
{
    return 4 * (n - 1);
}

然而,后者的未定义行为风险要大得多。将其乘以4会大大增加UB的风险,因为更多的n集合会产生UB。有多大呢?嗯,在第一个示例中,约40亿个可能值中只有一个可能值的n溢出。在第二个示例中,大约有30亿个n会溢出或下溢。为什么?因为如果每个具有比加0或乘1更复杂性的表达式都被标记为可能溢出,整数算术就将不可能实现。

可以说,对于设置如此高的警告,几乎对int的任何算术运算都会受到警告。

此答案展示了如何在VS 2019的代码分析规则集编辑器中禁用此警告。

警告C26454:算术溢出:'-'操作在编译时产生负无符号结果(io.5)

然而,截至VS2022,微软不再为此发出C26451波浪形警告。它也不会在-Wall下显示。他们显然看到了光明面。


3
这不是 bug。在这里查看讨论和已接受的答案:https://dev59.com/1rjna4cB1Zd3GeqP3w8n - user11748261
2
@user11748261 接受的答案被移除了吗?现在没有被接受的答案。 - HelloGoodbye
1
那个问题确实没有被采纳的答案(顺便说一句,这并不意味着回答的正确性或质量——顶楼的账户已经不存在,所以它甚至无法获得一个被采纳的答案)。它仍然有多个回答和评论解释了警告以及为什么它不是一个 bug。可惜的是,人们经常假设静态分析器在不立即理解问题的情况下就是错误的。在许多情况下,警告是关于意外角落的情况,这些情况通常是未知或被忽视的(这正是静态分析器如此有用的原因)。 - wovano
1
肯定不是一个错误,它说可能会溢出,所以最好适当地转换值。 - CM777

7
警告提示您,计算可能在转换为结果(更大)类型之前会溢出原始(较小)类型。 在第一种情况下,如果v是MIN_INT(-2 31 ),减法将下溢,导致未定义行为(可能是一个很大的正数),然后存储在midiNote 中。 为了避免警告,请先转换为较大的类型:
midiNote = double(v) - 48;

同样适用于您的第二个示例。

虽然您可以知道 setMidiNote 不会使用可能存在问题的值进行调用,但是编译器不知道并发出此警告以警告您存在潜在问题的可能性。


警告级别为高级别4 (/W4)。 - Elan Hickler
1
@doug 根据标准,这是未定义行为,但在VS中具有已定义的行为。警告显示您可能会得到一个存在下溢/上溢的结果,而通过替代措辞可以消除这个问题。 - 1201ProgramAlarm
3
当然可以,但简单的表达式"I+1"也可能会产生未定义行为。"4.0*(v-1)"与"4.0*(v+1)"或仅仅是"v+1"一样都有问题,因为任何C++用户都非常清楚int溢出问题,而将所有这些问题用强制转换括起来以表示程序员假定负责防止未定义行为是荒谬的,会产生分散注意力的噪音。 - doug
1
@ElanHickler 你使用的是哪个编译器版本和编译器标志?我正在尝试复现这个问题,但还没有找到能够产生警告的神奇组合。 - 1201ProgramAlarm
我正在使用VS2019。有人需要解释一下如何获取所需的编译器标志。虽然我认为编译器标志并不相关。 - Elan Hickler
2
你可以通过 #pragma warning (disable : 26451) 来禁用警告。 - Hosein Rahnama

3

我在查看一些Microsoft Docs后解决了这个问题,但你也可以将变量更改为long long类型(有些过分,我知道)。这对我来说已经消除了错误。希望他们能尽快解决这个问题。


3
我通过将变量类型更改为“unsigned __int64”来消除了警告。这就是Microsoft开发者社区建议的做法!

1

0

static_cast<>()是推荐的解决方案。我目前正在阅读的书籍非常强调这种新的转换约定。仅使用(v)被认为是C风格。(根据文献)。我想知道,是否只声明v为auto就可以工作。听起来Vector正在做某些事情....

void setDarkIntensity(auto v) { dank = v * 2 + 1; }


0

尽早将数据类型转换为所需类型,然后一直使用它:

// midiNote is a double as it is used in floating point equation
// v is int because that's informative that the function wants whole numbers
void setMidiNote(int v) { midiNote = static_cast<double>(v) - 48.0;  }

// input should be 0 to 10 integer, and dank will be odd integers only
// dank is a double, it is ultimately used in a floating point equation
void setDarkIntensity(int v) { dank = static_cast<double>(v) * 2.0 + 1.0; }

以前整数运算比浮点数运算快得多,但在现代CPU上已不再如此。

使用2.0而不是2会强制将v隐式转换为double,但明确更清晰。

额外建议:考虑在调试版本中验证任何假设。这将有助于避免在将来更改代码时破坏这些假设。例如:

// input should be 0 to 10 integer, and dank will be odd integers only
// dank is a double, it is ultimately used in a floating point equation
void setDarkIntensity(int v) 
{ 
    assert(v >= 0 && v <= 10);
    dank = static_cast<double>(v) * 2.0 + 1.0; 
}

额外建议2:如果这些函数不是类的一部分,我会将它们标记为inline,这样优化器在处理这些简单函数时有更好的机会。如果它们是类定义的一部分,那么这已经隐式地完成了。


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