在C语言中,结构体是否保证按sizeof(size_t)对齐?

3

我有以下结构体:

struct data {
        char buffer[12];
        bool flag;
}

当测试 data 结构的大小时,我意识到它是 16 字节,因为布尔标志添加了 3 个字节的填充。因此,我决定在使用 buffer 的最后一个字节作为标志时执行以下操作:

struct data {
        char buffer[16];
}

这样我的buffer会多出3个字节的免费空间因为它本来就会在填充中被浪费。

然而,我希望这个结构是与平台无关的,因此我查看了cppreference,找到以下内容(强调是我的):

每种完整对象类型都有一个名为对齐要求的属性,它是一个size_t类型的整数值,表示此类型的对象可以分配的连续地址之间的字节数。

基于此,我编写了以下内容:

struct data {
        char buffer[12 + sizeof(size_t)];
}

它在我的设备上运行良好,但我只是想知道这是否保证在所有平台上正确对齐并且不浪费任何字节?
编辑:我知道sizeof(size_t)可能是8个字节,使我的结构体为20个字节。但是,如果对齐方式是8个字节,则无论如何我的结构体都将是20个字节。这就是为什么我想知道结构体是如何对齐的。

4
我会说,不要费心。从设计的角度来看,在数组中使用布尔标志没有任何意义。从可读性、可理解性和可维护性的角度来看也是如此。而且过早地进行优化通常是不好的。编译器将确保结构对象被适当地对齐。唯一需要关注这些事情的时候是如果你有纳秒级的时间要求,或者极端的内存限制。 - Some programmer dude
3
顺便说一下,sizeof(size_t)实际上可能是个字节,这将使你的结构体大小为20个字节。 - Some programmer dude
1
如果你不想浪费空间,为什么不这样做 struct data { char buffer[15]; char flag; } 或者类似的方式呢? - Steve Summit
1
@SteveSummit 这并不是解决方案,因为您的 char 可能会对齐,即使结构体被打包,下一个对象也将对齐,仅留下这个潜在打包结构体和该对象之间未使用的间隙。 - 0___________
显示剩余16条评论
5个回答

3

在C语言中,结构体是否对齐到sizeof(size_t)并没有保证。


我猜这个方法可以达到OP的目的,即获取填充字节。成员的顺序很重要,应该将最小的成员放在最后面,char类型始终是最小的。

struct data_ref {
  bool flag;
  char buffer[12];
};

#define PAD (sizeof(struct data_ref) - sizeof(char)*12 - sizeof(bool))

struct data {
  bool flag;
  char buffer[12 + PAD];
};

2
@Stargateur,你认为在这里如何使用offsetof - chux - Reinstate Monica
它不会节省任何字节,只是使结构体的大小更小。如果它是唯一的对象,那么这是真实的。否则,它将在您的结构体之后添加填充。 - 0___________
那是一个好问题...算了吧!我对你的例子感到不舒服,但是...是的,有些地方不对,但我不知道是什么。 - Stargateur
@Stargateur 或许可以使用 #define PAD (sizeof(struct data_ref) - offsetof(data_ref, buffer)/*the last member*/ - sizeof(char)*12)?虽然这个答案还不能很好地处理具有许多成员的更复杂的 struct,但你的偏移量想法有助于找到从最后一个成员到结尾的填充。 - chux - Reinstate Monica

1

C标准没有规定struct内部字段的对齐方式。第6.7.2.1节第14段C标准说明:

结构体或联合体对象的每个非位字段成员都以适合其类型的实现定义方式对齐。

因此,不能保证给定的结构体在相同源代码的两个不同编译版本中以相同的方式布局,无论是不同的编译器还是具有不同优化设置的同一编译器。

作为反例,考虑以下结构体:

struct test1 {
    char s[13];
};

