如何在C语言中将二进制文件读入结构体

5

我有一个名为product的结构体,我正在尝试从二进制文件中读取它以填充此函数:

void reading(FILE *fp){

  Product *obuff = malloc(sizeof(Product));
  fread(obuff->code, sizeof(obuff->code), 1, fp);
  fread(obuff->name, sizeof(obuff->name), 1, fp);
  fread(obuff->quantity, sizeof(obuff->quantity), 1, fp);
  fread(obuff->price, sizeof(obuff->price), 1, fp);   

  printf("%s %s %d %.2f\n", obuff.code, obuff.name, obuff.quantity, obuff.price);
}

当我尝试编译时,出现错误提示无法通过错误数据类型传递参数。是否有一种方法可以从二进制文件中读取结构,还是我在这里做错了什么?
结构体:
#pragma pack(2)
struct product {
   char code[15];
   char name[50];
   short int quantity;
   double price;
};
#pragma pack()

typedef struct product Product;

2
除非您从完全相同的“struct”和完全相同的系统(相同的ABI,相同的字节序)生成了文件,否则使用适当的移位和位运算进行编组是更好的方法。 - too honest for this site
1
将答案编辑到问题中并不是一个好的做法,因为对于第一次看到问题的人来说现在会变得混乱。最好的方法是撤销您的编辑,接受答案,然后发布一个关于新错误的新问题。顺便提一下,包括一个MCVE将获得更有用的答案。 - M.M
@M.M 我的错误。我现在会立即处理。 - s d
@sd,我认为你应该考虑更改被接受的答案。 - Antti Haapala -- Слава Україні
4个回答

4
你需要传递指针才能使用fread()函数读取数据。
void reading(FILE *fp){

  Product *obuff = malloc(sizeof(Product));
  fread(&obuff->code, sizeof(obuff->code), 1, fp);
  fread(&obuff->name, sizeof(obuff->name), 1, fp);
  fread(&obuff->quantity, sizeof(obuff->quantity), 1, fp);
  fread(&obuff->price, sizeof(obuff->price), 1, fp);

  printf("%s %s %d %.2f\n", obuff->code, obuff->name, obuff->quantity, obuff->price);
  free(obuff); /* free whatever you allocated after finished using them */
}

哦,我明白了。谢谢你。它能够编译通过,但是当我执行它时,在 fread 语句的 obuff->name 附近会出现分段错误。 - s d
1
@sd 你用过valgrind吗?它非常容易使用,当你遇到segfaults时我强烈推荐使用。在这种情况下,我认为你遇到了一些堆栈或堆的损坏(意味着segfault的原因位于你提供的代码之外),或者你在欺骗printf,告诉它obuff->codeobuff->name是字符串,而实际上它们不是(就我们看到的而言)。 - autistic

1

这段代码存在几个问题。其中一些问题将导致编译失败,而其他问题可能会导致不稳定的行为。由于不稳定的行为可能不容易被注意到,因此我将首先解决这个问题。


在尝试使用 malloc 返回值之前,您应始终检查它。 例如:

Product *obuff = malloc(sizeof *obuff);
if (obuff == NULL) {
    /* obuff can't be used because allocation failed */
    return;
}

回复内容存在敏感词^**$、realloccalloc分配的内存时,可以通过使用free来避免内存泄漏...

另一个相关的问题是,除非fread的返回值表示成功,否则不应使用obuff->codenamequalityprice的值。


fread(&obuff->code, sizeof(obuff->code), 1, fp);
fread(&obuff->name, sizeof(obuff->name), 1, fp);

这将编译,但是您不需要和事实上不应该在此处使用“&”符号。表达式“obuff->code”和“&obuff->code”都指向同一位置(因为“obuff->code”是一个数组),但它们指向的对象类型不同;“obuff->code”指向数组的第一个字节,而“&obuff->code”指向整个数组。
fread(&obuff->quantity, sizeof(obuff->quantity), 1, fp);
fread(&obuff->price, sizeof(obuff->price), 1, fp);  

