免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6400 | 回复: 15
打印 上一主题 下一主题

[C] 递归实验-C语言递归调用的极限 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-12-21 23:33 |只看该作者 |倒序浏览
C语言递归调用不是无限的,当递归到一定时候,会出现stack over flow的问题。http://en.wikipedia.org/wiki/Stack_buffer_overflow

但是,这个度是多少呢?

我构造了一个极为简单的C语言递归程序,大家可以参考这里https://gist.github.com/749543

另外要说明的是,由于只是为了开心写着玩,所以没有测试其它平台,也没有横向纵向对比,如Linux,Mac PPC。这些结果都是程序在我机器上运行得到,仅做参考。

#include "stdafx.h"
int count = 0;
void f()
{
  int a = 1; int b = 1; int c = 1; int d = 1;
  a = b + 3;
  c = count + 4;
  d = count + 5*c;
  count++;
  printf("count: %d\n", count);
  f();
}

int _tmain(int argc, _TCHAR* argv[])
     {
        f(); return 0;
      }

我使用的是VC2010SP1的C语言模式编译,console程序,无优化。

运行结果如下:

默认情况下,count最后结果为42860,然后程序就结束了。通过查询MSDN可以知道

http://msdn.microsoft.com/en-us/ ... 6%28v=vs.80%29.aspx

我们可以通过修改project的link选项中的stack commit size和stack reserve size来改变程序的stack大小。

如果这两个size都设置为1、64、640、2048这几个值,结果都是10092.

如果size设置为1024000,那么结果是42860,与默认情况相同。可以看出这个size是以BYTE为单位。

如果size设置为1048000,结果为43372.

size设置为1072000以及2048000,结果都是86551.

size设置为2110000以及3072000,结果都是130242.

一般来说,我们基本上不需要考虑修改这个大小,因为很少会有将近四万的递归层次,默认值已经足够用了。但是如果万一不够用或者有这种递归需求,那么就需要修改这两个值,另外当我们新建一个线程的时候,也可以编程修改线程stack的大小。

http://cs.nyu.edu/exact/core/doc/stackOverflow.txt 这里解释了一下不同平台上stackoverflow的问题,有一些数据可以参考。

http://stackoverflow.com/questio ... ble-stack-size-in-c 在这里有人提供了一个有趣的递归C代码片段来判断栈大小。

在这里多废话几句,关于Lua的proper tail call http://www.lua.org/pil/6.3.html 。当我们使用Lua for windows构造一个简单的递归程序运行,依然是会得到stack over flow的结果。但是当我们使用proper tail call这种方式调用,(由于抛弃了前面程序的栈)调用可以无限循环下去,所以Lua这个特性常常用来构造state machine。关于使用proper tail call来实现斐波拉契,可以参考这里http://lua-users.org/wiki/ProperTailRecursion

http://sunxiunan.com/?p=1784

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
2 [报告]
发表于 2010-12-22 00:10 |只看该作者
某些C编译器( gcc, vc的较高版本) 也可以优化一部分tail call。
只是C语言没有要求, 不像lua那样有保障。

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
3 [报告]
发表于 2010-12-22 06:33 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
1
CU十二周年纪念徽章
日期:2013-10-24 15:41:34
4 [报告]
发表于 2010-12-22 08:44 |只看该作者
我知道递归不是无限的,不过也不需要考虑这种情况{:3_189:}

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
5 [报告]
发表于 2010-12-22 09:03 |只看该作者
回复 4# rubylc_unix

  1. #include <stdio.h>

  2. void infinite(unsigend i, unsigned c)
  3. {
  4.       if (++i==0)
  5.             printf("%u\n", c++);
  6.       infinite(i, c);
  7. }

  8. int main(void)
  9. {
  10.       infinite(0, 0);
  11.       return 0;
  12. }
复制代码
编译器够好的话:
./a.out >nul

可以一直跑下去……

论坛徽章:
1
CU十二周年纪念徽章
日期:2013-10-24 15:41:34
6 [报告]
发表于 2010-12-22 09:07 |只看该作者
回复 5# OwnWaterloo


    谢谢提供实例,但是分配的栈不会溢出吗?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
7 [报告]
发表于 2010-12-22 09:11 |只看该作者
回复 5# OwnWaterloo

再改有趣一点……

  1. #include <stdio.h>
  2. #include <time.h>

  3. void infinite(unsigned i, unsigned c, clock_t t)
  4. {
  5.       if (++i==0)
  6.       {
  7.             clock_t t2 = clock();
  8.             printf("wrap %u tims, use %f sec\n"
  9.                   ,++c, (double)(t2-t)/CLOCKS_PER_SEC);
  10.             t = t2;
  11.       }
  12.       infinite(i, c, t);
  13. }

  14. int main(void)
  15. {
  16.       infinite(0, 0, clock() );
  17.       return 0;
  18. }
复制代码

论坛徽章:
0
8 [报告]
发表于 2010-12-22 09:13 |只看该作者
有特殊需求的时候还是非常有用的。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
9 [报告]
发表于 2010-12-22 09:20 |只看该作者
回复 6# rubylc_unix

假设:
f()
{

      ...
      g();

}

f函数以调用g作为最后一个语句。
再假设, 某个调用者(假设叫c)调用了f。


从语义上, 调用链应该是 c->f->g, 且逆向返回。
但考虑g返回到f后, 再没有多余的事可做; 所以可认为是g直接返回到c。
那么, 在调用g时, f的frame就不需要保持(因为返回时不需要), 继而……  f的frame就可以被g复用。

f中的g的调用, 称为尾调用。
如果尾调用是对自身的递归调用, 就称了尾递归。 优化工作比一般性的尾调用更容易。
每次递归调用时, 调用者的栈被复用, 原地作为被调用者的栈; 栈的总体使用不会增长。

具体可看汇编代码。  不过, 也就好玩而已, 切不可依赖。
实际代码还是老老实实的转化为迭代比较靠谱……

评分

参与人数 1可用积分 +8 收起 理由
davelv + 8 ++

查看全部评分

论坛徽章:
0
10 [报告]
发表于 2010-12-22 09:22 |只看该作者
顶楼主
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP