无论是正数还是负数,通过char *缓冲区读取int的行为都不同

法雷诺

背景:我想知道如果我们通过char *缓冲区将二进制数据反序列化

假设:作为最小示例,我们将在这里考虑:

  • 我只有一个int通过char*缓冲区序列化
  • 我想int从缓冲区取回原件
  • sizeof(int) == 4 在目标系统/平台上。
  • 目标系统/平台的字节序little-endian

注意:这纯粹出于一般兴趣,因此我不想使用任何类似的方法,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(以模拟我intchar*缓冲区中存储了实数)。因此,其大小为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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

读取文件缓冲区通过

来自分类Dev

OpenGL ES2.0 glReadPixels()从渲染缓冲区通过帧缓冲区读取数据

来自分类Dev

从char缓冲区读取32位变量

来自分类Dev

从char缓冲区读取一点

来自分类Dev

将中间转换的char *缓冲区设置为int *

来自分类Dev

如何从char缓冲区创建long int?

来自分类Dev

使用imap获取电子邮件附件,但是无论是从Outlook Client还是通过Web发送电子邮件,都会得到不同的结果

来自分类Dev

Matlab通过原点进行绘制,无论仅是负数据还是仅是正数据

来自分类Dev

多个缓冲区还是单个缓冲区?

来自分类Dev

ServerName问题,无论是www还是http,都不能同时存在..为什么呢?

来自分类Dev

协议缓冲区负数int32 / int64编码器的规格模糊

来自分类Dev

相同的数字代码返回不同的输出,无论是在C ++还是C中

来自分类Dev

在C中获取char缓冲区(从读取的文件)中的值数

来自分类Dev

nodejs缓冲区是异步还是同步?

来自分类Dev

nodejs缓冲区是异步还是同步?

来自分类Dev

如果我的缓冲读取器正在读取大于2 Mb的行,它将缓冲该行还是会增加缓冲区大小?

来自分类Dev

我想根据值是正数还是负数执行不同类型的操作

来自分类Dev

将char指针转换为int指针-缓冲区错误10

来自分类Dev

无论是发行版还是镜像版,我的 Ubuntu SHA256SUM 都不匹配……我做错了什么?

来自分类Dev

如何使char缓冲区充当文件?

来自分类Dev

通过JavaScript中的缓冲区循环

来自分类Dev

通过WebGL支持模板缓冲区

来自分类Dev

如何将char *缓冲区转换为unsigned char缓冲区

来自分类Dev

kmalloc中的缓冲区还是DMA安全缓冲区吗?

来自分类Dev

使用单个顶点缓冲区还是多个顶点缓冲区?

来自分类Dev

使用单个顶点缓冲区还是多个顶点缓冲区?

来自分类Dev

使用C中的格式说明符从fprinft中的char缓冲区读取特定范围的索引

来自分类Dev

char []和char *的缓冲区溢出漏洞

来自分类Dev

从jni返回的字节缓冲区是副本还是引用?

Related 相关文章

  1. 1

    读取文件缓冲区通过

  2. 2

    OpenGL ES2.0 glReadPixels()从渲染缓冲区通过帧缓冲区读取数据

  3. 3

    从char缓冲区读取32位变量

  4. 4

    从char缓冲区读取一点

  5. 5

    将中间转换的char *缓冲区设置为int *

  6. 6

    如何从char缓冲区创建long int?

  7. 7

    使用imap获取电子邮件附件,但是无论是从Outlook Client还是通过Web发送电子邮件,都会得到不同的结果

  8. 8

    Matlab通过原点进行绘制,无论仅是负数据还是仅是正数据

  9. 9

    多个缓冲区还是单个缓冲区?

  10. 10

    ServerName问题,无论是www还是http,都不能同时存在..为什么呢?

  11. 11

    协议缓冲区负数int32 / int64编码器的规格模糊

  12. 12

    相同的数字代码返回不同的输出,无论是在C ++还是C中

  13. 13

    在C中获取char缓冲区(从读取的文件)中的值数

  14. 14

    nodejs缓冲区是异步还是同步?

  15. 15

    nodejs缓冲区是异步还是同步?

  16. 16

    如果我的缓冲读取器正在读取大于2 Mb的行,它将缓冲该行还是会增加缓冲区大小?

  17. 17

    我想根据值是正数还是负数执行不同类型的操作

  18. 18

    将char指针转换为int指针-缓冲区错误10

  19. 19

    无论是发行版还是镜像版,我的 Ubuntu SHA256SUM 都不匹配……我做错了什么?

  20. 20

    如何使char缓冲区充当文件?

  21. 21

    通过JavaScript中的缓冲区循环

  22. 22

    通过WebGL支持模板缓冲区

  23. 23

    如何将char *缓冲区转换为unsigned char缓冲区

  24. 24

    kmalloc中的缓冲区还是DMA安全缓冲区吗?

  25. 25

    使用单个顶点缓冲区还是多个顶点缓冲区?

  26. 26

    使用单个顶点缓冲区还是多个顶点缓冲区?

  27. 27

    使用C中的格式说明符从fprinft中的char缓冲区读取特定范围的索引

  28. 28

    char []和char *的缓冲区溢出漏洞

  29. 29

    从jni返回的字节缓冲区是副本还是引用?

热门标签

归档