我们知道C语言当中结构可以使用{}进行初始化,例如有结构体定义如下:
typedef struct type_t { int a; int b; int c; int d; }type_t;
我们可以使用下面的语句来初始化一个变量:
struct type_t o = { 1, 2, 3, 4 }
但是如果像下面这样来初始化,结果会是什么呢?
type_t o = { 8, o.a, o.b, o.c };
其结果是依赖于编译器的实现,笔者分别在VS2010和GCC下进行了测试,结果如下:
1. vs2010下C和C++的结果都是:
o.a = 8, o.b = 8, o.c = 8, o.d = 8
2. GCC的C编译运行结果:
o.a = 8 o.b = 0, o.c = 0, o.d = 4195296
3. GCC的C++编译运行结果:
o.a = 8, o.b = 8, o.c = 8, o.d = 8
为啥会这样呢?带着疑问,笔者又去看了一下反汇编的代码,VS2010的反汇编代码如下:
type_t o = { 8, o.a, o.b, o.c }; 004113A8 mov dword ptr [ebp-18h],8 004113AF mov eax,dword ptr [ebp-18h] 004113B2 mov dword ptr [ebp-14h],eax 004113B5 mov eax,dword ptr [ebp-14h] 004113B8 mov dword ptr [ebp-10h],eax 004113BB mov eax,dword ptr [ebp-10h] 004113BE mov dword ptr [ebp-0Ch],eax
这段代码翻译过来的意思就是:
o.a = 8; eax = o.a o.b = eax eax = o.b o.c = eax eax = o.c o.d = eax
gcc编译的反汇编代码:
=> 0x00000000004004cd <+9>: mov -0x20(%rbp),%ecx 0x00000000004004d0 <+12>: mov -0x1c(%rbp),%edx 0x00000000004004d3 <+15>: mov -0x18(%rbp),%eax 0x00000000004004d6 <+18>: movl $0x8,-0x20(%rbp) 0x00000000004004dd <+25>: mov %ecx,-0x1c(%rbp) 0x00000000004004e0 <+28>: mov %edx,-0x18(%rbp) 0x00000000004004e3 <+31>: mov %eax,-0x14(%rbp)
这段代码可翻译如下:
ecx=o.a edx=o.b eax=o.c o.a=8 o.b=ecx o.c=edx o.d=eax
g++编译的反汇编代码:
=> 0x00000000004005ad <+9>: movq $0x0,-0x20(%rbp) 0x00000000004005b5 <+17>: movq $0x0,-0x18(%rbp) 0x00000000004005bd <+25>: movl $0x8,-0x20(%rbp) 0x00000000004005c4 <+32>: mov -0x20(%rbp),%eax 0x00000000004005c7 <+35>: mov %eax,-0x1c(%rbp) 0x00000000004005ca <+38>: mov -0x1c(%rbp),%eax 0x00000000004005cd <+41>: mov %eax,-0x18(%rbp) 0x00000000004005d0 <+44>: mov -0x18(%rbp),%eax 0x00000000004005d3 <+47>: mov %eax,-0x14(%rbp)
这段代码翻译如下:
将o变量清零 o.a = 8; eax = o.a o.b = eax eax = o.b o.c = eax eax = o.c o.d = eax
现在该完全清楚了,g++和vs2010生成的汇编指令基本一致,都是先将前一个成员初始化后再进行取值。而gcc则是先将o的成员变量值全部保存的寄存器中,然后逐一对o变量进行初始化。
但是所不明白的是为何gcc要这样生成指令?