【问题标题】:Writing Constant struct to Binary File not deterministic?将常量结构写入二进制文件不确定?
【发布时间】:2021-09-03 17:26:00
【问题描述】:

我有一个将常量结构写入二进制文件的程序。我需要它与以不同方式创建的另一个二进制文件完全匹配。但是,每次我运行我的可执行文件时,生成的二进制文件都是不同的。我需要每次生成的文件都相同。

重现问题的代码:

main.c:

#include <stdio.h>

typedef struct {
  double vector1[3];
  double vector2[3];
  unsigned int a_uint32_field;
  unsigned char a_uint8_field;
} Struct_type;

void CreateStruct(Struct_type* Struct_instance) {
  Struct_instance->vector1[0] = 0.0;
  Struct_instance->vector2[0] = 0.0;
  Struct_instance->vector1[1] = 0.0;
  Struct_instance->vector2[1] = 0.0;
  Struct_instance->vector1[2] = 0.0;
  Struct_instance->vector2[2] = 0.0;
  Struct_instance->a_uint32_field = 0U;
  Struct_instance->a_uint8_field = 0U;
}

int main() {
  Struct_type Struct_instance;
  FILE* file_pointer;

  CreateStruct(&Struct_instance);

  file_pointer = fopen("Saved_Struct.bin", "wb");
  fwrite((void*)&Struct_instance, sizeof(Struct_instance), 1, file_pointer);
  fclose(file_pointer);
  
  return (0);
}

编译:

gcc -o executable main.c -m32 -O0

然后运行:

./executable

第一次运行,文件以十六进制\AE\CB\FF结尾,第二次是\F4\9C\FF。删除旧文件或让fopen 删除它似乎没有什么区别。它应该以全零结束,即:00\00\00

为什么会这样?我该如何预防?

【问题讨论】:

  • 填充会伤害你 - 在读取和写入之前尝试将结构归零。类似memset(&amp;Struct_instance, 0, sizeof Struct_instance);
  • 也就是说,尝试在CreateStruct 的开头添加memset(Struct_instance, 0, sizeof(*Struct_instance))
  • 我想知道Struct_type Struct_instance = {0}; 是否能解决问题?可能无法保证。
  • @yano 结构体所在堆栈上的垃圾数据。如果填充字节未初始化为任何内容,它们将保留此垃圾。
  • = {0} 初始化程序会将填充初始化为全零位。请参阅 6.7.9/21,它说如果初始化器的数量少于结构成员,则结构的其余部分应像具有静态存储持续时间的对象一样初始化。并且 6.7.9/10 说 “如果它是一个聚合,则每个成员都根据这些规则进行初始化(递归),并且任何填充都初始化为零位;”

标签: c gcc binaryfiles


【解决方案1】:

问题在于填充字节。你无法控制它们的价值。对于您的特定结构,结构末尾可能有 3 个填充字节。它们可以有任何值。

这在 6.2.6.1(草案 N1570)中有描述:

当值存储在结构或联合类型的对象中时,包括在成员中 对象,对应于任何填充字节的对象表示的字节采用 未指定的值

因此,即使您可以通过使用短初始值设定项或使用callocmemset 等从填充中的零位开始,它也只适用于第一个赋值。

进一步阅读:Is it guaranteed that the padding bits of "zeroed" structure will be zeroed in C?

确保每次都获得相同的二进制文件的唯一方法是在写入文件之前去掉填充。这可以通过使用打包结构来完成。例如见What is a "packed" structure in C?

作为打包结构的替代方案,您可以编写结构使其没有填充。喜欢:

typedef struct {
  double vector1[3];
  double vector2[3];
  unsigned int a_uint32_field;
  unsigned char a_uint8_field;
  unsigned char mypadding[3];  // 3 is just a guess
} Struct_type;

然后有一个(静态)断言来检查结构的大小实际上是否等于各个成员的大小之和。

还有另一种选择:不要使用单个fwrite 将整个结构写入文件。使用多个fwrite 将各个成员一一写入。这样填充就不会进入文件了。

【讨论】:

  • 再次感谢您的帮助 =)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多