zoukankan      html  css  js  c++  java
  • C/C++函数内的数据存放方式

    一般来说,我们写的代码都是在main函数内运行,main函数就是一切的核心,这的确没错,

    main函数包含我们所写的代码的主要流程,我们会把想法灌注到其中去,写出一段段代码,最终编译出程序,

    即使面向对象的应用开发也是如此。

    不过main函数是个函数,它跟其他函数有没有什么根本上的区别?

    答案是否定的,我们用main()作为函数的开头只是因为编译器这样子要求,

    如果编译器要求我们用其他函数作为我们代码体的入口,那么main函数就只是一个单纯的函数。

    以上说的只是为了表明函数中数据存放方式的通用性,如果需要了解更多上面描述的相关信息,可以去看看程序反汇编或者相关资料。

    要用函数必须要设置栈(设置sp、ep,当然这是对于裸板程序来说的),栈是函数数据的基本表现形式,

    当要调用某个函数的时候,我们需要保存所调用函数所用到的寄存器的数据,数据保存在栈内;

    当有参数通过形参传给函数体时,通过栈来保存形参(某些编译器直接通过寄存器传数据,如果寄存器不够用的时候再用栈保存数据);

    当我们在函数提内定义变量时,在栈内保存该变量(这只使用与一般情形,特殊情况下面会讲到);

    寄存器sp是栈顶,ep是栈底,读写栈内数据就是通过sp跟ep来确定栈内地址,从而达到数据读写的目的

    下面基于i386跟g++分析函数内数据存放方式

    首先我们在main函数内定义了一段数据:

        int index=0;
        unsigned long sum=0;
        
        const int a=2;
        
        static int b=3;
        
        char * str[]={
            "hello ",
            "what ",
            "is ",
            "your ",
            "name ",
        };
        
        const unsigned long longnum[]={
            0x02345678,
            0x02345678,
            0x13245678,
            0x02345678,
            0x01345678,
            0x01245678,
            0x12345678,
            0x12345678,
        };

    然后用objdump查看反汇编:

      4011ba:    c7 45 f4 00 00 00 00     movl   $0x0,-0xc(%ebp)                        //int index=0;
      4011c1:    c7 45 f0 00 00 00 00     movl   $0x0,-0x10(%ebp)                        //unsigned long sum=0;
      4011c8:    c7 45 ec 02 00 00 00     movl   $0x2,-0x14(%ebp)                        //const int a=2;
      
      4011cf:    c7 45 c8 80 21 44 00     movl   $0x442180,-0x38(%ebp)            //"hello "字符串首地址
      4011d6:    c7 45 cc 87 21 44 00     movl   $0x442187,-0x34(%ebp)            //"what "字符串首地址
      4011dd:    c7 45 d0 8d 21 44 00     movl   $0x44218d,-0x30(%ebp)            //"is "字符串首地址
      4011e4:    c7 45 d4 91 21 44 00     movl   $0x442191,-0x2c(%ebp)            //"your "字符串首地址
      4011eb:    c7 45 d8 97 21 44 00     movl   $0x442197,-0x28(%ebp)            //"name "字符串首地址
      
      4011f2:    c7 45 a8 78 56 34 02     movl   $0x2345678,-0x58(%ebp)            //0x02345678,
      4011f9:    c7 45 ac 78 56 34 02     movl   $0x2345678,-0x54(%ebp)            //0x02345678,
      401200:    c7 45 b0 78 56 24 13     movl   $0x13245678,-0x50(%ebp)        //0x13245678,
      401207:    c7 45 b4 78 56 34 02     movl   $0x2345678,-0x4c(%ebp)            //0x02345678,
      40120e:    c7 45 b8 78 56 34 01     movl   $0x1345678,-0x48(%ebp)            //0x01345678,
      401215:    c7 45 bc 78 56 24 01     movl   $0x1245678,-0x44(%ebp)            //0x01245678,
      40121c:    c7 45 c0 78 56 34 12     movl   $0x12345678,-0x40(%ebp)        //0x12345678,
      401223:    c7 45 c4 78 56 34 12     movl   $0x12345678,-0x3c(%ebp)        //0x12345678,
    00442180 <.rdata>:
      442180:    68 65 6c 6c 6f     //"hello "
      442185:    20 00            
      442187:    77 68            //"what "
      442189:    61                
      44218a:    74 20             
      44218c:    00 69 73         //"is "
      44218f:    20 00          
      442191:    79 6f            // "your "
      442193:    75 72            
      442195:    20 00             
      442197:    6e               //"name "
      442198:    61               
      442199:    6d               
      44219a:    65 20 00            
      44219d:    00 00     

    由上面可以看出,int index =0;  unsigned long sum = 0;  const int a = 2;  都是在栈内自动生成(这些就是前面所说的一般情况)

    而 const * str[]={...}倒是从.rodata  (只读数据段)取数据

    const unsigned longnum[]={...}  里面的数据全部都在栈内自动生成。

    因此我们可以得出这样一个结论:

    在该编译器,栈内保存的都是类型化的(int,char,long,unsigned ..)的数据,并且对于重复性的类型化数据(如数组),也是保存在栈内。但是对于复杂性的数据(如:字符串),在只读数据段中保存。

    然后如果我们仔细观察上面的代码会发现,static int b = 3;没出现在上示代码段中,我们可以通过要求输出b来得到该变量的位置。

        cout<<b<<endl;

    反汇编:

      401247:    a1 04 20 44 00           mov    0x442004,%eax                //b的地址赋给eax寄存器
      40124c:    89 44 24 04              mov    %eax,0x4(%esp)
      401250:    c7 04 24 a0 53 44 00     movl   $0x4453a0,(%esp)
      401257:    e8 74 bd 02 00           call   42cfd0 <__ZNSolsEi>    

    static int b的位置:

    Disassembly of section .data:
    
    00442000 <__data_start__>:
      442000:    45                       inc    %ebp
      442001:    00 00                    add    %al,(%eax)
        ...
    
    00442004 <_ZZ4mainE1b>:
      442004:    03 00                    add    (%eax),%eax        //b的位置

    从上面看出,static 变量的时候,该变量是存放在.data(可读写数据段)的,并不在栈内。

    最后,对于不同的编译器,上面的描述可能会有些许不同

    比如在用交叉编译工具arm-linux-gcc编译代码的时候

    unsigned long 类型的数组是存放在只读数据段内的,

    它需要先把数据先从.rodata读进寄存器r0-r3,然后再从r0-r3寄存器把数据写到栈内

    如果该数组有大量数据,则会分开几次做数据传送。

  • 相关阅读:
    gitment Error:validation failed错误解决办法
    Hexo博客yilia主题添加Gitment评论系统
    用DateTime的ParseExact方法解析特殊的日期时间
    C#中的日期处理函数
    SQL,Linq,Lambda之间的转换练习
    Windows Azure Platform 系列文章目录
    Linq查询表达式
    EF框架的三种工作方式
    jQuery UI 实现图片循环显示,常用于网站首页banner广告切换
    jQuery UI Datepicker
  • 原文地址:https://www.cnblogs.com/TaigaCon/p/2824353.html
Copyright © 2011-2022 走看看