C中结构填充的假设

Assumption of structure padding in C

我正在学习 C 中的结构填充并遇到 this video

基本上它说如果我有一个结构

struct abc {
    char a;    // 1 byte
    char b;    // 1 byte
    int c;     // 4 bytes
} var;

那么,结构体不是这样存储的(c,...,c表示c的四个字节;||是字边界;_是byte的地方)

_  _  _  _ || _  _  _  _
a  b  c  c    c  c    

两个字节的空space会在b之后补上,导致(e表示空)

_  _  _  _ || _  _  _  _
a  b  e  e    c  c  c  c

这样 CPU 可以在一个 CPU 周期内得到 int c。

However, this does build on the assumption that the first member (a in my case) of struct will be stored immediately after word boundary. Is it always so?

结构类型对象的地址总是等于对象第一个成员的地址。

来自 C 标准(6.7.2.1 结构和联合说明符)

15 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

这是一个演示程序

#include <stdio.h>

int main(void) 
{
    struct abc
    {
        char a;
        char b;
        int c;
    } abc = { 'A', 'B', 3 };
    
    printf( "&abc = %p, &abc.a = %p\n", ( void * )&abc, ( void * )&abc.a );
    
    struct abc *p = &abc;
    
    printf( "*( char * )p = %c\n", *( char * )p );
    
    return 0;
}

程序输出可能看起来像

&abc = 0x7ffe8cfad6c0, &abc.a = 0x7ffe8cfad6c0
*( char * )p = A

这是编译器进行的优化,因为它对 CPU 更容易。大多数编译器应该允许您禁用它。例如,在 GCC 中,您可以使用 __attribute__((packed)).

另见

However, this does build on the assumption that the first member of struct will be stored immediately after word boundary. Is it always so?

是的。

定义结构类型时,结构的对齐要求至少是其成员的最严格对齐要求。例如,如果一个结构的成员的对齐要求为 1 字节、8 字节和 4 字节,则该结构的对齐要求将为 8 字节。定义结构时,编译器会自动解决这个问题。 (从技术上讲,C 标准可能允许编译器为结构提供更好的对齐方式——我没有看到任何反对它的规则——但实际上并没有这样做。)

然后,每当 C 实现为结构对象保留内存时(当您定义该类型的对象时,例如 struct foo x),它将确保内存按照该结构的要求对齐。这导致成员的对齐要求也得到满足。当程序使用 malloc 分配内存时,返回的内存始终根据需要与所请求大小的任何对象对齐。

(如果你在程序中做任何“有趣的事情”来为对象设置你自己的内存位置,例如将一个放在用 malloc 分配的内存的中间,你有责任获得对齐对。)

此外,如有必要,结构将在末尾进行填充,以便其总大小是该对齐要求的倍数。然后,在这些结构的数组中,数组的每个连续元素也将从正确对齐的位置开始。