关于数据对齐和填充的另一个混淆

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 字节,我将在这里假设 data12 字节,data24 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 objectdata1 是两个字节,因此它可以容纳“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等容易混淆的一些概念的随笔

关于对齐中word-aligned,word,dword,Half-Word等容易混淆的一些概念的随笔

剖析一行(混淆的?)Python

Swift tableview 填充单元格导致内容混淆

SQL Server中易混淆的数据类型