【问题标题】:fetch 32bit instruction from binary file in C从C中的二进制文件中获取32位指令
【发布时间】:2014-05-28 19:09:32
【问题描述】:

我需要从二进制文件中读取 32 位指令。 所以我现在拥有的是:

unsigned char buffer[4];
fread(buffer,sizeof(buffer),1,file);

这会将 4 个字节放入一个数组中

我应该如何将这 4 个字节连接在一起以便稍后处理 32 位指令? 或者我什至应该以不同的方式开始而不使用 fread?

我现在奇怪的方法是创建一个大小为 32 的整数数组,并用缓冲区数组中的位填充它

【问题讨论】:

  • 你知道二进制文件是存储为Little Endian还是Big Endian?
  • 为什么不直接复制到int32_t
  • “将这 4 个字节连接在一起”是什么意思?
  • 否则使用位移
  • 我相信它是大端的

标签: c file 32-bit


【解决方案1】:

答案取决于 32 位整数在二进制文件中的存储方式。 (我假设整数是无符号的,因为它确实是一个 id,并使用来自 <stdint.h> 的类型 uint32_t。)

本机字节顺序 数据在本机上以整数形式写入。只需使用fread 读取整数:

uint32_t op;

fread(&op, sizeof(op), 1, file);

基本原理:fread 将整数的原始表示读入内存。匹配的 fwrite 则相反:它将原始表示写入 thze 文件。如果您不需要在平台之间交换文件,这是存储和读取数据的好方法。

Little-endian 字节顺序 数据存储为四个字节,最低有效字节在前:

uint32_t op = 0u;

op |= getc(file);            // 0x000000AA
op |= getc(file) << 8;       // 0x0000BBaa
op |= getc(file) << 16;      // 0x00CCbbaa
op |= getc(file) << 24;      // 0xDDccbbaa

基本原理:getc 读取一个字符并返回一个介于 0 和 255 之间的整数。(为简洁起见,此处不考虑流用完且 getc 返回负值 EOF 的情况,即懒惰。)通过将读取的每个字节移动 8 的倍数或将它们与现有值一起移动来构建整数。 cmets 勾勒出它是如何工作的。正在读取大写字母,小写字母已经存在。尚未分配零。

大端字节序 数据存储为四个字节,最低有效字节在后:

uint32_t op = 0u;

op |= getc(file) << 24;      // 0xAA000000
op |= getc(file) << 16;      // 0xaaBB0000
op |= getc(file) << 8;       // 0xaabbCC00
op |= getc(file);            // 0xaabbccDD

基本原理:与上面几乎相同,只是您以另一种顺序移动字节。

您可以将 little-endian 和 big-endian 想象为将数字 120 树 (CXXIII) 写为 321 或 123。位移类似于在除以或乘以 10 的幂时移动十进制数字, 只是你把我的 8 位移到这里乘以 2^8 = 256。

【讨论】:

  • 哇,这与 getc 完美配合,现在我需要弄清楚如何获取多个指令,谢谢!
【解决方案2】:

添加

unsigned int instruction;
memcpy(&instruction,buffer,4);

到您的代码。这会将 4 个字节的缓冲区复制到单个 32 位变量。因此,您将获得 4 个字节的连接 :)

【讨论】:

  • 由于某种原因这对我不起作用,难道我的缓冲区不只是 4 个字节?
  • 不,它会正常工作。它将从buffer复制4个字节到instruction
【解决方案3】:

如果您知道文件中的 int 与运行程序的机器的字节序相同,那么您可以直接读取 int。不需要字符缓冲区。

unsigned int instruction;
fread(&instruction,sizeof(instruction),1,file);

如果您知道文件中 int 的字节顺序,但不知道运行程序的机器,那么您需要一起添加和移位字节。

unsigned char buffer[4];
unsigned int instruction;
fread(buffer,sizeof(buffer),1,file);

//big-endian
instruction = (buffer[0]<<24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3];

//little-endian
instruction = (buffer[3]<<24) + (buffer[2]<<16) + (buffer[1]<<8) + buffer[0];

另一种思考方式是,它是基于 256 的位置数字系统。因此,就像您在以 10 为基数中组合数字一样。

257 
= 2*100 + 5*10 + 7
= 2*10^2 + 5*10^1 + 7*10^0

因此您也可以使用霍纳规则将它们组合起来。

//big-endian
instruction = ((((buffer[0]*256) + buffer[1]*256) + buffer[2]*256) + buffer[3]);

//little-endian
instruction = ((((buffer[3]*256) + buffer[2]*256) + buffer[1]*256) + buffer[0]);

【讨论】:

    【解决方案4】:

    @luser droog

    您的代码中有两个错误。

    变量“指令”的大小不能是 4 个字节:例如,Turbo C 假定 sizeof(int) 为 2。显然,您的程序在这种情况下会失败。但是,更重要且不那么明显的是:如果 sizeof(int) 超过 4 个字节,您的程序也会失败!要理解这一点,请考虑以下示例:

    int main()
    {   const unsigned char a[4] = {0x21,0x43,0x65,0x87};
        const unsigned char* p = &a;
        unsigned long x = (((((p[3] << 8) + p[2]) << 8) + p[1]) << 8) + p[0];
        printf("%08lX\n", x);
        return 0;
    }
    

    这个程序在amd64下打印“FFFFFFFF87654321”,因为一个unsigned char变量在使用时变成了SIGNED INT!因此,将变量“指令”的类型从“int”更改为“long”并不能解决问题。

    唯一的办法就是写这样的东西:

    unsigned long instruction;
    
    instruction = 0;
    for (int i = 0, unsigned char* p = buffer + 3; i < 4; i++, p--) {
        instruction <<= 8;
        instruction += *p;
    }
    

    【讨论】:

      猜你喜欢
      • 2015-11-11
      • 2011-09-25
      • 1970-01-01
      • 2021-09-07
      • 2012-10-12
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      • 2015-04-27
      相关资源
      最近更新 更多