我想使用setjmp()
/ longjmp()
来实现协程系统。
然后我决定编写一个.c文件来测试它。在MinGW,没关系;我得到了我想要的结果。
但是当我在MSVC ++中编译它时,程序崩溃:“访问冲突”
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
jmp_buf a;
int is_invoke=0;
void
action_1()
{
for ( ;; ) {
printf("hello~~~A\n");
if(!setjmp(a)) {
is_invoke=1;
return;
}
}
}
void
func()
{
if (is_invoke) {
longjmp(a,1);
}
action_1();
printf("end\n");
}
void
dummy()
{
;
}
int
main(int argc, char *argv[])
{
for ( ;; ) {
func();
dummy();
}
return 0;
}
答案 0 :(得分:7)
setjmp的手册页说:
setjmp()
将堆栈上下文/环境保存在env
中供以后使用longjmp()
。如果函数,堆栈上下文将无效 调用setjmp()
返回。
在一个简单的实现中,你可能会认为jmp_buf
包含一个地址来重置堆栈指针和一个跳转到的地址。一旦从保存jmp_buf
的函数返回,jmp_buf
指向的堆栈帧就不再有效,并且可能立即被破坏。
或者换句话说,你只能依靠longjmp作为一种超级return
语句 - 从不深入。
我认为这对你来说在mingw(对我来说在Linux上)适用的原因是特定于实现的,可能还有运气。还有另外一种方法 - 你读过Simon Tatham's evil coroutine macros论文吗?
答案 1 :(得分:4)
由于您正在调用未定义的行为,因此一个编译器崩溃并且另一个编译器似乎可以正常工作。两者都是正确的 - 这是未定义行为的美妙。
问题是保存的上下文 - jmp_buf
- 只有在调用setjmp()
来设置它的函数没有返回时才会保持有效。
C99标准(不再是现行标准,但这种措辞不太可能发生重大变化)说:
§7.13.2.1
longjmp
函数
longjmp
函数恢复最近调用的环境setjmp
宏在程序的相同调用中使用相应的jmp_buf
论点。如果没有这样的调用,或者函数包含 在此期间调用setjmp
宏已终止执行 208),或者如果setjmp
宏的调用在变量的标识符范围内 修改后的类型和执行已将该范围留在过渡期,行为未定义。208)例如,通过执行
return
语句或因为另一个longjmp
调用导致了 转移到嵌套调用集中较早的函数中的setjmp
调用。
您的代码几乎立即从action_1()
退出,使jmp_buf
保存的setjmp()
毫无价值。
几年前我创建了setjmp()
和longjmp()
的小演示。它可能对你有帮助。
/*
@(#)File: $RCSfile: setjmp.c,v $
@(#)Version: $Revision: 1.1 $
@(#)Last changed: $Date: 2009/10/01 16:41:04 $
@(#)Purpose: Demonstrate setjmp() and longjmp()
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 2009
*/
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
static jmp_buf target_location;
static void do_something(void)
{
static int counter = 0;
if (++counter % 10 == 0)
printf("---- doing something: %3d\n", counter);
if (counter % 1000 == 0)
{
printf("||-- doing_something: calling longjmp() with value -1\n");
longjmp(target_location, -1);
}
}
static void do_something_else(int i, int j)
{
printf("-->> do_something_else: (%d,%d)\n", i, j);
do_something();
if (i > 2 && j > 2 && j % i == 2)
{
printf("||-- do_something_else: calling longjmp() with value %d\n", (i + j) % 100);
longjmp(target_location, (i + j) % 100);
}
printf("<<-- do_something_else: (%d,%d)\n", i, j);
}
static void doing_stuff(void)
{
int i;
printf("-->> doing_stuff()\n");
for (i = rand() % 15; i < 30; i++)
{
int j;
do_something();
for (j = rand() % 10; j < 20; j++)
{
do_something_else(i, j);
}
}
printf("<<-- doing_stuff()\n");
}
static void manage_setjmp(void)
{
printf("-->> manage_setjmp()\n");
switch (setjmp(target_location))
{
case 0:
/* Initial return - get on with doing stuff */
doing_stuff();
break;
case -1:
/* Error return - terminate */
printf("<<-- manage_setjmp() - error return from setjmp()\n");
return;
default:
/* NB: not officially possible to assign the return from setjmp() */
printf("---- manage_setjmp() - non-error return from setjmp()\n");
doing_stuff();
break;
}
printf("<<-- manage_setjmp()\n");
}
int main(void)
{
printf("-->> main()\n");
manage_setjmp();
printf("<<-- main()\n");
return(0);
}
答案 2 :(得分:1)
你不能将setjmp / longjmp用于协同程序。在POSIX上使用makecontext / swapcontext,或在窗口上使用光纤(CreateFiber等)。