如何解决 Visual Studio C++ 问题,“算术溢出:在 4 字节值上使用运算符 '*',然后将结果转换为 8 字节值。”?

Posted

技术标签:

【中文标题】如何解决 Visual Studio C++ 问题,“算术溢出:在 4 字节值上使用运算符 \'*\',然后将结果转换为 8 字节值。”?【英文标题】:How to Solve Visual Studio C++ problem, "Arithmetic overflow: Using operator '*' on a 4 byte value and then casting the result to a 8 byte value."?如何解决 Visual Studio C++ 问题,“算术溢出:在 4 字节值上使用运算符 '*',然后将结果转换为 8 字节值。”? 【发布时间】:2019-09-17 13:34:33 【问题描述】:

我已经标记了警告位置。如果我理解正确地将两个 32 位数字相乘可以得到 64 位,那么我得到一个错误。

如果 C++ int 是 32 位,那么存储 64 位不也是溢出吗?请解释我做错了什么。警告消息显示在 Visual Studio 2019 中。

转换为更大的数据类型解决了这个问题。为什么?

#include <iostream>
using namespace std;


unsigned int m;
unsigned int n;


// Symbols 
int symbols[] =  -1, 0, 1 ;
unsigned int symbols_size = sizeof(symbols) / sizeof(symbols[0]);



// Returns false to signify that element exiting in array
bool check_symbol_validity(int val) 
    for (unsigned int i = 0; i < symbols_size; ++i) 
        if (symbols[i] == val) 
            return false;
        
    
    return true;



int main()


    cout << "Place enter graph node count: ";
    cin >> m;
    n = m;


    // Just a separator
    cout << string(30, '#') << "\n";
    cout << "Enter Transition Symbol Between Node i and Node j. \nIf none then enter -1.\n";



    // Transition table, take node and symbol, return next node
    //int* transition_table = new int[m * symbols_size]; // WARNING
    int* transition_table = new int[(int64_t)m * symbols_size]; // OK


    // Declare C++ dynamic array
    //int* weight_matrix = new int[m * n]; // WARNING
    //int* weight_matrix = new int[(long)m * n]; // WARNING
    int* weight_matrix = new int[(long long)m * n]; // OK


    // Store positional values for direct access rather than using multiplication
    int* width_val_arr = new int[m];


    // TAKE VALID INPUT
    int user_input_value;
    for (unsigned int i = 0; i < m; ++i) 
        int tmp1 = i * m;
        width_val_arr[i] = tmp1;
        for (unsigned int j = 0; j < n; j++)
            cout << "Enter connection between " << i << " and " << j << "\n";
            cin >> user_input_value;
            while (check_symbol_validity(user_input_value)) 
                cout << "WRONG! ENTER CORRECT VALUE." << "\n";
                cout << "Enter connection between " << i << " and " << j << "\n";
                cin >> user_input_value;
            
            weight_matrix[tmp1 + j] = user_input_value;
        
    


    cout << string(30, '#') << "\n";


    // SHOW THE WEIGHT MATRIX
    for (unsigned int i = 0; i < m; ++i) 
        //int tmp1 = i * m;
        for (unsigned int j = 0; j < n; j++) 
            cout << weight_matrix[width_val_arr[i] + j] << " ";
        
        cout << "\n";
    



    // Delete the dynamic arrays
    delete[] weight_matrix;
    delete[] transition_table;
    delete[] width_val_arr;

警告信息:

“算术溢出:在 4 字节值上使用运算符 '*',然后将结果转换为 8 字节值。在调用运算符 '*' 之前将值转换为更广泛的类型以避免溢出”。

【问题讨论】:

我不明白你不明白什么。在您标记警告的地方,您还解决了取消注释行中的问题。 32 位乘以 32 位是 32 位结果。 64 位乘以 32 位是 64 位结果。 这看起来不像是错误。它类似于 C26450,一个代码分析警告。很难猜测正在使用什么规则,我当然无法从给定的来源复制它。无论如何,使用分析 > 配置来选择您要强制执行的规则。 【参考方案1】:

C26451 RESULT_OF_ARITHMETIC_OPERATION_CAST_TO_LARGER_SIZE Using operator

在调用 operator [operator] 之前将值转换为更宽的类型以避免溢出

此警告表示由于整数提升规则和类型大于通常执行算术的类型而导致的错误行为。我们检测到窄类型整数值何时左移、乘法、加法或减法,并且该算术运算的结果被转换为更宽的类型值。如果操作溢出窄类型值,则数据丢失。您可以通过在算术运算之前将值转换为更广泛的类型来防止这种损失。

Arithmetic overflow checks in C++ Core Check

【讨论】:

这是我测试的分析规则。然而,它并没有对 OP 的代码产生诊断。可能是 VS 版本号问题,这些规则很新,用 16.2.4 测试【参考方案2】:

您收到此警告是因为编译器假设由于您将乘法结果存储在 64 位宽类型中,因此您也希望在 64 位空间中进行乘法运算。当你这样做时

