C语言中结构填充的假设

C语言中结构填充的假设,c,memory,struct,padding,C,Memory,Struct,Padding,我在学习C中的结构填充时遇到了一个问题 基本上它说如果我有一个结构 struct abc { char a; // 1 byte char b; // 1 byte int c; // 4 bytes } var; 然后,不是像这样存储结构(c,…,c表示c的四个字节;| |是单词边界;u是字节的位置) 两个字节的空白将填充在b之后,结果是(e表示空白) 因此,CPU可以在一个CPU周期内获得int c 然而,这是建立在假设struct的第一个成员(

我在学习C中的结构填充时遇到了一个问题

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

struct abc {
    char a;    // 1 byte
    char b;    // 1 byte
    int c;     // 4 bytes
} var;
然后,不是像这样存储结构(c,…,c表示c的四个字节;| |是单词边界;u是字节的位置)

两个字节的空白将填充在b之后,结果是(e表示空白)

因此,CPU可以在一个CPU周期内获得int c

然而,这是建立在假设struct的第一个成员(在我的例子中是a)将立即存储在单词边界之后的基础上的。总是这样吗


结构类型的对象的地址始终等于该对象的第一个成员的地址

来自C标准(6.7.2.1结构和联合规范)

15在结构对象中,非位字段成员和单元 位字段所在的地址按顺序递增 它们在其中声明指向结构对象的指针, 适当转换后,指向其初始成员(或如果该成员是 一个位字段,然后发送到它所在的单元),反之亦然。 结构对象中可能有未命名的填充,但其位置不存在 开始。

这是一个演示程序

#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;
}

结构类型的对象的地址始终等于该对象的第一个成员的地址

来自C标准(6.7.2.1结构和联合规范)

15在结构对象中,非位字段成员和单元 位字段所在的地址按顺序递增 它们在其中声明指向结构对象的指针, 适当转换后,指向其初始成员(或如果该成员是 一个位字段,然后发送到它所在的单元),反之亦然。 结构对象中可能有未命名的填充,但其位置不存在 开始。

这是一个演示程序

#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;
}

这是编译器进行的优化,因为这对CPU来说更容易。大多数编译器应该允许您禁用它。例如,在GCC中,您可以使用
\uuuuuu属性((打包))


另请参见。

这是编译器所做的优化,因为这对CPU来说更容易。大多数编译器应该允许您禁用它。例如,在GCC中,您可以使用
\uuuuuu属性((打包))

另见

然而,这是建立在假设struct的第一个成员将立即存储在单词边界之后的基础上的。总是这样吗

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

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

(如果你在程序中做了一些有趣的事情来设置你自己的对象的内存位置,比如把一个放在内存中分配给MalOC/),你就有权获得正确的对齐。 此外,如有必要,结构将在末端进行填充,以使其总尺寸为该线形要求的倍数。然后,在这些结构的数组中,数组的每个连续元素也将从正确对齐的位置开始

然而,这是建立在假设struct的第一个成员将立即存储在单词边界之后的基础上的。总是这样吗

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

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

(如果你在程序中做了一些有趣的事情来设置你自己的对象的内存位置,比如把一个放在内存中分配给MalOC/),你就有权获得正确的对齐。


此外,如有必要,结构将在末端进行填充,以使其总尺寸为该线形要求的倍数。然后,在一系列结构中,数组的每个连续元素也将从正确对齐的位置开始。

您使用的是哪种编译器?针对的是哪种CPU?听起来您感兴趣的是结构的对齐要求。我觉得重要的是要注意,这里起作用的不是单词边界,而是对齐要求。甚至在一个区域内也可能存在填充
#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