背景:我想知道如果我们通过char *
缓冲区将二进制数据反序列化。
假设:作为最小示例,我们将在这里考虑:
int
通过char*
缓冲区序列化。int
从缓冲区取回原件。sizeof(int) == 4
在目标系统/平台上。注意:这纯粹出于一般兴趣,因此我不想使用任何类似的方法,std::memcpy
因为我们不会看到遇到的奇怪行为。
测试:我建立了以下测试用例:
#include <iostream>
#include <bitset>
int main()
{
// Create neg_num and neg_num_bytes then display them
int neg_num(-5000);
char * neg_num_bytes = reinterpret_cast<char*>(&neg_num);
display(neg_num, neg_num_bytes);
std::cout << '\n';
// Create pos_num and pos_num_bytes then display them
int pos_num(5000);
char * pos_num_bytes = reinterpret_cast<char*>(&pos_num);
display(pos_num, pos_num_bytes);
std::cout << '\n';
// Get neg_num back from neg_num_bytes through bitmask operations
int neg_num_back = 0;
for(std::size_t i = 0; i < sizeof neg_num; ++i)
neg_num_back |= static_cast<int>(neg_num_bytes[i]) << CHAR_BIT*i; // For little-endian
// Get pos_num back from pos_num_bytes through bitmask operations
int pos_num_back = 0;
for(std::size_t i = 0; i < sizeof pos_num; ++i)
pos_num_back |= static_cast<int>(pos_num_bytes[i]) << CHAR_BIT*i; // For little-endian
std::cout << "Reconstructed neg_num: " << neg_num_back << ": " << std::bitset<CHAR_BIT*sizeof neg_num_back>(neg_num_back);
std::cout << "\nReconstructed pos_num: " << pos_num_back << ": " << std::bitset<CHAR_BIT*sizeof pos_num_back>(pos_num_back) << std::endl;
return 0;
}
其中display()
定义为:
// Warning: num_bytes must have a size of sizeof(int)
void display(int num, char * num_bytes)
{
std::cout << num << " (from int) : " << std::bitset<CHAR_BIT*sizeof num>(num) << '\n';
std::cout << num << " (from char*): ";
for(std::size_t i = 0; i < sizeof num; ++i)
std::cout << std::bitset<CHAR_BIT>(num_bytes[sizeof num -1 -i]); // For little-endian
std::cout << std::endl;
}
我得到的输出是:
-5000 (from int) : 11111111111111111110110001111000 -5000 (from char*): 11111111111111111110110001111000 5000 (from int) : 00000000000000000001001110001000 5000 (from char*): 00000000000000000001001110001000 Reconstructed neg_num: -5000: 11111111111111111110110001111000 Reconstructed pos_num: -120: 11111111111111111111111110001000
我知道测试用例代码很难阅读。简要说明一下:
int
。char*
指向先前创建的第一个字节的数组int
(以模拟我int
在char*
缓冲区中存储了实数)。因此,其大小为4。int
和及其二进制表示形式int
存储的每个字节的和并char*
进行比较,以比较它们是否相同(由于字节顺序的原因,顺序相反)。int
从缓冲区取回原件。int
及其二进制表示形式。我对负值和正值都执行了此过程。这就是为什么代码的可读性差(对此感到抱歉)。
如我们所见,负值可以成功重构,但对正值无效(我期望5000
并且得到了-120
)。
我已经用其他几个负值和正值进行了测试,结论仍然是相同的,它在负数下可以正常工作,但在正数下不能工作。
问题:我很难理解为什么将4连chars
成一个int
通过按位移位时为什么char
正数的值与负值保持不变而改变呢?
当我们查看二进制表示形式时,我们可以看到重构的数字不是由char
我串联的s组成。
与有关static_cast<int>
吗?如果我删除它,则积分提升规则将隐式应用它。但是我需要完成此操作,因为我需要将其转换为int
,以免丢失转换的结果。
如果这是问题的核心,如何解决?
另外:有没有比逐位移位更好的方法来取回值?不依赖于系统/平台的字节序的东西。
也许这应该是另一个单独的问题。
这里有两个主要因素会影响结果:
char
可以是有符号的也可以是无符号的,这是编译器保留的实现细节。这里可能发生的是char
在您的系统上和使用您的编译器进行了签名。这意味着当您将字节转换为anint
并将高位设置为1时,该值将被符号扩展(例如,二进制10000001
将被符号扩展为1111111111111111111111111000001
)。
这当然会影响按位操作。
解决方案是使用显式的无符号数据类型,即unsigned char
。我还建议您使用unsigned int
(或uint32_t
)进行类型转换和数据的临时存储,并且仅将完整结果转换为plain int
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句