C++ 中的异或加密,警告“使用带二进制位运算符的带符号整数操作数”

问题描述

我正在学习 C++ 中的简单 XOR 加密算法。

接下来的代码工作正常:

void test(int8_t* data,const int data_length) {
    const uint8_t key = 123;

    for (int index = 0; index < data_length; index++)
        data[index] = data[index] ^ key;
}

我得到的数据是有符号的,因此类型为int8_t

问题是编译器显示了下一个警告:

“使用带二进制位运算符的有符号整数操作数”

我可以在执行 XOR 操作时通过将 datauint8_t 一起转换来发出警告,但我不知道其含义。我做了一些测试,似乎没有问题,但我很困惑,因为数据可以包含带符号的值,所以我不确定通过转换它是否弄乱了数据。

即使数据可能包含负值,转换为 uint8_t 是否正确?还是我应该忽略警告?

解决方法

编译器发出警告,因为不应该对有符号整数执行按位运算。在 C++20 之前的 C++ 中,允许有符号整数的不同表示,这意味着相同的数字可以在不同的机器和编译器上用不同的位模式表示。这使得对有符号整数进行位操作的结果不可移植。诚然,intN_t 始终需要使用 two's complement 表示(并且 C++20 将该要求扩展到所有有符号整数),但仍然不建议将有符号整数用于按位运算。

在您的特定情况下,data[index]key 都被提升为 int 以执行 XOR 操作。然而,由于 data[index] 是一个有符号整数,它的值被符号扩展,而无符号 key 被零扩展。这意味着 XOR 仅影响中间 int 值的低 8 位,结果可能不适合 int8_t 范围。当您将结果分配回 data[index] 时,可能会发生有符号溢出,这在 C++ 中是 UB(在 C++20 之前;从 C++20 开始,它被很好地定义为截断高位)。>

在这种情况下,正确的做法是将您的数据视为原始字节数组,而不管这些字节代表什么值。这意味着,您应该使用 std::bytestd::uint8_t 来表示输入和输出数据。这样您就可以对无符号整数进行操作,并且不会出现可移植性或潜在的溢出问题。

,

使用 c++20,你应该使用按位复制:

void test(int8_t* data,const int data_length) {
    const uint8_t key = 123;

    for (int index = 0; index < data_length; index++){
        auto const encrypted = std::bit_cast<std::byte>(data[index]) ^ key;
        data[index] = std::bit_cast<int8_t>(encrypted);
    }
}

对于以前的版本,您应该将有符号类型静态转换为相应的无符号类型。

void test(int8_t* data,const int data_length) {
    const uint8_t key = 123;

    for (int index = 0; index < data_length; index++){
        auto const encrypted = static_cast<std::byte>(data[index]) ^ key;
        data[index] = static_cast<int8_t>(encrypted);
    }
}