将常量结构写入二进制文件不是确定性的吗?
Writing Constant struct to Binary File not deterministic?
我有一个程序可以将常量结构写入二进制文件。我需要它与以不同方式创建的另一个二进制文件完全匹配。但是,每次我 运行 我的可执行文件时,生成的二进制文件都是不同的。我需要每次生成的文件都一样。
重现问题的代码:
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
,第二次是\F4C\FF
。删除旧文件或让 fopen
删除它似乎没有什么区别。它应该以全零结尾,即:00[=16=][=16=]
为什么会这样?我该如何预防?
问题是填充字节。你无法控制它们的价值。对于您的特定结构,结构末尾可能有 3 个填充字节。它们可以有任何值。
这在 6.2.6.1(草案 N1570)中有描述:
When a value is stored in an object of structure or union type, including in a member
object, the bytes of the object representation that correspond to any padding bytes take
unspecified values
因此,即使您可以通过使用短初始化程序或使用 calloc
、memset
等从填充中的零位开始,它也只适用于第一个赋值。
进一步阅读:
确保每次都获得相同二进制文件的唯一方法是在写入文件之前去掉填充。这可以通过使用打包结构来完成。例如参见 [=17=]
作为打包结构的替代方法,您可以编写没有填充的结构。喜欢:
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;
然后有一个(静态)断言来检查结构的 sizeof 实际上等于各个成员的 sizeof 的总和。
还有另一种选择:不要使用单个 fwrite
将整个结构写入文件。使用多个 fwrite
一一写下个人成员。这样填充就不会进入文件。
我有一个程序可以将常量结构写入二进制文件。我需要它与以不同方式创建的另一个二进制文件完全匹配。但是,每次我 运行 我的可执行文件时,生成的二进制文件都是不同的。我需要每次生成的文件都一样。
重现问题的代码:
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
,第二次是\F4C\FF
。删除旧文件或让 fopen
删除它似乎没有什么区别。它应该以全零结尾,即:00[=16=][=16=]
为什么会这样?我该如何预防?
问题是填充字节。你无法控制它们的价值。对于您的特定结构,结构末尾可能有 3 个填充字节。它们可以有任何值。
这在 6.2.6.1(草案 N1570)中有描述:
When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values
因此,即使您可以通过使用短初始化程序或使用 calloc
、memset
等从填充中的零位开始,它也只适用于第一个赋值。
进一步阅读:
确保每次都获得相同二进制文件的唯一方法是在写入文件之前去掉填充。这可以通过使用打包结构来完成。例如参见 [=17=]
作为打包结构的替代方法,您可以编写没有填充的结构。喜欢:
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;
然后有一个(静态)断言来检查结构的 sizeof 实际上等于各个成员的 sizeof 的总和。
还有另一种选择:不要使用单个 fwrite
将整个结构写入文件。使用多个 fwrite
一一写下个人成员。这样填充就不会进入文件。