为什么 setjmp/longjmp 在 MSVC 上会崩溃,而在 MinGW 中却不会?
我想使用 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;
}
I wanna use setjmp()
/longjmp()
to implement a coroutine system.
Then I decide to code a little .c file to test it. In MinGW, it's OK; I got the result I want.
But when I compile it in MSVC++, the program crashes: "access violation"
#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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
setjmp 的手册页显示:
在一个简单的实现中,您可能会假设
jmp_buf
包含一个要重置堆栈指针的地址和一个要跳转到的地址。一旦您从保存jmp_buf
的函数返回,jmp_buf
指向的堆栈帧就不再有效,并且可能立即损坏。或者换句话说,您只能依靠 longjmp 来充当某种超级
return
语句 - 永远不会更深入。我认为这对您在 mingw 中(以及对我在 Linux 上)有效的原因是特定于实现的,并且可能取决于运气。还有另一种方法 - 你读过Simon Tatham 的邪恶协程宏文章吗?
The man page for setjmp says:
In a simple implementation you might suppose that a
jmp_buf
contains an address to reset the stack pointer to and an address to jump to. As soon as you return from the function which saved thejmp_buf
, the stack frame pointed to by thejmp_buf
is no longer valid and may immediately become corrupted.Or in other words, you can only rely on longjmp to act as a sort-of super-
return
statement - never to go deeper.I think the reason this works for you in mingw (and for me on Linux) is implementation-specific and possibly down to luck. There is another way - have you read Simon Tatham's evil coroutine macros essay?
由于您正在调用未定义的行为,因此一个编译器崩溃而另一个编译器似乎正常工作是可以接受的。两者都是正确的——这就是未定义行为的美妙之处。
问题在于,保存的上下文 -
jmp_buf
- 只有在调用setjmp()
来设置它的函数尚未返回时才保持有效。C99 标准(不再是现行标准,但这种措辞不太可能发生重大变化)表示:
您的代码几乎立即从
action_1()
退出,使得setjmp()
保存的jmp_buf
毫无价值。几年前,我创建了这个
setjmp()
和longjmp()
的小演示。它可能对你有帮助。Since you're invoking undefined behaviour, it's OK for one compiler to crash and another to appear to work. Both are correct - that's the beauty of undefined behaviour.
The trouble is that a saved context - the
jmp_buf
- only remains valid as long as the function that calledsetjmp()
to set it has not returned.The C99 standard (no longer the current standard, but this wording is unlikely to have changed significantly) says:
Your code is exiting from
action_1()
almost immediately, rendering thejmp_buf
saved bysetjmp()
worthless.I created this little demonstration of
setjmp()
andlongjmp()
a couple of years ago. It may help you.您不能将 setjmp/longjmp 用于协程。在 POSIX 上使用 makecontext/swapcontext,或在 Windows 上使用纤程(CreateFiber 等)。
You cannot use setjmp/longjmp for coroutines. Use makecontext/swapcontext on POSIX, or fibers (CreateFiber, etc.) on windows.