深入理解C
如何理解返回值
简单的分析如下程序:
1 int main(){
2 return 0;
3 }
通过gcc -S hello.c -o hello.s
获得如下汇编代码:
1 .section __TEXT,__text,regular,pure_instructions
2 .macosx_version_min 10, 13
3 .globl _main
4 .p2align 4, 0x90
5 _main: ## @main
6 .cfi_startproc
7 ## BB#0:
8 pushq %rbp
9 Lcfi0:
10 .cfi_def_cfa_offset 16
11 Lcfi1:
12 .cfi_offset %rbp, -16
13 movq %rsp, %rbp
14 Lcfi2:
15 .cfi_def_cfa_register %rbp
16 xorl %eax, %eax
17 movl $0, -4(%rbp)
18 popq %rbp
19 retq
20 .cfi_endproc
21
22
23 .subsections_via_symbols
在c语言中return 0;
编译后就是汇编语言中的xorl %eax, %eax语句;如果对c语言进行修改再编译,例如让其返回1而不是0再编译后得到如下:
1 int main(){
2 return 1;
3 }
再编译后我们得到如下的汇编语句:
1 .section __TEXT,__text,regular,pure_instructions
2 .macosx_version_min 10, 13
3 .globl _main
4 .p2align 4, 0x90
5 _main: ## @main
6 .cfi_startproc
7 ## BB#0:
8 pushq %rbp
9 Lcfi0:
10 .cfi_def_cfa_offset 16
11 Lcfi1:
12 .cfi_offset %rbp, -16
13 movq %rsp, %rbp
14 Lcfi2:
15 .cfi_def_cfa_register %rbp
16 movl $1, %eax
17 movl $0, -4(%rbp)
18 popq %rbp
19 retq
20 .cfi_endproc
21
22
23 .subsections_via_symbols
我们可以看到在C中返回的1编译后直接变为 16 movl $1, %eax
,而在返回0
时却采用了异或的方式获得0
而不是 16 movl $0, %eax
说明这里可能编译器采取了优化。
如何理解变量的内存分布
1 int g;
2 int main(){
3 return 1;
4 }
编译后,我们会得到如下汇编语句:
1 .section __TEXT,__text,regular,pure_instructions
2 .macosx_version_min 10, 13
3 .globl _main
4 .p2align 4, 0x90
5 _main: ## @main
6 .cfi_startproc
7 ## BB#0:
8 pushq %rbp
9 Lcfi0:
10 .cfi_def_cfa_offset 16
11 Lcfi1:
12 .cfi_offset %rbp, -16
13 movq %rsp, %rbp
14 Lcfi2:
15 .cfi_def_cfa_register %rbp
16 movl $1, %eax
17 movl $0, -4(%rbp)
18 popq %rbp
19 retq
20 .cfi_endproc
21
22 .comm _g,4,2 ## @g
23
24 .subsections_via_symbols
这时候我们可以看到唯一增加了一句int g
变量声明语句后,我们就得到了如下这句汇编。
22 .comm _g,4,2 ## @g
再试试如果声明变量的同时再进行初始化会得到什么:
1 int g=99;
2 int main(){
3 return 1;
4 }
现在我们再进行编译,可以得到如下汇编:
1 .section __TEXT,__text,regular,pure_instructions
2 .macosx_version_min 10, 13
3 .globl _main
4 .p2align 4, 0x90
5 _main: ## @main
6 .cfi_startproc
7 ## BB#0:
8 pushq %rbp
9 Lcfi0:
10 .cfi_def_cfa_offset 16
11 Lcfi1:
12 .cfi_offset %rbp, -16
13 movq %rsp, %rbp
14 Lcfi2:
15 .cfi_def_cfa_register %rbp
16 movl $1, %eax
17 movl $0, -4(%rbp)
18 popq %rbp
19 retq
20 .cfi_endproc
21
22 .section __DATA,__data
23 .globl _g ## @g
24 .p2align 2
25 _g:
26 .long 99 ## 0x63
27
28
29 .subsections_via_symbols
这时候我们得到了好几行汇编语句:
22 .section __DATA,__data
23 .globl _g ## @g
24 .p2align 2
25 _g:
26 .long 99 ## 0x63
我们可以猜测下面这几行的含义:
*22行声明数据段
*23行声明全局变量名
*24行(暂时未知)
*25行对全局变量进行赋值
*26行赋值类型为长整数据
为了确认我们的假设,现在增加一些全局变量。
1 int g=99;
2 int k;
3 int h=97;
4 int main(){
5 return 1;
6 }
我们得到如下结果:
1 .section __TEXT,__text,regular,pure_instructions
2 .macosx_version_min 10, 13
3 .globl _main
4 .p2align 4, 0x90
5 _main: ## @main
6 .cfi_startproc
7 ## BB#0:
8 pushq %rbp
9 Lcfi0:
10 .cfi_def_cfa_offset 16
11 Lcfi1:
12 .cfi_offset %rbp, -16
13 movq %rsp, %rbp
14 Lcfi2:
15 .cfi_def_cfa_register %rbp
16 movl $1, %eax
17 movl $0, -4(%rbp)
18 popq %rbp
19 retq
20 .cfi_endproc
21
22 .section __DATA,__data
23 .globl _g ## @g
24 .p2align 2
25 _g:
26 .long 99 ## 0x63
27
28 .globl _h ## @h
29 .p2align 2
30 _h:
31 .long 97 ## 0x61
32
33 .comm _k,4,2 ## @k
34
35 .subsections_via_symbols
很明显声明并且初始化一个全局变量是保存在数据段的,并且由globl
修饰。
28 .globl _h ## @h
29 .p2align 2
30 _h:
31 .long 97 ## 0x61
至于p2align
暂时不理解其含义,对应的为初始化的还是会保存为comm
段。
33 .comm _k,4,2 ## @k
为了理解4,2是什么意思,我们可以再增加一个不同类型的全局变量char
得到如下结果,(只截取关注的部分)
.comm _k,4,2 ## @k
.comm _c,1,0 ## @c
可以看到,我们的全局变量char
类型被声明为1,0
,也就是说这个类型决定了。基于这个问题,我们可以继续探索如何将变量作为返回值返回?它又会生成什么样的汇编代码?我将再下一篇分析
Can we drop this masquerade