这将编译通过,并且为了澄清,在这里你确实需要使用“&”符号,但是需要说明的是,在不同的系统上,short int和double可能具有不同的表示形式。你需要通过对这些字段进行串行化来形成文件中一致的表示形式。串行化是一个冗长的主题,更适合放在软件设计书的章节中进行讨论,所以为了简洁起见,我将继续进行,除非在此处提出进一步的具体问题。
#pragma pack(2)

这是不可移植的,在您提供的代码中没有真正的需要。
printf("%s %s %d %.2f\n", obuff.code, obuff.name, obuff.quantity, obuff.price);

预期%s对应一个字符串指针;字符串是以第一个'\0'字符结尾的字符序列。但是,您的代码没有显式分配任何'\0'字符,因此我们无法保证obuff.codeobuff.name 字符串。如果它们不是,行为是未定义的(或者如我所描述的那样不稳定)。也许您想使用%15s%50s分别表示它们可能是字符串,但如果不是,有最大长度吗?

这也是编译错误的来源。请注意,在之前的代码片段中,您引用了obuff->codeobuff->name等内容,而在此片段中,您引用了obuff.code等内容。点运算符(.)用于访问结构体的字段,而箭头运算符(->)用于访问指向的结构体的字段……也许您在这里应该使用箭头运算符?

为什么我们不应该在 obuff->code 前使用 &?我认为将指针传递给整个数组是一个好习惯,因为参数表明 fread() 应该读取大小为 sizeof(obuff->code)一个 元素,这应该意味着整个数组。 - MikeCAT
另外,为什么不停止使用 malloc(),而是直接使用常规变量,如 Product obuff; - MikeCAT
@MikeCAT 这是一个不必要的字符,我们应该教导人们何时何地需要使用取地址运算符,而不是因为 魔法 而教导人们在 fread 中总是使用取地址运算符。关于您提出的 malloc 建议,您提出了一个很好的观点;在这种情况下,应当明确使用自动存储期。 - autistic
@MikeCAT 考虑一下在这里使用取地址运算符可能会错误地转换为在诸如 scanffgets 这样的函数中使用取地址运算符,其中提供错误类型的参数可能会导致警告或错误消息。如果出现警告,我们应该教导人们分析它们,而不是“哦,我被教导总是使用取地址运算符,那编译器知道什么?”这是我们可以通过教导人们何时需要以及何时不需要使用 & 来避免的错误类型。 - autistic
在使用&与数组盲目结合时,还会遇到另一个问题,即有人可能以为应该“将其用于函数参数中的Product foo[]变量,因为它也是一个‘数组’”。 - Antti Haapala -- Слава Україні

0

obuff->code 确实不是指针,但它是一个数组... 表示数组的表达式将被隐式转换为指向数组第一个元素的指针。你使用的参考资料既不权威(请使用 opengroup 或三个 C 标准之一),也没有树立良好的榜样(在示例中忽略了 fopen 的返回值并打印了一个潜在的非字符串作为字符串,这两者都会导致常见的问题,在 StackOverflow 上被问到)。你在读哪本书?我可以推荐 K&R 2E。 - autistic

0
请注意fread()函数的定义,第一个参数需要是指针;另外一个小错误是我们需要使用“->”来获取printf()函数中的元素。
以下是我的修改:
void reading(FILE *fp){

  Product *obuff = malloc(sizeof(Product));
  fread(obuff->code, sizeof(obuff->code), 1, fp);
  fread(obuff->name, sizeof(obuff->name), 1, fp);
  fread(&obuff->quantity, sizeof(obuff->quantity), 1, fp);
  fread(&obuff->price, sizeof(obuff->price), 1, fp);   

  printf("%s %s %d %.2f\n", obuff->code, obuff->name, obuff->quantity, obuff->price);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接