函数调用机制

来源:互联网 发布:免费客户跟进软件 编辑:程序博客网 时间:2024/04/30 01:02
 栈是一种数据结构,它的工作原理就像在子弹匣中压子弹一样,最先压人的子弹要等到最后才飞射出去,而最后压人的子弹则首先飞射出去。
  C++的函数调用过程, 需要调用初始化和善后处理的环节。函数调用的整个过程就是栈空间操作的过程。函数调用时,C++首先:
  (1)建立被调函数的栈空间。
  (2)保护调用函数的运行状态和返回地址。
  (3)传递参数。
  (4)将控制转交被调函数。
  例如,下面的代码在主函数中调用一个函数,该函数又调用了另一个函数,它得到图5-3中所示的内存布局:
    
void funcA(int,int);
    void funcB(int);
    void main()
    {
     int a=6,b=12;
     funcA(a,b);
    }
    void funcA(int aa,int bb)
    {
     int n=5;
     //...
     funcB(n);
    }

    void funcB(int s)
    {
     int x;
     //...

    }
  该函数运行的栈区分布图是示意性的。函数的栈操作原理是一致的,但具体的实现因编译器不同而不同。介绍函数栈的操作具体过程之目的是要让读者对函数运行的机制有一个感性的认识。
  在图5-3中,主函数main()返回的地址应该是在操作系统环境中。 其参数是操作系统传递过来的,在8.9节中将对其进行介绍。主函数定义了两个局部变量a和b,a和b的次序并不一定如图所示, 这些实现的细节都是与C++标准无关的。我们观察栈内存时,可以忽略返回地址和调用函数运行状态。
  当主函数调用funcA()时,funcA()着手保护调用函数的地址等数据,分配两个形参的空间,将主函数的实参传递过来。 所以funcA()中的形参aa和bb分别为6和12。
  funcA()的栈区和main()的栈区是互相独立的。在funcA()中不能访问main()中的局部变量a和b。通过参数传递, 使得funcA()中的形参赋有main()函数中的实参值。funcA()可以修改其变量aa和bb, 但始终不会影响main()中的a和b。这便是C++函数的参数传值特性。



图5—3 函数调用机制中的栈结构

  在函数funcA()中,定义了一个局部变量n, 并以该变量作实参来调用函数funcB()。同样,funcB()建立了它自己的栈空间,保护funcA()的返回地址,获取参数值,随后运行它自己的函数体语句。
  栈是有限的资源,每次调用一个函数,
剩余的栈空间会逐渐减少。可以看出,如果一层层地调用下去,或者,过多的定义局部变量特别是数组 (将在第7章介绍),最后可能导致栈空间枯竭而引起程序运行出错。如果程序确实要占用相当大的栈空间,可以在连接前通过设置栈空间大小来改善 (见实验指导书)。
  函数在返回时,将把返回值保护在临时变量空间中(如果有返回值的话),然后恢复调用函数的运行状态,释放栈空间,使其属于调用函数栈空间的一部分,最后根据返回地址,回到调用函数。
  图5-4是funcB()返回到funcA(),funcA()又回到main()时的栈内容。 可以看出,返回到主函数后, funcA()和uncB()的局部变量和形参都消失,但对应内存中的内容还是存在着,这就是上节中,程序chil.cpp得到该运行结果的原因。

原创粉丝点击