OK, 回到主题,言归正传。
请注意,是任意形式的递归,是化解的一般式。
通过学习这个一般式,学习完毕之后,任意复杂的递归式,要将其化解为栈处理,只需要按照死步骤一步一步做就可以了 -- 当然,LZ这里说的是,该怎么样去写代码,因为它最简单直白。怎么样,如果还不会这个一般式,或者,在做类似的事情时总有些感觉吃力,那么它听起来是不是还是有一点儿诱惑力的,呵呵。
为了不对新手造成误导,这里有必要澄清本帖的主题。主题所谓的“递归调用化解为栈处理”,意思是,将递归函数调用化解为“一个由stack_push stack_pop stack_top等函数调用组成的循环式子”。这里的 stack_push, stack_pop, stack_top是指,程序员自己实现的一个ADT(Abstract Data Type)中的函数操作接口,这个ADT叫做栈。
要知道,在C语言中,函数调用链本身就是栈处理的,处理C语言中函数调用链的是进程栈/线程栈,进程栈/线程栈是一个C语言程序运行环境的必备部件。
将递归调用化解为栈处理的意义在于,大多数计算机系统,对进程栈/线程栈的大小都是有限制的。譬如在Linux系统上,进程栈的大小限制大约是10MB. 因此,如果程序员直接使用进程栈来进行递归调用, 有可能耗尽进程栈空间,导致 Segment Fault. 虽然进程栈的大小是可调整的;然而程序员最好不要做这样的假设;因此,将递归调用化解到一般位于堆(Heap)上的ADT栈上, 在某些场合也许非常有必要。
更为苛刻的是,比如Linux内核,每个进程的内核栈大小只有1 - 2页,每页只有4 / 8KB, 也就是最多只有16KB的内核栈;假设程序员在编制一个驱动程序;这个驱动程序如果耗尽了内核栈 -- 呵呵,结果嘛...
============> 阅读本帖的童鞋, 请注意!!!
读到这里,如果你还是没有明白本帖的主题是指什么,或者你不知道什么是进程/线程栈,或者你不清楚如何实现一个叫做栈的ADT(Abstract Data Type, 抽象数据类型, 数据结构中讲授的内容),那么,你非常非常不适合阅读这篇文章,这篇文章可能会对你造成概念上的混淆什么的。
===》因此,LZ要在这里劝退你,等你有了足够的基础知识之后再来阅读。如果你具备了足够的基础知识,而且你正好对于本帖主题的问题有所迷惑,那么欢迎你,LZ向你推荐本文(稍微有点那啥卖瓜的味道,呵呵).
[当然, 本帖讨论的语境限于C语言, 因此,
1. 请勿将
-- LisP Like的, prolog like的, ...
-- 或者所有functional programming paradigm, declarative paradigm的...东西乱入;
-- 甚至, C++ -- LZ个人口味上只是喜欢C, 对C++不是很感冒.
2. 并避免
-- corutine, continuation之类的东西;
理由: 1, 这些都不是C语言所具备的特性;2, LZ想要限定场景和语境以明确讨论主体]。