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

  • 相关阅读:
    linux基础_用户和组的三个文件
    python_文件
    linux基础_用户组的管理
    mysql基础_数据类型
    mysql基础_操作数据库、表、记录
    linux基础_用户管理
    python_集合
    linux基础_关机重启注销
    docker创建私有仓库
    制作docker镜像
  • 原文地址:https://www.cnblogs.com/landpack/p/8459398.html
Copyright © 2011-2022 走看看