C 堆栈上数据的对齐方式是什么?
我读了K&R C(第二版)185p,其中一部分很难理解 尽管机器各不相同,但每台机器都有一个限制性最强的类型:如果限制性最强的类型可以存储在特定的地址,则所有其他类型也可以。在某些机器上,限制性最强的类型是双精度的;在其他情况下,int或long就足够了 我想C 堆栈上数据的对齐方式是什么?,c,architecture,malloc,memory-alignment,C,Architecture,Malloc,Memory Alignment,我读了K&R C(第二版)185p,其中一部分很难理解 尽管机器各不相同,但每台机器都有一个限制性最强的类型:如果限制性最强的类型可以存储在特定的地址,则所有其他类型也可以。在某些机器上,限制性最强的类型是双精度的;在其他情况下,int或long就足够了 我想 大多数现代计算机都是字节可寻址的(通过wiki)。最小的数据类型char足以适合任何堆栈区域。因此,所有数据类型都适合任意堆栈位置。但为何会有这样的限制呢在类似问题中 CPU通常要求(或更有效地工作,如果)某些类型的数据存储在某个(二的幂
大多数现代计算机都是字节可寻址的(通过wiki)。最小的数据类型char足以适合任何堆栈区域。因此,所有数据类型都适合任意堆栈位置。但为何会有这样的限制呢
在类似问题中 CPU通常要求(或更有效地工作,如果)某些类型的数据存储在某个(二的幂)值的倍数的地址 这就解释了我的问题。但我不明白。这是否意味着堆栈中二次幂(2,4,8,16,…,1024,2048,…)的某些地址需要某些类型?
若然,原因为何?或者,如果我错了,它指的是什么?对齐数据有两个原因:
- 硬件要求。某些机器只有在正确对齐的情况下才能访问内存中的数据。当然,您可以执行多次读取,并使用一些位算法模拟从任何地址读取,但这将对性能造成破坏
- 表演。即使机器可以访问任何地址的任何数据,如果数据适当对齐,其性能也可能会更好
int
将被放置在一个可被4整除的内存地址上,64位指针将被放置在一个可被8整除的内存地址上,以此类推
您可以在结构中看到这一点
#包括
#包括
类型定义结构{
uint32_t u32;
void*p;
uint8_t u8;
}结构;
内部主(空){
结构;
printf(“%p\n”,(void*)和s.u32);
printf(“%p\n”,(void*)和s.p);
printf(“%p\n”,(void*)和s.u8);
printf(“%p\n”,(void*)(&s+1));
printf(“0x%zx\n”,大小f);
}
$gcc-Wall-Wextra-pedantic a.c-o a&&a
0x7ffef5f775d0
0x7ffef5f775d8
0x7ffef5f775e0
0x7ffef5f775e8
0x18
这意味着我们有:
0123456789abcdef01234567
+-------+-------+---------------+-+-------------+
|u32 | XXXXXXX | p |*| xxxxxxxxxxxx |*=u8
+-------+-------+---------------+-+-------------+X=未使用
注意u32
和p
之间浪费的空间。就是这样,p
正确对齐
还要注意u8
之后浪费的空间。这样,当您有一个数组时,结构本身就正确对齐了。如果没有最后的填充,数组第二个元素的u32
和p
将无法正确对齐
最后,请注意使用
typedef结构{
uint32_t u32;
uint8_t u8;
void*p;
}结构;
将导致更小的结构
01123456789abcdef
+-------+-+-----+---------------+
|u32 |*| XXXXX | p |*=u8
+-------+-+-----+---------------+X=未使用
哇,我明白了!你的回答甚至解释了结构的填充。关于你的第一个例子(大结构),我有一个问题。在该结构中,对齐是(4(u32)、4(x)、8(p)、1(u8)、7(x)),如果我们有一个数组,则相同的对齐将继续。但也可以在末尾设置3个字节的填充,而不是7个字节。然后两个模式将被重复为((4,4(填充),8,1,3(填充)),(4,8,1,3(填充)))。也可以使用某些系统吗?Re“但也可以在末尾设置3字节的填充,而不是7”,否。结构的大小需要可以被其最大对齐限制整除。我们的情况是8。而且0x14
不能被8整除。让我们用一个例子来说明这是为什么。假设数组从地址0x1000开始<代码>a[0]。u32将位于0x1000(正常),a[0]。p
将位于0x1008(正常),a[1]。u32
将位于0x1014(正常),但a[1]。p
将位于0x101C(不正常)。额外的四个字节的填充将a[1].u32
推送到0x1018(正常),将a[1].p
推送到0x1020(正常)