我有C#背景。我是C之类的底层语言的新手。
在C#中,struct
默认情况下,的内存由编译器布置。编译器可以重新排序数据字段或隐式填充字段之间的其他位。因此,我必须指定一些特殊属性来覆盖此行为,以实现准确的布局。
AFAIK,C在struct
默认情况下不会重新排序或对齐的内存布局。但是,我听说很难找到一个例外。
C的内存布局行为是什么?什么应该重新排序/对齐而不是?
Answers:
在C语言中,允许编译器为每种原始类型指定某种对齐方式。通常,对齐方式是类型的大小。但这完全是特定于实现的。
引入填充字节,以便每个对象正确对齐。不允许重新排序。
可能每个远程的现代编译器实现#pragma pack
都可以控制填充,并将填充留给程序员以符合ABI。(不过,这绝对是非标准的。)
根据C99§6.7.2.1:
12结构或联合对象的每个非位字段成员都以适合于其类型的实现定义的方式对齐。
13在结构对象中,非位字段成员和位字段所在的单元的地址按照声明的顺序增加。指向经过适当转换的结构对象的指针指向其初始成员(或者,如果该成员是位字段,则指向它所驻留的单元),反之亦然。结构对象内可能存在未命名的填充,但在其开始处没有。
_Alignas
。
它是特定于实现的,但是在实践中,规则(不存在#pragma pack
或类似条件)是:
sizeof(T)
字节对齐。因此,给出以下结构:
struct ST
{
char ch1;
short s;
char ch2;
long long ll;
int i;
};
ch1
在偏移量0s
在偏移量2ch2
紧接s之后位于偏移量4ll
在偏移量8i
在ll之后紧靠偏移量16sizeof(ST)
24也是如此。
可以通过重新排列成员以避免填充来将其减少到16个字节:
struct ST
{
long long ll; // @ 0
int i; // @ 8
short s; // @ 12
char ch1; // @ 14
char ch2; // @ 15
} ST;
char
在您的示例中添加最终成员。
sizeof(T)
字节对齐。例如,double
常见的32位体系结构为8字节,但通常仅需要4字节对齐。此外,仅在结构端部的填充会垫到最宽的结构成员的对齐方式。例如,一个3个char变量的结构可以没有填充。
您可以先阅读数据结构对齐维基百科文章,以更好地理解数据对齐。
从维基百科文章:
数据对齐意味着将数据放在等于字长的倍数的内存偏移处,这由于CPU处理内存的方式而提高了系统的性能。为了对齐数据,可能有必要在最后一个数据结构的末尾与下一个数据结构的末尾(即数据结构填充)之间插入一些无意义的字节。
从GCC文档的6.54.8结构打包语法开始:
为了与Microsoft Windows编译器兼容,GCC支持一组#pragma指令,这些指令可更改结构成员(零宽度位域除外),并集和随后定义的类的最大对齐方式。始终要求下面的n值是2的小数并以字节为单位指定新的对齐方式。
#pragma pack(n)
只需设置新的对齐方式。#pragma pack()
将对齐方式设置为开始编译时生效的对齐方式(另请参见命令行选项-fpack-struct [=]参见代码生成选项)。#pragma pack(push[,n])
将当前对齐方式设置推入内部堆栈,然后选择设置新对齐方式。#pragma pack(pop)
将对齐设置恢复为内部堆栈顶部保存的对齐设置(并删除该堆栈条目)。请注意,#pragma pack([n])
这不会影响此内部堆栈。因此有可能#pragma pack(push)
跟随多个#pragma pack(n)
实例并由一个实例终结#pragma pack(pop)
。一些目标(例如i386和powerpc)支持ms_struct
#pragma
,该ms_struct列出了所记录的结构__attribute__ ((ms_struct))
。
#pragma ms_struct on
打开声明的结构的布局。#pragma ms_struct off
关闭声明的结构的布局。#pragma ms_struct reset
返回到默认布局。
#pragma pack
与之相同的效果,但对语义的控制更为精细。