如何在没有填充位的情况下用 C 语言输出二进制文件

How to output a binary file in C without padding bits

我想将结构的数据输出到二进制文件,但每个变量的信息之间没有任何填充位。例如:

struct s {
    int i1;
    short s1;
    char c1;
};
struct s example[2];

如果我使用fwrite(&example, sizeof(struct s), 2, file),二进制文件仍然有填充位,例如s1c1,以及c1i1 (of the 2nd struct).

从输出文件中删除这些填充位的好方法是什么?

谢谢!感谢任何帮助

许多编译器接受一个命令行参数,这意味着 "pack structures"。另外,很多接受一个pragma:

#pragma pack(1)

其中1表示字节对齐,2表示16位字对齐,4表示32位字对齐等

为了使您的解决方案独立于平台,您可以创建一个函数,一次写入 struct 的每个字段,然后调用该函数写入尽可能多的 struct根据需要。

int writeStruct(struct s* obj, size_t count, FILE* file)
{
   size_t i = 0;
   for ( ; i < count; ++i )
   {
      // Make sure to add error checking code.
      fwrite(&(obj[i].i1), sizeof(obj[i].i1), 1, file);
      fwrite(&(obj[i].s1), sizeof(obj[i].s1), 1, file);
      fwrite(&(obj[i].c1), sizeof(obj[i].c1), 1, file);
   }

   // Return the number of structs written to file successfully.
   return i;
} 

用法:

struct s example[2];
writeStruct(s, 2, file);

我只是手动建议 reading/writing 结构的成员。使用您的编译器指令打包可能会导致低效和可移植性问题以及未对齐的数据访问。如果您必须处理字节序,那么稍后当您的读取操作分解为字段成员而不是整个结构时,很容易支持它。

另一件事,这更多地与未来的维护类型问题有关,是如果您稍微更改结构(添加新元素),您不希望您的序列化代码或人们到目前为止保存的文件被破坏甚至更改顺序作为缓存行优化,例如)。因此,与将结构的内存内容直接转储到文件中相比,提供更多喘息空间的代码可能会 运行 减少很多痛苦,而且通常最终值得序列化您的会员个人。

如果你想概括一个模式并减少你编写的样板文件的数量,你可以做这样的事情作为一个基本的例子来开始和构建:

struct Fields
{
     int num;
     void* ptrs[max_fields];
     int sizes[max_fields];
};

void field_push(struct Fields* fields, void* ptr, int size)
{
    assert(fields->num < max_fields);
    fields->ptrs[fields->num] = ptr;
    fields->sizes[fields->num] = size;
    ++fields->num;
}

struct Fields s_fields(struct s* inst)
{
    struct Fields new_fields;
    new_fields.num = 0;

    field_push(&new_fields, &inst->i1, sizeof inst->i1);
    field_push(&new_fields, &inst->s1, sizeof inst->s1);
    field_push(&new_fields, &inst->c1, sizeof inst->c1);

    return new_fields;
}

现在您可以使用带有通用函数的 Fields 结构来读写任何结构的成员,如下所示:

void write_fields(FILE* file, struct Fields* fields)
{
    int j=0;
    for (; j < fields->num; ++j)
         fwrite(fields->ptrs[j], fields->sizes[j], 1, file);
}

这通常比某些接受回调的函数式 for_each_field 方法更容易使用。

现在,当您创建一些新结构 S 时,您所需要担心的就是定义一个函数来从实例输出 struct Fields,然后启用您编写的所有这些通用函数与 struct Fields 一起工作,现在自动与这个新的 S 类型一起工作。