C 内存对齐和填充—;32位和64位之间的差异

C 内存对齐和填充—;32位和64位之间的差异,c,gcc,memory,struct,x86,C,Gcc,Memory,Struct,X86,我想了解对以下小代码进行“gcc-m32”和“gcc-m64”编译得到的结果: #include <stdio.h> #include <stdlib.h> int main() { struct MixedData { char Data1; short Data2; int Data3; char Data4; }; struct X { char c; uint64_t x; }; printf("size of struct MixedData

我想了解对以下小代码进行“
gcc-m32
”和“
gcc-m64
”编译得到的结果:

#include <stdio.h>
#include <stdlib.h>

int main() {

struct MixedData
{
 char Data1;
 short Data2;
 int Data3;
 char Data4;
};

struct X {
 char c;
 uint64_t x;
};

printf("size of struct MixedData = %zu\n", sizeof(struct MixedData));
printf("size of struct X = %zu\n", sizeof(struct X));
printf("size of uint64_t = %zu\n", sizeof(uint64_t));

return 0;
}
由于编译器设置了以下填充,结构X的大小是否等于12

struct X {
 char c;     // 1 byte
 char d[3];  // 3 bytes
 uint64_t x; // 8 bytes
};
如果是这种情况,那么一个32位编译(4字节)的单词的大小是多少?如果它等于4字节,这将是一致的,因为12是4的倍数

现在,关于带“
gcc-m32
”编译的
MixedData的大小,我得到了“
struct MixedData的大小=12
”。我不理解这个值,因为我看到一个结构的总大小必须是这个结构中最大大小属性的倍数。例如,这里的
结构MixedData
,最大的属性是
intdata3
,其中
sizeof(Data3)=4个字节;我们为什么不使用以下填充:

struct MixedData
{
 char Data1;        // 1 byte
 char Datatemp1[3]; // 3 bytes
 short Data2;       // 2 bytes
 short Data2temp;   // 2 bytes
 int Data3;         // 4 bytes 
 char Data4;        // 1 byte
 char Data4temp[3]  // 3 bytes
};
struct X {
 char c;     // 1 byte
 char d[7];  // 7 bytes
 uint64_t x; // 8 bytes
};
因此,
struct MixedData
的总大小将等于
16字节
,而不是像我得到的那样
12字节

有人能看出这两种解释有什么不对吗

类似的问题是关于“
gcc-m64
”编译;输出为:

size of struct MixedData = 12
size of struct X = 16
size of uint64_t = 8
struct X
16字节
)的大小似乎是一致的,因为我认为64位模式下的编译器设置了以下填充:

struct MixedData
{
 char Data1;        // 1 byte
 char Datatemp1[3]; // 3 bytes
 short Data2;       // 2 bytes
 short Data2temp;   // 2 bytes
 int Data3;         // 4 bytes 
 char Data4;        // 1 byte
 char Data4temp[3]  // 3 bytes
};
struct X {
 char c;     // 1 byte
 char d[7];  // 7 bytes
 uint64_t x; // 8 bytes
};

但是我不理解
structmixeddata
12字节
)的值。事实上,我不知道编译器在这种情况下是如何设置填充的,因为在64位模式下,12不是内存字的倍数(假设这一个等于
8字节
)。您能告诉我在最后一种情况下“
gcc-m64
”生成的填充(对于
struct MixedData
)吗?

这是一个好奇的问题

struct
{
 char Data1;
 short Data2;
 int Data3;
 char Data4;
} x;

unsigned fun ( void )
{
    x.Data1=1;
    x.Data2=2;
    x.Data3=3;
    x.Data4=4;
    return(sizeof(x));
}
先编译后反汇编

64

0000000000000000 <fun>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   c6 05 00 00 00 00 01    movb   $0x1,0x0(%rip)        # b <fun+0xb>
   b:   66 c7 05 00 00 00 00    movw   $0x2,0x0(%rip)        # 14 <fun+0x14>
  12:   02 00 
  14:   c7 05 00 00 00 00 03    movl   $0x3,0x0(%rip)        # 1e <fun+0x1e>
  1b:   00 00 00 
  1e:   c6 05 00 00 00 00 04    movb   $0x4,0x0(%rip)        # 25 <fun+0x25>
  25:   b8 0c 00 00 00          mov    $0xc,%eax
  2a:   5d                      pop    %rbp
  2b:   c3                      retq   

32

00000000 <fun>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   c6 05 00 00 00 00 01    movb   $0x1,0x0
   a:   66 c7 05 02 00 00 00    movw   $0x2,0x2
  11:   02 00 
  13:   c7 05 04 00 00 00 03    movl   $0x3,0x4
  1a:   00 00 00 
  1d:   c6 05 08 00 00 00 04    movb   $0x4,0x8
  24:   b8 0c 00 00 00          mov    $0xc,%eax
  29:   5d                      pop    %ebp
  2a:   c3                      ret    
生产

00000000004004d6 <fun>:
  4004d6:   55                      push   %rbp
  4004d7:   48 89 e5                mov    %rsp,%rbp
  4004da:   48 c7 45 f8 38 10 60    movq   $0x601038,-0x8(%rbp)
  4004e1:   00 
  4004e2:   c6 05 4f 0b 20 00 01    movb   $0x1,0x200b4f(%rip)        # 601038 <x>
  4004e9:   66 c7 05 48 0b 20 00    movw   $0x2,0x200b48(%rip)        # 60103a <x+0x2>
  4004f0:   02 00 
  4004f2:   c7 05 40 0b 20 00 03    movl   $0x3,0x200b40(%rip)        # 60103c <x+0x4>
  4004f9:   00 00 00 
  4004fc:   c6 05 3d 0b 20 00 04    movb   $0x4,0x200b3d(%rip)        # 601040 <x+0x8>
  400503:   b8 0c 00 00 00          mov    $0xc,%eax
  400508:   5d                      pop    %rbp
  400509:   c3                      retq   
