题
我有一个C#背景。我非常是像C一样低级语言的新手。
在C#中, struct
默认情况下,编译器将其内存列出。编译器可以隐含在字段之间重新排序数据字段或PAD附加位。因此,我必须指定一些特殊属性,以覆盖此行为以进行精确布局。
afaik,C不重新排序或对齐记忆布局 struct
默认情况下。但是,我听说有一个很难找到的例外。
C的内存布局行为是什么?应该重新订购/对齐什么?
解决方案
在C中,允许编译器要求每种原始类型的一定对齐。通常,对齐方式是类型的大小。但这完全是特定于实施的。
引入填充字节,以使每个对象都正确对齐。不允许重新排序。
可能每个遥远的现代编译器工具 #pragma pack
这允许控制填充物,并将其留给程序员遵守ABI。 (不过,这是严格的非标准。)
来自C99§6.7.2.1:
12结构或联合对象的每个非现场成员都以适合其类型的实现方式对齐。
13在一个结构对象中,非位置成员和位田居住的单位的地址会增加声明的顺序。适当转换的结构对象的指针指向其初始成员(或者如果该成员是位磁场,则指向其居住的单元),反之亦然。结构对象内可能有未命名的填充物,但在其起初没有。
其他提示
它是特定于实施的,但实际上是规则(在没有的情况下 #pragma pack
之类的)是:
- 结构成员以声明的顺序存储。 (如前所述,这是C99标准所要求的。)
- 如有必要,在每个结构成员之前添加填充物,以确保正确对齐。
- 每种原始类型t都需要对齐
sizeof(T)
字节。
因此,给定以下结构:
struct ST
{
char ch1;
short s;
char ch2;
long long ll;
int i;
};
ch1
在偏移0- 插入填充字节以对齐...
s
在偏移2ch2
在偏移4,在S之后立即- 3个填充字节被插入以对齐...
ll
在偏移8i
在偏移16,在LL之后- 在末尾添加了4个填充字节,因此总结构为8个字节的倍数。我在64位系统上检查了此:32位系统可以允许结构具有4字节对齐。
所以 sizeof(ST)
是24。
通过重新安排会员以避免填充,可以将其减少到16个字节:
struct ST
{
long long ll; // @ 0
int i; // @ 8
short s; // @ 12
char ch1; // @ 14
char ch2; // @ 15
} ST;
您可以从阅读 数据结构对齐Wikipedia文章 为了更好地了解数据一致性。
来自 维基百科文章:
数据对齐意味着将数据放在一个等于单词大小的某些倍数的内存偏移量上,这由于CPU处理内存的方式增加了系统的性能。为了对齐数据,可能有必要在最后一个数据结构的末尾和下一个开始的开始(即数据结构填充)之间插入一些毫无意义的字节。
从 6.54.8结构包装布拉格斯 海湾合作委员会文档:
为了与Microsoft Windows编译器兼容,GCC支持一组#Pragma指令,这些指令改变了结构成员(零宽的Bitfields除外),工会和类随后定义的类。下面的n值始终是两个小功率,并指定字节中的新对齐。
#pragma pack(n)
简单地设置新的对齐。#pragma pack()
将对齐设置为汇编时有效的对齐(另请参见命令行选项-FPACK struct [=]请参阅代码gen Options)。#pragma pack(push[,n])
在内部堆栈上按下当前的对齐设置,然后选择设置新的对齐。#pragma pack(pop)
将对齐设置恢复为保存在内部堆栈顶部的设置(并删除该堆栈条目)。注意#pragma pack([n])
不影响这种内部堆栈;因此有可能拥有#pragma pack(push)
其次是多个#pragma pack(n)
实例并由一个单一完成#pragma pack(pop)
.某些目标,例如i386和PowerPC,支持MS_STRUCT
#pragma
它列出了记录的结构__attribute__ ((ms_struct))
.
#pragma ms_struct on
打开声明的结构的布局。#pragma ms_struct off
关闭声明的结构的布局。#pragma ms_struct reset
返回到默认布局。