关于数据对齐和填充的另一个混淆
Posted
技术标签:
【中文标题】关于数据对齐和填充的另一个混淆【英文标题】:Yet another confusion about data alignment and padding 【发布时间】:2018-07-28 14:27:55 【问题描述】:我一直在阅读有关数据对齐和结构填充的内容。我从here 开始,为了表达我的理解,我可以说T
类型的元素或数据e
是自然对齐的,如果它的地址a
是它的数据大小的倍数[sizeof T
],
让我们考虑
struct test
short a;
char b;
;
constexpr auto size = sizeof test
是 4,因为 struct test
的最大对齐方式(short
)
然后我去了here,第一个答案很好地解释了数据对齐,作者使用了这个例子
data1: "ab"
data2: "cdef"
|a b c d| |e f 0 0|
鉴于内存访问粒度为 4 字节,我将在这里假设 data1
是 2
字节,data2
是 4 byte
,因为 data2
开始于一个奇数地址或者它的地址不是4 byte
的倍数,这被认为是未对齐的数据,因此访问data2
会根据架构产生不同的效果。
这是通过添加 junkies(或纯 0)使 data2
自然对齐的填充,最后我们有
|a b 0 0| |c d e f|
到目前为止很好,现在让我们将两个数据包装到一个结构中
struct wrap
data1: "ab"
data2: "cdef"
;
现在考虑到最大对齐,wrap
大小将变为 8
,并带有两个字节的填充。
我在这里,在struct wrap
的情况下,无论是填充还是不填充,处理器实际上都需要两个周期(假设 WORD 大小为 4 字节)来获取 wrap
对象,对吗?
那么为什么我们需要填充呢?
没有填充:
|a b c d| |e f 0 0|
假设处理器获取了 8 个字节 wrap object
,data1
是两个字节,因此它可以容纳“ab”,剩余的将被截断(就像填充位将被截断一样)
而data2
可以从它的地址读取连续的 4 个字节(读取“cdef”),其余的被截断了,那么为什么我们需要在这里填充或者我弄错了?
第二个问题和上面差不多
struct test2
short a; //2 byte
char _3,_4,_5,_6,_7,_8,_9,_10,_11 ;
;
而sizeof test2
将给出 12,假设现在内存访问粒度(更改 WORD = 8)是 8 字节,那么 test2
的对象将占用两个 WORD,无论是否有填充,它都会占用两个 WORD,因此无论test2
的对齐方式如何,都需要两个周期来获取其对象,如果是这种情况,为什么填充在这里很重要?为什么是 12,而不是 11,虽然两者都需要在 8 字节边界内有两个周期?
最后一个问题,
struct final
char a;
int b;
;
假设 struct final
已打包(无对齐),而 var 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:x
这里不是位字段的意思
我了解到,只要我通过 ` 使用对象访问 test::b。或 ->' 会很好,但是当我获取它的地址时
int * p = &(test::b) ; (void)*p;
此语句将影响性能或崩溃。
同样,如果 WORD = 8 字节,那么 struct final
的对象将在一个周期内进入内存,假设指针 p
持有地址 1
,这很奇怪,但不能只是尊重的*p
将从地址1
开始直到指向元素类型(4 字节)的位复制到内存中?为什么这里有问题?我错过了什么?
【问题讨论】:
【参考方案1】:数据错位的问题与整个结构的加载/存储无关。它显示为可以访问各个字段。
当一个字段未对齐时,它需要两次读/写加上字节重新排列,而不是一次。填充结构更大,我们不介意。
【讨论】:
在我上面展示的一个实例中,struct final
可以适应 WORD 边界(给定 WORD=8),并假设我使用指针 p
访问它的单个元素 int b
地址1
,所以在这里,只从给定地址读取 4 个字节有什么问题?为什么会发生碰撞/崩溃?
@Explorer_N:如果机器即使对于未对齐的 int 也执行 8 个字节的提取,无论如何它都需要在内部重新对齐该字段。如果它使用 4 字节对齐的提取,则需要其中两个。
对不起,当我@
提到这个名字没有出现。 “在内部重新对齐字段”我有点卡在这里,我们已经获取了 8 个字节,并且指针 p
知道在 8 字节边界内从哪里开始读取,它也知道它可以读取多少字节,所以当我们说temp=*p
为什么不能像(示例)memcpy(&tmp,p,sizeof(int))
那样进行按位复制,为什么需要重新调整?或者它只是一个规则,int
元素的地址应该是 4 的倍数?
@Explorer_N:内存总线可能被设计成无法执行未对齐的读取。以上是关于关于数据对齐和填充的另一个混淆的主要内容,如果未能解决你的问题,请参考以下文章
关于对齐中word-aligned,word,dword,Half-Word等容易混淆的一些概念的随笔
关于对齐中word-aligned,word,dword,Half-Word等容易混淆的一些概念的随笔