C中结构填充的假设
Posted
技术标签:
【中文标题】C中结构填充的假设【英文标题】:Assumption of structure padding in C 【发布时间】:2021-07-23 04:01:31 【问题描述】:我在学习 C 中的结构填充时遇到了this video。
基本上它说如果我有一个结构
struct abc
char a; // 1 byte
char b; // 1 byte
int c; // 4 bytes
var;
那么,不要像这样存储结构体(c,...,c 表示 c 的四个字节;|| 是单词边界;_ 是字节的位置)
_ _ _ _ || _ _ _ _
a b c c c c
在b之后会填充两个字节的空白空间,结果是(e表示空)
_ _ _ _ || _ _ _ _
a b e e c c c c
这样CPU可以在一个CPU周期内得到int c。
但是,这确实建立在 struct 的第一个成员(在我的例子中为 a)将在单词边界之后立即存储的假设之上。总是这样吗?
【问题讨论】:
你使用的是哪个编译器? 你的目标是哪个 CPU? 听起来你感兴趣的是结构的对齐要求。 我觉得重要的是要注意在这里起作用的不是单词边界,而是对齐要求。即使在一个单词中也可以有填充。例如,尝试一个以char
作为第一个元素,short
作为第二个元素的结构。
@Tony 我正在使用来自 Segger Embedded Studio 的默认 GCC。
【参考方案1】:
但是,这确实建立在 struct 的第一个成员将在字边界之后立即存储的假设之上。总是这样吗?
是的。
定义结构类型时,结构的对齐要求至少是其成员最严格的对齐要求。例如,如果一个结构具有对齐要求为 1 个字节、8 个字节和 4 个字节的成员,则该结构的对齐要求将是 8 个字节。定义结构时,编译器会自动计算出来。 (从技术上讲,C 标准可能允许编译器对结构进行更大的对齐——我没有看到任何反对它的规则——但实际上并没有这样做。)
然后,每当 C 实现为结构对象保留内存时(如定义该类型的对象,例如 struct foo x
),它将确保内存按照该结构的要求对齐。这导致成员的对齐要求也得到满足。当程序使用malloc
分配内存时,返回的内存总是根据请求大小的任何对象对齐。
(如果你在程序中做了任何“有趣的事情”来为对象设置你自己的内存位置,例如将一个放在分配给malloc
的内存中间,你有责任获得正确的对齐方式。)
此外,如有必要,结构将在末尾填充,以便其总大小是该对齐要求的倍数。然后,在这些结构的数组中,数组的每个连续元素也将从正确对齐的位置开始。
【讨论】:
谢谢!您能否详细说明“结构的对齐要求将至少是其成员最严格的对齐要求”?一个例子将不胜感激。 @jleng: 如果结构的成员有1字节、1字节、4字节、8字节、1字节和4字节的对齐要求,那么结构的对齐要求将为8字节,因为这是成员最严格的对齐要求。 @jleng:大多数平台要求多字节对象“对齐”,以便它们从 2 或 4 或 8 倍数的地址开始(取决于平台、类型和其他考虑)。如果struct
的成员必须对齐,使其地址为4 的倍数,则struct
对象本身也将对齐,使其地址为4 的倍数。
@JohnBode:谢谢。这消除了我留下的所有困惑。【参考方案2】:
结构类型的对象的地址总是等于对象的第一个成员的地址。
来自 C 标准(6.7.2.1 结构和联合说明符)
15 在结构对象中,非位域成员和单元 位域所在的地址按顺序增加 在其中声明它们。 指向结构对象的指针, 适当转换,指向其初始成员(或者如果该成员是 一个位域,然后到它所在的单元),反之亦然。 结构对象中可能有未命名的填充,但在其 开始。
这是一个演示程序
#include <stdio.h>
int main(void)
struct abc
char a;
char b;
int c;
abc = 'A', 'B', 3 ;
printf( "&abc = %p, &abc.a = %p\n", ( void * )&abc, ( void * )&abc.a );
struct abc *p = &abc;
printf( "*( char * )p = %c\n", *( char * )p );
return 0;
程序输出可能看起来像
&abc = 0x7ffe8cfad6c0, &abc.a = 0x7ffe8cfad6c0
*( char * )p = A
【讨论】:
【参考方案3】:这是编译器进行的优化,因为它对 CPU 来说更容易。大多数编译器应该允许您禁用它。例如,在 GCC 中,您可以使用 __attribute__((packed))
。
另见How to override C compiler aligning word-sized variable in struct to word boundary。
【讨论】:
以上是关于C中结构填充的假设的主要内容,如果未能解决你的问题,请参考以下文章
Python 和 C 结构之间的大小不匹配,默认结构对齐/填充
C 语言文件操作 ( 学生管理系统 | 命令行接收数据填充结构体 | 结构体写出到文件中 | 查询文件中的结构体数据 )