首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么在64位机器上结构对齐为4字节(32位)?

为什么在64位机器上结构对齐为4字节(32位)?
EN

Stack Overflow用户
提问于 2021-06-05 00:41:04
回答 3查看 203关注 0票数 1

我试着用下面的代码来理解一些关于结构填充的事情:

代码语言:javascript
运行
复制
#include <stdio.h>
#include <stdint.h>

struct azaza { // of course suboptimal arrangement of elements
     uint32_t addr1;
     uint32_t addr2;
     uint8_t tmp;
     uint32_t addr3;
     uint8_t flags;
};

int main(void) {
     printf("%d\n", sizeof(struct azaza));

     return 0;
}

输出是:20

但我希望是24,因为我的机器和操作系统是64位的,并且我认为对齐应该是4字节边界。为什么x86-64操作系统的结构对齐在4字节边界上?

EN

回答 3

Stack Overflow用户

发布于 2021-06-05 01:11:34

术语“64位机器”是模糊的。计算机处理器和系统在同一台机器上有几个大小可能不同的功能,包括:

  • 大多数处理器寄存器的宽度。
  • 地址的宽度。
  • 数据总线的宽度。
  • 算术逻辑单元的宽度。

现在,让我们假设所有这些都是64位的。即便如此,我们为什么要要求,比如说uint32_t必须与64位对齐?

要求对齐的一个原因是避免跨存储器传输拆分访问。如果总线是64位宽,则系统通常被设计为以8字节(64位)的倍数访问存储器。当处理器想要读取一些内存时,例如从64位地址,它只向存储设备发送前61位。(61很多,但我们假设这台机器中的所有内容都是64位的。)存储设备获得与这61位匹配的所有8个字节-我们没有发送的低3位的全部8个组合。它一次获取8个字节,因为这是适合总线的,并且我们想要高效。

因此,每当进程从内存中读取时,它总是会获得8个字节,并且这些字节将是64位对齐的。

现在我们可以看到,如果一个uint32_t开始于某个地址,比如xxx0101,其中x表示我们不关心的位,那么它的四个字节将位于地址xxx0101、xxx0110、xxx0111和xxx1000。但第四个字节属于不同的八个字节组。前三个都在同一组中,即由初始位xxx0寻址的组。最后一个字节在一个新组xxx1中。为了读取这个uint32_t,我们必须从内存中读取两次。这是低效的。

但是,如果uint32_t位于地址xxx0000或xxx1000,则它的所有字节都在一个组中。它们可能是该组中的前四个字节或最后四个字节,因此我们需要处理器能够从它从内存中获取的八个字节中选择前四个或最后四个字节,但只需从内存中读取一次即可获得这些字节。

因此,uint32_t的四字节对齐足以确保它足够好地对齐,我们只需要一次读取就可以从内存中获取它。

几乎没有理由需要8字节对齐。一个原因可能是,如果它是8字节对齐的,我们就不需要处理器中的额外导线和开关来选择8个字节中的前4个或最后4个字节。我们只需要取前四个。但这一微小的优势被一个事实所掩盖,那就是我们每八个字节只能存储一个uint32_t。一半的内存会被填充浪费掉。使用四字节对齐,我们可以很好地读取uint32_t对象,并且一次可以读取两个。

使用uint8_t,8字节对齐会更糟,我们每8个字节只能有一个uint8_t,浪费了87.5%的内存。

在大多数情况下,长度为n字节的对象只需要具有n字节对齐,以便在硬件上表现良好(假设n是2的幂)。这种对齐将允许它们整齐地适应总线和内存操作,无论它们的宽度是多少。

此外,如果总线宽度为b,对象大小为n,则对齐要求可能只是b或n中较小的一个。一旦对象大于总线宽度,我们将需要多次传输才能获得它,而且通常需要比总线宽度更多的对齐不会获得任何结果。

票数 1
EN

Stack Overflow用户

发布于 2021-06-05 00:56:20

uint32_t是4字节,2* uint32_t =8字节,uint8_t是1字节,但是因为最大的变量大小是4字节,所以编译器将uint8_t推送到4字节,现在我们有12字节+ uint32_t + uint8_t,我们得到20字节。假设我们有

代码语言:javascript
运行
复制
struct azaza {
 uint32_t addr1;
 uint8_t tmp;
 uint8_t tmp1;
 uint8_t tmp2;
 uint32_t addr3;
 uint8_t flags;
};

大小变为4字节块中的4+3字节+4 =4 +4 +4 = 16的块中的4 +1字节

代码语言:javascript
运行
复制
struct azaza { 
 uint32_t tmp;
 uint8_t tmp1;
 uint8_t tmp2;
 uint8_t tmp3;
 uint64_t tmp4;
 uint8_t tmp5;
};

最大的元素是8字节tmptmptmptmptmp1tmp2tmp3-|tmp4tmp4tmp4tmp4tmp4tmp4tmp4tmp4|tmp5------- =24字节

票数 -1
EN

Stack Overflow用户

发布于 2021-06-05 01:03:02

另一个例子

代码语言:javascript
运行
复制
  struct azaza {

 uint8_t t1;
 uint16_t t2;
 uint32_t t3;
 
};

最大的元素是4个字节。考虑-作为空块。t1-t2t2|t3t3t3t3 =8字节

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67841002

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档