基本问题,但我希望这个结构占用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
请原谅新生儿。我正在努力学习:)如何有效地将我的struct的数据组件写入文件?
答案 0 :(得分:6)
微处理器不是为从任意地址获取数据而设计的。诸如4字节int
s之类的对象应仅存储在可被4整除的地址中。此要求称为alignment。
C使编译器可以自由地在struct成员之间插入padding bytes以对齐它们。填充量只是不同平台之间的一个变量,另一个主要变量是endianness。这就是为什么如果你想让程序在多台机器上运行,你不应该简单地将结构“转储”到磁盘上。
最佳做法是明确地编写每个成员,并在二进制输出之前使用htonl
将endianness修复为big-endian。回读时,使用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 */
答案 1 :(得分:3)
答案 2 :(得分:1)
当您使用fwrite
编写结构时,您将在内存中进行编写,包括由于 padding 而插入的结构内部的“死字节”。此外,您的多字节数据是使用系统的 endiannes 编写的。
如果您不希望发生这种情况,请编写一个序列化结构中数据的函数。您只能编写非填充区域,并以可预测的顺序写入多字节数据(例如,在network byte order中)。
答案 3 :(得分:1)
结构符合对齐规则,这意味着它中的一些项目被填充。看一下,看起来第一个unsigned char
字段已填充到4个字节。
这里的一个问题是规则可能因系统而异,所以如果在一个平台上用一个编译器编译的程序中使用fwrite
编写整个结构,然后尝试在另一个上使用fread
读取它,你可能会得到垃圾,因为第二个程序会假设数据是对齐的,以适应它的结构布局的概念。
通常,您必须:
确定保存的数据文件仅对共享某些特征的程序版本有效(取决于您使用的编译器的记录行为),或
不要将整个结构整体编写为一个,而是实现更正式的数据格式,其中每个元素都是单独编写的,其大小是明确控制的。
(相关问题是字节顺序可能不同;同样的选择通常也适用于此,除了在选项2中您要明确指定数据格式的字节顺序。)
答案 4 :(得分:1)
这是因为称为内存对齐的东西。第一个char扩展为占用4个字节的内存。事实上,像int
这样的较大类型只能在4个字节的块的开头“开始”,因此编译器会填充字节以达到这一点。
我遇到了与位图标题相同的问题,从2个char开始。我在结构体中使用了char bm[2]
并想知道2天里#$%^标题的第3个和第4个字节在哪里...
如果您想阻止这种情况,可以使用__attribute__((packed))
但beware, memory alignment IS necessary to your program to run conveniently。
答案 5 :(得分:1)
尽量不要这样做!大小差异是由编译器/链接器使用的填充和对齐引起的,以便通过速度优化对变量的访问。填充和对齐规则与语言和操作系统。此外,由于字节顺序,在不同的硬件上写入和读取它们可能会有问题。
将字符串逐字节写入一个不会被误解的结构中。以空值终止的ASCII字符串正常。
答案 6 :(得分:1)
我使用了一个由Troy D. Hanson编写的令人敬畏的开源代码段,称为TPL:http://tpl.sourceforge.net/。 使用TPL,您没有任何外部依赖。这就像将tpl.c和tpl.h包含到您自己的程序中并使用TPL API一样简单。
答案 7 :(得分: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);