/****************栈的应用:函数调用***************/
在函数进行调用的时候,系统会产生一个过程活动记录,它包括:局部变量、参数、指向前一个活动记录的指针、返回地址
一般来说这个过程活动记录块是压到栈中的,不过,如果不利用递归程序的,栈段就并非必须的了,因为当一个简单的函数被调用的时候不会将它的过程活动记录入栈,这大大的增加了计算机的运算负担。
另外栈段还可以用于暂时的存储区,也就是前面提到的临时数据的保存,这个临时数据一般是通过alloca()函数申请的,这也是一个防止内存泄漏的办法,但是它可能被下一个函数覆盖,所以它并不是一个好的办法。
程序例:
int Recall(int i) { if(i == 1) printf("i is the 1\n"); else Recall(--i);return 0; } int main() { int i = 3; Recall(i); return 0; } |
(1)递归调用的流程图
栈段是一般是由内存高地址向底地址生长,首先main的过程活动记录入栈,然后调用Recall函数,Recall的过程活动记录入栈,反复,知道printf函数执行了,再由printf函数返回,挨着返回,挨着退栈,知道main结束,这是整个递归函数的调用过程。
一般来说,对于非递归的简单函数,是不会让它入栈的,因为编译器会考虑到效率问题,从上面的递归函数调用原理来看,每一个Recall函数的参数、局部变量、前一个活动的指针和返回地址都要入栈,这大大拖慢了程序的速度,虽然经典的栈模式可以很好的解决递归问题,但是先入栈、最后等都执行完了再出栈,这种方法肯定慢了很多,效率不高,所以一般就不要用递归函数解决方法,即使它真的很利于大家理解。
(2)从函数中返回值的办法
从上面对递归函数调用原理的解说,我们可以更深刻的理解这么一个问题:不能从函数中返回一个指向该函数内部局部变量的指针。
当这个函数结束的时候,函数的过程活动记录退栈,内存被回收,其他的函数可能入栈覆盖掉了该函数,那么里面记录的所有值都是不确定的了,接着指针就成了野指针,这可是相当危险的事情。
但是我们仍然可以从函数中返回一个如我们希望的指针,看看最开始的C源文件在可执行文件的布局,数据段的生存周期是和程序一样的,它包括全局变量和静态变量,这也就是说我们把函数中的指针定义为静态变量就可以将这个指针轻松的返回了,因为它不会被回收,在程序结束之前。