在我的机器上,sizeof(struct test1) 的值为13,_Alignof(struct test1) 的值为1。事实上,使用您的第一个struct data定义得到了相同的结果,表明没有填充。因此,不能保证结构体对sizeof(size_t)边界对齐。
关于您提供的引用:
每个完整的对象类型都有一个称为对齐要求的属性,它是一个size_t类型的整数值,表示可以分配该类型对象的连续地址之间的字节数。
这说明所有类型都有一个对齐要求,并且该值的类型为size_t,而不是类型必须具有sizeof(size_t)的对齐方式。
话虽如此,结构体通常会按照一定的布局方式排列,如果遵循这些约定,通常可以减小结构体的大小。
一般来说,结构体的对齐要求取决于其字段中最大的对齐需求,而数组的对齐要求与其所包含的类型相同。此外,大小为n字节的原始类型通常需要在n字节边界上对齐。因此,在结构体中有具有不同对齐要求的字段时,通常会插入填充以确保每个字段的对齐要求得到满足。
在您的结构中,您有一个元素大小为1的char数组和一个大小(至少在我的机器上)也为1的bool,因此整个结构的对齐要求为1,无需填充。
利用这些属性,通常可以减小结构的大小。 结构打包的失落艺术详细介绍了这一点。
然而,需要再次强调的是,按照标准,结构体中字段的布局方式是实现定义的。因此,尽管您可能能够布置结构体以最小化填充,但仍然不能保证在所有环境中始终相同。

仅仅因为这个神奇的链接介绍了C结构体打包的失传艺术,我需要能够点赞不止一次 ;) - Stargateur

1

在C语言中,结构体是否保证按照sizeof(size_t)对齐?

不是的。为什么要这样做呢?


@MichaelSmith 是的,所有对象都有对齐要求。这与 sizeof (size_t) 有什么关系? - melpomene
我认为你的回答太长了,“不”就足够了。 - Stargateur
@MichaelSmith 是的,对齐方式是 size_t 类型。例如,char 是一个对象类型,其对齐要求为 (size_t)1。数字的类型与其值有什么关系? - melpomene
2
@MichaelSmith 不要太在意 - 但我认为这个话题对你来说还为时过早。你应该相信我们,并记住你的解决方案不会节省任何字节。 - 0___________
@PeterJ_01,“你的解决方案不会为你节省任何字节。”是这样吗?被接受的答案直接与此相矛盾。 - Michael Smith
显示剩余5条评论

0
结构体在C语言中是否保证按照sizeof(size_t)对齐?
不是的。
你误解了引用段落中size_t的用法。类型的对齐方式是一个size_t类型的值。例如,char类型的对齐方式为(size_t)1,仅包含单个char成员的结构体本身的对齐方式可能为(size_t)1(或者可能具有更大的对齐方式)。
size_t只是用于表示对齐值的类型。类型size_t的大小或对齐方式与结构体的大小或对齐方式无关,除非结构体恰好具有size_t类型的成员。

-3

struct的所需对齐方式是通过其任何成员中最大的对齐方式来测量的。

例如,只包含char类型的struct将以char对齐方式对齐(在大多数情况下为单个字节)。

包含int的结构体(有时使用bool实现int)也将以int对齐方式对齐,并包括结构体中所需的任何填充以使int对齐。

这种对齐方案背后的逻辑是为了struct数组。

i.e.:

/* assuming int requires a 4 byte boundary alignment */
struct example_s {
  char bytes[2];
  /* char padding[2]; //  2 byte padding so `int` is aligned */
  int i;
};

struct example_s example_array[10];

这将保证example_array [2] .i被正确地对齐。

如果你要给我点踩,请至少告诉我为什么以及需要改进的地方。 - Myst
我没有对你的回答投票,但是它提出了一个相当强烈的主张,而没有任何支持。你有任何标准的参考资料来支持它吗? - melpomene
2
错误:alignof(char[15])为1,但sizeof(char[15])为15。 - Basile Starynkevitch
1
@BasileStarynkevitch 原始答案提到的是“类型大小”,而不是“成员大小”——即 sizeof(char),而不是 char a[15]; sizeof(a)……但我可以理解为什么会被误解。更新后的答案可能更清晰。 - Myst
1
@BasileStarynkevitch - 你在我的答案中哪里看到了 sizeof - Myst
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接