m * symbols_size
// or
m * n
// or
(long)m * n

你将两个 32 位宽的类型相乘,所以你得到一个 32 位宽的结果。数组的大小虽然是 64 位宽的类型,但会触发假设。

如您所见,您可以通过将操作数之一设为 64 位宽来消除此警告。如果您不关心您是在 32 位空间中进行乘法运算,另一种解决方案是禁用该警告。

【讨论】:

@B200011011 您使用的是无符号整数类型,因此您计算结果以 2^32 为模。 糟糕,我错误地删除了我之前的评论。我缺少一些基本的理解。但是,正如之前的评论,如果我乘两个32位的数字,不带符号31位数字,1111111111111111111111111111111 * 1111111111111111111111111111111我得到11111111111111111111111111111100000000000000000000000000000001.它如何可以存储在32位宽的结果作为答案给出,“你是相乘两个32位宽类型一起,所以你得到一个 32 位宽的结果”。 @B200011011 因为在 C++ 中,它就是这样工作的。如果它们是有符号整数类型,那么您有有符号整数溢出,这是未定义的行为,但大多数实现使用二进制恭维整数,因此结果将为 1,因为您保留了 11111111111111111111111111111100000000000000000000000000000001 的最低有效 32 位 00000000000000000000000000000001。跨度> 【参考方案3】:

在 C++(以及几乎任何其他类似 C 的高级语言)中,如果原始类型相同,则两个数字之间的算术运算会产生相同类型的值,而 common 类型会产生一个值 em> 两者中的一个,如果它们不同的话。确实,将两个 32 位数字相乘会产生 64 位结果,但这不是 C++ 的工作原理

以下算术运算符的参数经过隐式转换以获得公共实数类型,即执行计算的类型:

二进制算术 *、/、%、+、- 关系运算符 、=、==、!= 二进制位算术 &, ^, |, 条件运算符 ?:

https://en.cppreference.com/w/c/language/conversion

获取通用类型的完整规则可以在上面的链接中阅读,也可以在下面的标准中阅读

如果 m 和 n 是 ints 那么它们的乘积也是一个 int。因此,new int[m * n]; 中有一个警告,因为新运算符会收到一个 size_t,它是您平台上的 64 位无符号类型。与new int[(long)m * n] 相同,因为longint 的常见类型是long(在Windows 上也是32 位类型)

要获得完整的 64 位产品,您需要将至少一个操作数转换为 64 位类型,即 MSVC 中的 long long。这就是最后一行new int[(long long)m * n]; 起作用的原因。但是,您会收到与有符号和无符号之间转换相关的警告

来自ISO/IEC 9899:201x C++ standard

6.3.1.8 常用算术转换

许多期望算术类型的操作数的运算符会以类似的方式导致转换和产生结果类型。目的是确定操作数和结果的通用实数类型。对于指定的操作数,每个操作数都被转换为一个类型,其对应的实数类型是公共实数类型。除非另有明确说明,否则公共实数类型也是结果的对应实数类型,如果操作数相同,则其类型域为操作数的类型域,否则为复数。这种模式称为通常的算术转换

首先,如果任一操作数的对应实数类型为 long double,则将另一个操作数转换为不改变类型域的类型, 对应的实数类型是 long double。 否则,如果任一操作数的对应实数类型为双精度,则将另一个操作数转换为不改变类型域的类型, 对应的实数类型是double。 否则,如果任一操作数对应的实数类型为浮点数,则另一个操作数将在不改变类型域的情况下转换为 对应的真实类型是float。 否则,将在两个操作数上执行整数提升。然后 以下规则适用于提升的操作数: 如果两个操作数的类型相同,则无需进一步转换。 否则,如果两个操作数都具有有符号整数类型或都具有无符号 整数类型,具有较小整数转换等级类型的操作数是 转换为具有更高等级的操作数的类型。 否则,如果具有无符号整数类型的操作数的秩大于或 等于另一个操作数的类型的等级,然后操作数与 有符号整数类型转换为无符号操作数的类型 整数类型。 否则,如果带符号整数类型的操作数的类型可以表示无符号整数类型的操作数类型的所有值,则将无符号整数类型的操作数转换为 带符号整数类型的操作数。 否则,两个操作数都转换为无符号整数类型 对应带符号整数类型的操作数的类型。

【讨论】:

以上是关于如何解决 Visual Studio C++ 问题,“算术溢出:在 4 字节值上使用运算符 '*',然后将结果转换为 8 字节值。”?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决 Visual Studio C++ 问题,“算术溢出:在 4 字节值上使用运算符 '*',然后将结果转换为 8 字节值。”?

如何在 Visual Studio 2003 中重命名 C++ 源文件?

如何在 Visual Studio C++ 中使用文件夹?

如何设置 Visual Studio C++“默认默认”项目设置

visual studio 2010 一个解决方案里有多个c++源文件 怎么只执行其中一个?

如何在 VIsual Studio 2013 中从 UML 图生成 C++ 代码