引自:http://www.oschina.net/translate/coroutines-in-c
在《计算机程序设计艺术》中,Donald Knuth提供了一个解决这类问题的方法。他的方法是彻底丢掉堆栈的概念,不要再想一个进程作为调用者,另一个作为被调用者,把他们当做平等的协作者关系。
实际上就是:把传统的“调用”稍微改为一个不同的方式。新的“调用”将在某个地方保存返回值而不是堆栈上,并且还能跳转到另一个保存返回值的指定位置上。因此,解码器每次生成一个字符,就保存它的程序计数器并且跳转到上次解析器的位置-解析器每次都需要一个新的字符,它保存自己的程序计数器并且跳转到上次解码器的位置。程序可以在两个函数之间来回自如的传递需要的数据了。
理论上看起来很美,但实际中你却只能在汇编语言中使用,因为通用的高级语言没有一个支持调用原始的协程。像类似于C的都是依赖于基础的堆栈结构,因此当在函数间进行数据传递时,一个必须作为调用者,领一个必须作为被调用者。所以如果你想写可移植的代码,这种技术和Unix管道一样不切实际。
int fuction(void)
{
static int i, state=0;
switch(state){
case 0: goto LABEL0;
case 1: goto LABEL1;
}
LABEL0:
for(i=0; i<10; i++)
{
state = 1;
return i;
LABEL1:;
}
}
更进一步:
int function(void)
{
static int i, state=0;
switch(state){
case 0:
for(i=0; i<10; i++){
state = 1;
retrn i;
case 1:;
}
}
}
更进一步
#define crBegin static int state=0; switch(state){case 0:
#define crReturn(i,x) do(state=i; return x; case i:;)while(0)
#define crFinish }
int function(void)
{
static int i;
crBegin;
for(i=0; i<10; i++)
crReturn(1,i);
crFinish;
}
剩下的唯一问题是传给crReturn的第一个参数。就像在上一节引进一个新标签一样,我们必须避免与已存在的标签名冲突,确保所有给crReturn的状态参数都是不同的。这影响是相当小的 -- 编译器会抓住它并并不让它在运行时出错 -- 但我们还是要避免这样做。
虽然这可以解决,ANSI C还是提供了扩展到当前行号的专门的宏名:__LINE__,因此我们可以把crReturn重写成:
#define crReturn(x) do(state=__LINE__; return x;\ case:__LINE__;)while(0)