7

基本问题,但我希望这个结构占用 13 个字节的空间(1 个用于 char,12 个用于 3 个无符号整数)。相反,sizeof(ESPR_REL_HEADER)给了我 16 个字节。

typedef struct {
  unsigned char version;
  unsigned int  root_node_num;
  unsigned int  node_size;
  unsigned int  node_count;
} ESPR_REL_HEADER;

我要做的是用一些值初始化这个结构,并将它包含的数据(原始字节)写入文件的开头,这样当我打开这个文件时,我以后可以重建这个结构并获得一些元数据有关文件其余部分包含的内容的数据。

我正在初始化结构并将其写入文件,如下所示:

int esprime_write_btree_header(FILE * fp, unsigned int node_size) {
  ESPR_REL_HEADER header = {
    .version       = 1,
    .root_node_num = 0,
    .node_size     = node_size,
    .node_count    = 1
  };

  return fwrite(&header, sizeof(ESPR_REL_HEADER), 1, fp);
}

node_size我实验时,目前在哪里4。

将结构写入文件后,该文件包含以下数据:

-bash$  hexdump test.dat
0000000 01 bf f9 8b 00 00 00 00 04 00 00 00 01 00 00 00
0000010

我希望它实际上包含:

-bash$  hexdump test.dat
0000000 01 00 00 00 00 04 00 00 00 01 00 00 00
0000010

原谅新手。我正在努力学习:) 如何有效地将结构的数据组件写入文件?

4

8 回答 8

6

微处理器并非旨在从任意地址获取数据。诸如 4-byte 之类int的对象只能存储在可被四整除的地址处。此要求称为对齐

C 使编译器可以自由地在结构成员之间插入填充字节以对齐它们。填充量只是不同平台之间的一个变量,另一个主要变量是字节序。这就是为什么如果您希望程序在多台机器上运行,您不应该简单地将结构“转储”到磁盘。

最佳实践是显式编写每个成员,并用于htonl在二进制输出之前将字节序固定为大字节序。回读时,用于memcpy移动原始字节,不要使用

char *buffer_ptr;
...
++ buffer_ptr;
struct.member = * (int *) buffer_ptr; /* potential alignment error */

而是做

memcpy( buffer_ptr, (char *) & struct.member, sizeof struct.member );
struct.member = ntohl( struct.member ); /* if member is 4 bytes */
于 2012-04-14T11:22:20.553 回答
3

这是因为结构填充,请参阅http://en.wikipedia.org/wiki/Sizeof#Implementation

于 2012-04-14T11:18:51.690 回答
1

当您按原样编写结构时fwrite,您将按原样写入内存,包括由于填充而插入的结构内的“死字节” 。此外,您的多字节数据是使用系统的字节序写入的。

如果您不希望这种情况发生,请编写一个函数来序列化结构中的数据。您可以只写入非填充区域,也可以以可预测的顺序(例如网络字节顺序)写入多字节数据。

于 2012-04-14T11:23:27.043 回答
1

该结构受对齐规则的约束,这意味着其中的某些项目被填充。看着它,看起来第一个unsigned char字段已经被填充到了 4 个字节。

这里的问题之一是规则可能因系统而异,因此,如果您fwrite在一个平台上使用一个编译器编译的程序中将结构作为一个整体编写,然后尝试在另一个平台上使用它来读取它fread,您可以得到垃圾,因为第二个程序将假定数据已对齐以适合其结构布局的概念。

通常,您必须:

  1. 确定保存的数据文件仅对共享某些特征的程序构建有效(取决于您使用的编译器的记录行为),或

  2. 不是将整个结构写成一个整体,而是实现一种更正式的数据格式,其中每个元素都是单独编写的,其大小显式控制。

(一个相关的问题是字节顺序可能不同;同样的选择通常也适用于那里,除了在选项 2 中您要明确指定数据格式的字节顺序。)

于 2012-04-14T11:23:59.940 回答
1

这是因为所谓的内存对齐。第一个字符被扩展为占用 4 个字节的内存。事实上,像这样的更大类型int只能在 4 字节块的开头“开始”,因此编译器用字节填充以达到这一点。

我对位图标题有同样的问题,从 2 个字符开始。我char bm[2]在结构内部使用了 a 并想知道 2 天 #$%^ 标头的第 3 和第 4 个字节去哪儿了...

如果你想防止这种情况,你可以使用__attribute__((packed))要小心,内存对齐你的程序方便运行所必需的

于 2012-04-14T11:25:50.627 回答
1

努力不要这样做!大小差异是由编译器/链接器使用的填充和对齐来优化对 vars 的访问速度造成的。语言和操作系统的填充和对齐规则。此外,由于字节顺序,在不同的硬件上写入和读取整数可能会出现问题。

将您的元数据逐个字节地写入一个不会被误解的结构中。以 Null 结尾的 ASCII 字符串是可以的。

于 2012-04-14T11:26:51.273 回答
1

I use a awesome open source piece of code written by Troy D. Hanson called TPL: http://tpl.sourceforge.net/. With TPL you don't have any external dependency. It's as simple as including tpl.c and tpl.h into your own program and use TPL API.

Here is the guide: http://tpl.sourceforge.net/userguide.html

于 2012-04-14T11:37:20.630 回答
0

如果要以特定格式写入数据,请使用unsigned char...的数组

unsigned char outputdata[13];
outputdata[0] = 1;
outputdata[1] = 0;
/* ... of course, use data from struct ... */
outputdata[12] = 0;
fwrite(outputdata, sizeof outputdata, 1, fp);
于 2012-04-14T11:24:02.120 回答