C++ 中的 Xor 加密,带有警告“使用带二进制位运算符的有符号整数操作数”
Posted
技术标签:
【中文标题】C++ 中的 Xor 加密,带有警告“使用带二进制位运算符的有符号整数操作数”【英文标题】:Xor encryption in C++ with warning "Use of a signed integer operand with a binary bitwise operator" 【发布时间】:2021-02-20 20:31:23 【问题描述】:我正在学习 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;
给我的data是有符号的,因此有一个int8_t类型。
问题是编译器显示下一个警告:
“带符号整数操作数与二进制位运算符的使用”
在执行 XOR 操作时,我可以通过使用 uint8_t 强制转换 data 来发出警告,但我不知道其中的含义。我做了一些测试,似乎没有问题,但我很困惑,因为数据可以包含有符号值,所以我不确定通过转换它是否会弄乱数据。
即使数据可以包含负值,转换为 uint8_t 是否正确?还是我应该忽略警告?
【问题讨论】:
只是出于好奇-您能告诉我们您使用的是哪个编译器吗?我尝试了几个(MSVC 和 clang-cl),但它们不会产生这样的警告。 @AdrianMole 这完全取决于您的警告级别;如果您将编译器配置为这样做,GCC、clang 和 MSVC 将提供此类警告。 我正在使用来自 IntelliJ 的 CLion 使用 clang @F***Keßler 我在 MSVC(VS-2019)中有 /Wall(启用所有警告)并且没有给出警告:在编译和静态分析中都没有。但是,我可以看到应该有一个警告。 【参考方案1】:编译器发出警告,因为不应该对有符号整数执行按位运算。在 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::byte
或std::uint8_t
来表示输入和输出数据。这样您就可以对无符号整数进行操作,并且没有可移植性或潜在的溢出问题。
【讨论】:
【参考方案2】:使用 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);
【讨论】:
在static_cast
之前没有std::
。此外,您可能希望始终将 std::
限定与 int8_t
和其他类型一起使用或省略它。
int8_t
和 uint8_t
不在我的 clang 编译器的全局命名空间中。当然,我可以通过using std::int8_t;
和using std::uint8_t;
将它们添加到那里。或者通过#include <stdint.h>
。
@F***Keßler C++ 将int8_t
定义为std
命名空间中的其他标准类型。您可以在 C 包含的全局命名空间中看到它,许多 C++ 实现在内部使用它。但是,该全局名称不是标准名称,而是偶然出现的。
@API_1024 不,std::byte 被定义为枚举,底层类型为 unsigned char。禁止所有数值运算,只允许按位运算。这是"modern" 表示您正在使用原始数据的方式。
@API_1024 std::byte
不是别名,它是一个单独的类型,与unsigned char
大小相同。转换为 std::byte
不会更改值的位表示,但会更改其解释。由于您无论如何都只使用 XOR 对位表示进行操作,因此结果是相同的(我的回答中提到了无符号/零扩展名)。以上是关于C++ 中的 Xor 加密,带有警告“使用带二进制位运算符的有符号整数操作数”的主要内容,如果未能解决你的问题,请参考以下文章