为什么 fwrite 为 uint8_t 写入两个字节?

Why does fwrite write two bytes for an uint8_t?

C不是很熟练,这可能只是一个新手问题。

我正在尝试将 3 个字节写入文件,但最后是 4 个字节。

#include <stdio.h>
#include <stdlib.h>

struct a {
  uint8_t x;
  uint16_t y;
};

int main()
{
  struct a record = { 1, 2 };
  FILE *fp = fopen("example.bin", "w");
  fwrite(&record, sizeof(struct a), 1, fp);
  fclose(fp);
}

出于某种原因,我最终得到:

$ hexdump -C example.bin
00000000  01 00 02 00                                       |....|
00000004

我期待:01 02 00

这是我的 c 编译器的版本,以防与 hardware/compiler 相关。

$ cc --version
Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.5.0
Thread model: posix

结构通常由编译器填充 - 在 x 之后插入空 space,以便 y 与 16 位边界对齐。

GCC 提供 __attribute__((packed)) 来禁用此行为;见 these docs

I am trying to write 3 bytes to file, but end up with 4.

您可能旨在写入 3 个字节,但实际上正在写入 sizeof(struct a),这很可能是 4,因为编译器插入了一个填充字节以进行对齐。也许您假设结构的大小等于其成员大小的总和,并且没有考虑可能的填充。

一般来说,编译器可以自由地以对齐方式插入填充(除非在结构的第一个成员之前不能有任何填充)。

如果您编写单个成员,您会看到预期的输出:

  fwrite(&record.x, sizeof record.x, 1, fp);
  fwrite(&record.y, sizeof record.y, 1, fp);

P.S.: 确保错误检查所有可能失败的函数(fopen、fwrite 等)。