Issue
I have a program that writes a constant struct to a binary file. I need it to be an exact match of another binary file that was created in a different manner. However, each time I run my executable, the resulting binary file is different. I need the produced file to be the same every time.
The code to reproduce the problem:
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);
}
Compile with:
gcc -o executable main.c -m32 -O0
Then run:
./executable
The first time I've run it, the file ended with hexadecimal \AE\CB\FF
, the second time it was \F4\9C\FF
. Deleting the older file or letting it be erased by fopen
seems to make no difference. It was supposed to end with all zeros, that is: 00\00\00
Why does this happen? How can I prevent it?
Solution
The problem is padding bytes. You can't control their value. For your specific struct, it's likely that you have 3 padding bytes at the end of the struct. They can have any values.
This is described in 6.2.6.1 (draft 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
So even if you can start out with zero bits in the padding by using a short initializer or by using calloc
, memset
etc., it only holds to the first assignment.
For further reading: Is it guaranteed that the padding bits of "zeroed" structure will be zeroed in C?
The only way to be sure to get the same binary file every time is to get rid of the padding before writing the file. That can be done by using a packed struct. See for instance What is a "packed" structure in C?
As an alternative to packed struct you can write your struct so it has no padding. Like:
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;
and then have a (static) assert to check that sizeof the struct actually equals the sum of the sizeof the individual members.
Yet another alternative: Don't write the whole struct to the file using a single fwrite
. Write the individual members one by one using multiple fwrite
. In this way the padding won't go to the file.
Answered By - 4386427