【问题标题】:What is the alignment of data on the stack?栈上数据的对齐方式是什么?
【发布时间】:2021-04-13 20:17:44
【问题描述】:

我读了 K&R C(2nd) 185p,其中一部分很难理解。

虽然机器各不相同,但对于每台机器来说,都有一个最严格的类型:如果最严格的类型可以存储在特定地址,那么所有其他类型也可能是。在某些机器上,限制最大的类型是 double;对于其他人,intlong 就足够了。

我认为
大多数现代计算机都是字节可寻址的(通过 wiki)。最小的数据类型 char 足以容纳任何堆栈区域。因此,所有数据类型都足以满足任意堆栈位置。但是为什么会有这样的限制呢?

this类似的问题中,

CPU 通常要求(或者如果)将某些类型的数据存储在某个(2 的幂)值的倍数的地址中,则可以更有效地工作。

这解释了我的问题。但我无法理解。这是否意味着堆栈中某些 2 的幂(2、4、8、16、...、1024、2048、...)的地址需要某些类型?
如果是这样,为什么?或者如果我错了,它指的是什么?

【问题讨论】:

    标签: c architecture malloc memory-alignment


    【解决方案1】:

    对齐数据有两个原因:

    • 硬件要求。有些机器只有在正确对齐的情况下才能访问内存中的数据。当然,您可以执行多次读取并使用一些位算术来模拟从任何地址读取,但这会对性能造成破坏性影响。
    • 性能。即使机器可以访问任何地址的任何数据,如果数据适当对齐,它可能会表现得更好。

    当然,这可能因机器而异,但“适当对齐”通常意味着 N 位数据的地址可以被 N/8 整除。

    因此,在对齐很重要的机器上,32 位 int 将被放置在可被 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
    

    注意u32p 之间浪费的空间。这是p 正确对齐的原因。

    还要注意u8 之后浪费的空间。这样当您拥有它们的数组时,结构本身就可以正确对齐。如果没有这个最后的填充,数组第二个元素的u32p 将无法正确对齐。

    最后,注意使用

    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
    

    【讨论】:

    • 哇,我明白了!您的回答甚至解释了结构的填充。我有一个关于你的第一个例子(更大的结构)的问题。使用该结构,对齐是 (4(u32), 4(x), 8(p), 1(u8), 7(x)) 并且如果我们有一个数组,则继续进行相同的对齐。但这也可以在末尾设置 3 个字节填充而不是 7。然后两个模式将重复为 ((4, 4(padding), 8, 1, 3(padding)), (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(ok),a[1].p推到0x1020(ok)
    猜你喜欢
    • 2010-10-14
    • 2011-05-09
    • 2011-07-05
    • 2012-08-11
    • 2011-02-17
    • 2021-10-24
    • 2016-02-25
    • 1970-01-01
    相关资源
    最近更新 更多