c - 栈上数据的对齐方式是什么?
问题描述
我读了 K&R C(2nd) 185p,其中一部分很难理解。
尽管机器各不相同,但对于每台机器来说,都有一个最严格的类型:如果最严格的类型可以存储在特定地址,那么所有其他类型也可能是。在某些机器上,最严格的类型是double;在其他人身上,int或long就足够了。
我认为
大多数现代计算机都是字节可寻址的(通过 wiki)。最小的数据类型char足以容纳任何堆栈区域。因此,所有数据类型都足以满足任意堆栈位置。但是为什么会有这样的限制呢?
在这个类似的问题中,
CPU 通常要求(或者如果)将某些类型的数据存储在某个(2 的幂)值的倍数的地址中,则可以更有效地工作。
这解释了我的问题。但我无法理解。这是否意味着堆栈中某些 2 的幂(2、4、8、16、...、1024、2048、...)的地址需要某些类型?
如果是这样,为什么?或者如果我错了,它指的是什么?
解决方案
对齐数据有两个原因:
- 硬件要求。有些机器只有在正确对齐的情况下才能访问内存中的数据。当然,您可以执行多次读取并使用一些位算术来模拟从任何地址读取,但这会对性能造成破坏性影响。
- 表现。即使机器可以访问任何地址的任何数据,如果数据适当对齐,它可能会表现得更好。
当然,这可能因机器而异,但“适当对齐”通常意味着 N 位数据的地址可以被 N/8 整除。
因此,在对齐很重要的机器上,32int
位指针将放置在可被 4 整除的内存地址,64 位指针将放置在可被 8 整除的内存地址,等等。
您可以在结构中看到这一点。
#include <stdint.h>
#include <stdio.h>
typedef struct {
uint32_t u32;
void* p;
uint8_t u8;
} Struct;
int main(void) {
Struct s;
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", sizeof(s));
}
$ gcc -Wall -Wextra -pedantic a.c -o a && ./a
0x7ffef5f775d0
0x7ffef5f775d8
0x7ffef5f775e0
0x7ffef5f775e8
0x18
这意味着我们有这个:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7
+-------+-------+---------------+-+-------------+
| u32 |XXXXXXX| p |*|XXXXXXXXXXXXX| * = u8
+-------+-------+---------------+-+-------------+ X = unused
u32
注意和之间浪费的空间p
。这是p
正确对齐的。
还要注意u8
. 这样当您拥有它们的数组时,结构本身就可以正确对齐。如果没有这个最终填充,数组的第二个元素的u32
andp
将无法正确对齐。
最后,注意使用
typedef struct {
uint32_t u32;
uint8_t u8;
void* p;
} Struct;
会导致更小的结构。
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------+-+-----+---------------+
| u32 |*|XXXXX| p | * = u8
+-------+-+-----+---------------+ X = unused
推荐阅读
- magento - 新的 magento 2.4.2 安装中的错误 500
- javascript - Fisher Yates 添加重复项
- javascript - Javascipt 中的 Math.random() 给出了相当非随机的分布
- volume - 可见范围指示器
- r - 这些表达式如何命名:a = 1 和 (a = 1)
- logstash - 遇到可重试的错误。将使用指数退避重试 413
- python - Minimalmodbus 我可以读取数据,但我不能写它(RTU)
- parsing - 用递归语法的 nltk 解析
- python - 类变量引用函数更改为instancemethod
- r - `stringr::str_glue`:如何转义反斜杠(\)?