深入理解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,也就是说这个类型决定了。基于这个问题,我们可以继续探索如何将变量作为返回值返回?它又会生成什么样的汇编代码?我将再下一篇分析

posted @ 2018-02-23 10:42  Landpack  阅读(546)  评论(0编辑  收藏  举报