我们得到了这个

00000000004004d6 <fun>:
  4004d6:   55                      push   %rbp
  4004d7:   48 89 e5                mov    %rsp,%rbp
  4004da:   48 c7 45 f8 38 10 60    movq   $0x601038,-0x8(%rbp)
  4004e1:   00 
  4004e2:   c6 05 4f 0b 20 00 01    movb   $0x1,0x200b4f(%rip)        # 601038 <x>
  4004e9:   66 c7 05 47 0b 20 00    movw   $0x2,0x200b47(%rip)        # 601039 <x+0x1>
  4004f0:   02 00 
  4004f2:   c7 05 3f 0b 20 00 03    movl   $0x3,0x200b3f(%rip)        # 60103b <x+0x3>
  4004f9:   00 00 00 
  4004fc:   c6 05 3c 0b 20 00 04    movb   $0x4,0x200b3c(%rip)        # 60103f <x+0x7>
  400503:   b8 08 00 00 00          mov    $0x8,%eax
  400508:   5d                      pop    %rbp
  400509:   c3                      retq   
0000000000 4004D6:
4004d6:55%推送rbp
4004d7:48 89 e5 mov%rsp,%rbp
4004da:48 c7 45 f8 38 10 60 movq$0x601038,-0x8(%rbp)
4004e1:00
4004e2:c6 05 4f 0b 20 00 01 movb$0x1,0x200b4f(%rip)#601038
4004e9:66 c7 05 47 0b 20 00 movw$0x2,0x200b47(%rip)#601039
4004f0:02 00
4004f2:c7 05 3f 0b 20 00 03 movl$0x3,0x200b3f(%rip)#60103b
4004f9:00
4004fc:c6 05 3c 0b 20 00 04 movb$0x4,0x200b3c(%rip)#60103f
400503:b8 08 00 mov$0x8,%eax
400508:5d pop%rbp
400509:c3 retq

结构的大小现在是8字节,它们生成了未对齐的访问。

short Data2temp[2];//2个字节实际上是4个字节。反汇编显示了什么?我假设这是您询问的x86?您从何处获得此规则结构的大小必须是最大项的倍数?我从intel i7 x86_64编译代码段。关于规则,我认为结构的大小必须是最大项的倍数,因为如果我有一个struct数组,元素将更直接可访问,不是吗?那么初始混合数据将是16或32字节,每个元素为4或8字节。但事实并非如此。我几乎从不拆解或检查x86,我通常坚持使用arm,所以很长的mov指令并不是我所期望的。我故意没有进行优化,因为所有这些都是死代码,可能会消失,需要看到它们实际生成代码来与结构对话。因此,如果这是优化的,而不是死代码,可能会选择其他指令。不仅32位访问(movl)在32位边界上对齐(未压缩),而且16位访问(movw)也在至少16位边界上对齐)。如果您将最后一个元素更改为short而不是int,则应该保持结构的大小不变(12),但更改替换movb的movw的偏移量,同时对齐该访问(在16位边界上)。感谢您的回答,但是我正在做困惑:您说什么“他们正在尝试对齐16位边界上的16位访问和32位边界上的32位访问”?您的意思是编译器试图将结构的总大小设置为4字节(使用gcc-m32)或8字节(使用gcc-m64)的倍数?Regard将8字节结构转换为12。偏移量0数据1,偏移量1跳过以填充数据2,偏移量2数据2,偏移量3数据2的一部分。偏移量4数据3。偏移量处的字节似乎用于对齐数据2。-m32和-m64与结构的形成方式无关
struct
{
 char Data1;
 short Data2;
 int Data3;
 char Data4;
} __attribute__((packed)) x;

unsigned fun ( void )
{
    unsigned long long z;
    z=(unsigned long long)&x;
    x.Data1=1;
    x.Data2=2;
    x.Data3=3;
    x.Data4=4;
    return(sizeof(x));
}

int main ( void )
{
    fun();
}
00000000004004d6 <fun>:
  4004d6:   55                      push   %rbp
  4004d7:   48 89 e5                mov    %rsp,%rbp
  4004da:   48 c7 45 f8 38 10 60    movq   $0x601038,-0x8(%rbp)
  4004e1:   00 
  4004e2:   c6 05 4f 0b 20 00 01    movb   $0x1,0x200b4f(%rip)        # 601038 <x>
  4004e9:   66 c7 05 47 0b 20 00    movw   $0x2,0x200b47(%rip)        # 601039 <x+0x1>
  4004f0:   02 00 
  4004f2:   c7 05 3f 0b 20 00 03    movl   $0x3,0x200b3f(%rip)        # 60103b <x+0x3>
  4004f9:   00 00 00 
  4004fc:   c6 05 3c 0b 20 00 04    movb   $0x4,0x200b3c(%rip)        # 60103f <x+0x7>
  400503:   b8 08 00 00 00          mov    $0x8,%eax
  400508:   5d                      pop    %rbp
  400509:   c3                      retq