loop、iterate、traversal和recursion这几个词是计算机技术书中经常会出现的几个词汇。众所周知,这几个词分别翻译 为:循环、迭代、遍历和递归。乍一看,这几个词好像都与重复(repeat)有关,但有的又好像不完全是重复的意思。那么这几个词到底各是什么含义,有什么区别和联系呢?下面就试着解释一下。
1、循环(loop),指的是在满足条件的情况下,重复执行同一段代码。比如,while语句。
2、迭代(iterate),指的是按照某种顺序逐个访问列表中的每一项。比如,for语句。
3、遍历(traversal),指的是按照一定的规则访问树形结构中的每个节点,而且每个节点都只访问一次。
4、递归(recursion),指的是一个函数不断调用自身的行为。通俗的解释:递归就像往存钱罐里存钱,先往里边塞钱,2块,5块,10块这样的塞,叫入栈。取钱的时候,后塞进去的先取出来,这叫出栈。具体多少钱,要全部出栈才知道。
在程序中循环、迭代、遍历经常统称为循环语句,下面以编程方式输出著名的斐波那契数列为例对比一下循环和递归的区别。斐波那契数列指的是这样一个数列,从第3项开始,每一项都等于前两项之和。
首先给出用递归求解的代码:
int Fibonacci(unsigned int n)
{
if( n <= 0 )
return 0;
if( n == 1 )
return 1;
return Fibonacci(n-1)+Fibonacci(n-2);
}
然后给出循环实现的代码:
int Fibonacci(unsigned int n)
{
int res[2] = { 0, 1 };
if (n < 2)
return res[n];
int first = 1;
int second = 0;
int fibN = 0;
int i;
for (i = 2; i <= n; i++)
{
fibN = first + second;
second = first;
first = fibN;
}
return fibN;
}
对比代码会发现递归显得更整洁,函数里面调用函数自身,循环代码显得比较多,但是函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址及临时变量,这些空间的占用只有在递归退出时才会释放,当递归调用的层级太多的时候,就会超出栈的容量,从而导致调用栈溢出。而且往栈里压入数据和弹出数据都需要时间,所以不难理解递归的实现效率不如循环。
小编前几天写代码时就遇到了栈溢出的问题,程序运行时崩溃退出了,后来gdb调试时发现递归的层数太多,栈空间不足导致的。然后通过两种方法可以避免这个问题,一是修改堆栈的大小,二是把递归改成了循环。接下来顺便说说Linux系统下如何查看程序的栈大小设置,如何修改。查看一个Linux指令就可以搞定ulimit –a,就会显示出系统的一些设置参数,stack size就是进程堆栈的大小。如果要是多线程程序里需要对每个线程进行栈大小的设置,查询和设置函数pthread_attr_getstack和pthread_attr_setstack,具体使用方法在线程属性里讲解。
言归正传,如何修改进程的栈大小,在Linux系统下可以用命令ulimit –s 102400,栈大小改为100M了。
用命令修改只是暂时的,退出对话框时设置就会取消。如果想要永久修改可以修改系统的配置文件/etc/security/limits.conf,如下图16384就是修改后堆栈的大小,16M。