我想了解以下小代码中使用“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;
}
使用“gcc -m32
”,输出为:
size of struct MixedData = 12
size of struct X = 12
size of uint64_t = 8
struct X
的大小是否等于12,因为编译器设置了以下填充?
struct X {
char c; // 1 byte
char d[3]; // 3 bytes
uint64_t x; // 8 bytes
};
如果是这种情况,32位编译的单个字的大小是多少(4个字节?)?如果它等于4个字节,这将是一致的,因为12是4的倍数。
现在关于带有“MixedData
”汇编的gcc -m32
的大小,我得到“size of struct MixedData = 12
”。我不明白这个值,因为我看到结构的总大小必须是此结构中最大大小属性的倍数。例如,此处为structure MixedData
,最大属性为int Data3
sizeof(Data3) = 4 bytes
;为什么我们不用以下填充:
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 MixedData
的总大小将等于16 bytes
,而不是12 bytes
,就像我得到的那样。
有人能看出这两种解释有什么不对吗?
类似的问题是关于“gcc -m64
”汇编;输出是:
size of struct MixedData = 12
size of struct X = 16
size of uint64_t = 8
struct X
(16 bytes
)的大小似乎是一致的,因为我认为64位模式的编译器设置了以下填充:
struct X {
char c; // 1 byte
char d[7]; // 7 bytes
uint64_t x; // 8 bytes
};
但我不明白struct MixedData
(12 bytes
)的价值。实际上,我不知道编译器在这种情况下如何设置填充,因为12不是64位模式下的存储器字的倍数(假设这个等于8 bytes
)。你能告诉我在最后一种情况下(gcc -m64
)“struct MixedData
”生成的填充吗?
答案 0 :(得分:0)
这是好奇心
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
理解m32和m64可能描述得很差,一个基本上是32位处理器,32位寄存器(ebx,eax,ax,但不是rbx,rax)和另一个64位处理器和64位寄存器( RBX,EBX,BX,BH,BL)
结构体的大小或构造与所选指令集之间不必存在联系。
这里有趣的是struct 1 + 2 + 4 + 1 = 8的大小,所以他们可以用8个字节完成它。现在他们可能想要int对齐,所以它会填充一个字节,也许他们希望整个事物在32位边界上对齐添加3个更多,这可能就是发生了什么。 32位代码确实使这一点变得清晰,它们不仅对齐了int而且还对齐了short。因此,它们在Data1和Data2之间填充,以便在16位边界上对齐Data2,然后使Data3在32位边界上对齐,而Data3是一个字节,因此无法对齐。填充结尾以对齐.data中的下一个内容。
64位代码看起来很糟糕,也许他们希望链接器修补那个代码。
00000000004004d6 <fun>:
4004d6: 55 push %rbp
4004d7: 48 89 e5 mov %rsp,%rbp
4004da: c6 05 57 0b 20 00 01 movb $0x1,0x200b57(%rip) # 601038 <x>
4004e1: 66 c7 05 50 0b 20 00 movw $0x2,0x200b50(%rip) # 60103a <x+0x2>
4004e8: 02 00
4004ea: c7 05 48 0b 20 00 03 movl $0x3,0x200b48(%rip) # 60103c <x+0x4>
4004f1: 00 00 00
4004f4: c6 05 45 0b 20 00 04 movb $0x4,0x200b45(%rip) # 601040 <x+0x8>
4004fb: b8 0c 00 00 00 mov $0xc,%eax
400500: 5d pop %rbp
400501: c3 retq
啊,我明白这就是他们正在做的事情。这就是他们对Data2和Data3进行了对齐。我想我应该让它为整个结构生成地址......
struct
{
char Data1;
short Data2;
int Data3;
char Data4;
} 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 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
确认基地址0x60138。
结构与指令集无关。改为这个
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
结构的大小现在是8个字节,它们生成了未对齐的访问。