阅读 hexdump,恐惧

Reading hexdump, fread

我有一个包含以下十六进制值的二进制文件。

使用正确的代码读取此二进制文件应显示以下内容:

第一张图中绿色高亮区域代表father, room, attraction, cake字段,输出值分别为1,1,0,1。

我必须弄清楚在下面编写的代码中,哪些数据类型用于为结构变量打印出 1,1,0,1。

我猜到 1101 来自十六进制 'd'。但是,我不确定它是如何在 4 个不同的字段中打印出来的。

我知道我的问题与结构填充或位域有关。但是,我仍然不确定我的代码和fread应该如何修改。

如果您能帮助我解决这个问题或提供示例或相关阅读材料,我们将不胜感激。

#define MAX_HAT 9
#include<stdio.h>
#include<stdlib.h>

//struct variables
struct test
{
    short int land;
    float experience;
    char boys;
    short int angle;
    double industry;
    int thread;
    long int shoe;
    float kitty;
    unsigned char price;
    
    //not sure whether this is done correctly
    unsigned int father: 1;
    unsigned int room: 1;
    unsigned int attraction: 1;
    unsigned int cake: 1;
    
    int foot;
    char hat[MAX_HAT];
    char nest;
    float bean;
};

int main(int argc, char **argv)
{
    struct test t1;
    
    FILE *fp;

        //Input Checking Error
        if (argc < 2) {
                fprintf(stderr, "Usage: %s input_file\n", argv[0]);
                exit(1);
        }
    
        //binary file to open for reading
        fp = fopen(argv[1], "rb");
    
        //File Checking Error
        if (fp == NULL){
                fprintf(stderr, "Cannot open the file %s\n", argv[1]);
                exit(1);
        }

    //Print out struct fields
    printf("land, experience, boys, angle, industry, thread, shoe, kitty, price, father, room, attraction, cake, foot, hat, nest, bean \n");

    //Allocate the values into the struct
    while(fread(&t1.land, sizeof(t1.land), 1, fp) == 1)
    {
        fread(&t1.experience, sizeof(t1.experience), 1, fp);
        fread(&t1.boys, sizeof(t1.boys), 1, fp);
        fread(&t1.angle, sizeof(t1.angle), 1, fp);
        fread(&t1.industry, sizeof(t1.industry), 1, fp);
        fread(&t1.thread, sizeof(t1.thread), 1, fp);
        fread(&t1.shoe, sizeof(t1.shoe), 1, fp);
        fread(&t1.kitty, sizeof(t1.kitty), 1, fp);
        fread(&t1.price, sizeof(t1.price), 1, fp);
        fread(&t1.father, sizeof(t1.father), 1, fp);
        fread(&t1.room, sizeof(t1.room), 1, fp);
        fread(&t1.attraction, sizeof(t1.attraction), 1, fp);
        fread(&t1.cake, sizeof(t1.cake), 1, fp);
        fread(&t1.foot, sizeof(t1.foot), 1, fp);
        fread(&t1.hat, sizeof(t1.hat), 1, fp);
        fread(&t1.nest, sizeof(t1.nest), 1, fp);
        fread(&t1.bean, sizeof(t1.bean), 1, fp); 
                
    //Print out the outputs
    printf("%d, %f, %i, %i, %f, %d, %ld, %f, %u, %, %, %, %, %x, %s, %c, %f\n", t1.land, t1.experience, t1.boys, t1.angle, t1.industry, t1.thread, t1.shoe, t1.kitty, t1.price, t1.father, t1.room, t1.attraction, t1.cake,t1.foot, t1.hat, t1.nest, t1.bean);
        
    }

    //close the file
    fclose(fp);

    return 0;
}

线条

fread(&t1.father, sizeof(t1.father), 1, fp);
fread(&t1.room, sizeof(t1.room), 1, fp);
fread(&t1.attraction, sizeof(t1.attraction), 1, fp);
fread(&t1.cake, sizeof(t1.cake), 1, fp);

错了。您不能对 bit-field 的成员使用 sizeof。这样做没有意义,因为位域成员可以小于一个字节,但 sizeof 可以 return 的最小大小是一个字节。另外,fread可以读取的最小单位是一个字节,而不是一位。

在您的问题中,您声明“突出显示区域”(2 个字节)代表 fatherroomattractioncake。但是,ISO C 只要求编译器支持大小为 int 的位域,在大多数平台上为 4 个字节。您可能想尝试使用 uint16_t(需要 #include <stdint.h>)而不是 unsigned int 作为位字段的基础数据类型,并希望您的编译器支持它。否则,输入数据的大小和位域的大小将不匹配,这会使事情变得更复杂。

还有一个问题是,ISO C没有规定比特在比特域中的存储顺序,即比特是从右到左还是从左到右打包。因此,如果您的输入将另一端的位打包为您的编译器,那么您将不得不对此进行补偿。

不使用位域,使用按位与运算符 & 屏蔽单个位并使用 >> 运算符移位它们可能更容易。结合使用这两个运算符可以提取单个位。

假设您有一个类型为 uint16_t 的变量 a,您可以使用以下代码将输入文件中的两个字节读入该变量:

if ( fread( &a, sizeof a, 1, fp) != 1 )
    /*handle error*/;

之后,如果你假设fatherleast-significant bit中表示,那么你可以提取这些位并使用以下代码将它们分配给适当的变量:

bool father     = ( a & (0x01<<0) ) >> 0;
bool room       = ( a & (0x01<<1) ) >> 1;
bool attraction = ( a & (0x01<<2) ) >> 2;
bool cake       = ( a & (0x01<<3) ) >> 3;

请注意,要能够使用数据类型 bool,您必须 #include <stdbool.h>

有关详细信息,请参阅 bitwise operations in C

如果您想用位域解决问题,那么我建议您更改行

unsigned int father: 1;
unsigned int room: 1;
unsigned int attraction: 1;
unsigned int cake: 1;

以下

uint16_t father: 1;
uint16_t room: 1;
uint16_t attraction: 1;
uint16_t cake: 1;

并希望您的编译器支持它(gcc 支持)并希望编译器从您输入的同一侧打包这些位。

但是,还有一个问题就是bit-field的地址是取不到的,因为它不一定是从具体的地址开始的。因此,您不能将位域的地址传递给 fread.

因此,您必须将位域包装到它们自己的子结构中,如下所示:

struct test
{
    [...]

    struct
    {
        uint16_t father: 1;
        uint16_t room: 1;
        uint16_t attraction: 1;
        uint16_t cake: 1;
    } bitfield;

    [...]
};

现在,您仍然无法获取任何位域成员的地址。但是,您可以获取包含它们的 struct 的地址,并将其传递给 fread.