我一直在阅读有关数据对齐和结构填充的信息。我从here开始,为了表达我的理解,我可以说类型为{{1的元素或数据e
}}如果其地址T
是其数据大小的倍数[a
,
考虑一下
sizeof T
由于struct test{
short a;
char b;
};
的最大对齐方式(constexpr auto size = sizeof test
), struct test
为4
然后我去here,第一个答案很好地解释了数据对齐,作者使用了这个示例
short
鉴于内存访问的粒度为4字节,我将在此处做出一个假设,例如data1: "ab"
data2: "cdef"
|a b c d| |e f 0 0|
是data1
字节,而2
是data2
,因为4 byte
从奇数地址开始,或者它的地址不是data2
的倍数(被认为是未对齐数据),因此根据架构,访问4 byte
会有不同的效果。< / p>
在这里填充以使data2
通过添加垃圾邮件(或纯0)自然对齐,最后我们有了
data2
到目前为止,现在让我们将两个数据包装成一个结构
|a b 0 0| |c d e f|
现在,通过考虑最大对齐方式,struct wrap{
data1: "ab"
data2: "cdef"
};
的大小将变为wrap
,并且填充了两个字节。
在这里,对于8
,是填充还是不填充,处理器实际上需要两个周期(给定WORD大小为4字节)来获取struct wrap
对象?
那么为什么我们需要填充?
没有填充:
wrap
假定处理器获取了8个字节的|a b c d| |e f 0 0|
,wrap object
是两个字节,因此它可以容纳剩余的“ ab”将被截断(就像填充位将被截断一样)
并且data1
可以从其地址读取连续的4个字节(读为“ cdef”),其余部分被截断了,那么为什么我们需要在此处填充,还是我弄错了?
与上述基本相似的第二个问题
data2
,struct test2{
short a; //2 byte
char _3,_4,_5,_6,_7,_8,_9,_10,_11 ;
};
将给出12,现在内存访问粒度(更改WORD = 8)为8字节,则sizeof test2
的对象将占用两个WORD,无论是否填充它会占用两个WORD,因此无论test2
的对齐方式如何,都需要两个周期来获取其对象,如果是这种情况,为什么填充在这里很重要?为什么12个而不是11个,尽管两者都需要在8字节边界内两个周期?
最后一个问题
test2
假设struct final{
char a;
int b;
};
被打包(不对齐),并且变量struct final
将被放置在奇数地址
内存(只是理论上的表示)
test::b
0 1 2 3 4 5 6 7
tets::a:1 test::b:1 test::b:2 test::b:3 test::b:4
并非此处的位字段
我了解到只要我通过`使用对象访问test :: b。或->'就可以了,但是当我拿到地址时
test::b:x
该语句将导致性能下降或崩溃。
同样,如果WORD = 8个字节,则int * p = &(test::b) ; (void)*p;
的对象将在一个周期内进入内存,并且假设指针struct final
拥有地址p
,这是奇数,但是不能仅凭1
的引用就将地址*p
开始的所有位复制到指向元素类型(4字节)的指针到内存中?为什么这里有问题?我一直想念的是什么?
答案 0 :(得分:3)
数据未对齐的问题与整个结构的加载/存储无关。它会显示对各个字段的访问权限。
当字段未对齐时,它需要两次读/写和字节重排,而不是一次。填充的结构较大,我们不介意。