c - alloca() 和 setjmp 的作用是什么?

标签 c coroutine setjmp alloca

这个问题来自Practical usage of setjmp and longjmp in CHow to implement coroutine within for loop in c这是我问的。

jmp_buf bufferA, bufferB;

void routineB(); // forward declaration

void routineA()
{
    int r = 0;

    printf("(A1)\n");

    if (setjmp(bufferA) == 0) {
        r++;
        alloca(2048);
        routineB();
    }

    printf("(A2) r=%d\n",r);

    if (setjmp(bufferA) == 0) {
        r++;
        longjmp(bufferB, 1);
    }

    printf("(A3) r=%d\n",r);

    if (setjmp(bufferA) == 0) {
        r++;
        longjmp(bufferB, 1);
    }

    printf("(A4) r=%d\n",r);
}

void routineB()
{
    int r = 0;

    printf("(B1)\n");

    if (setjmp(bufferB) == 0) {
        r++;
        longjmp(bufferA, 1);
    }

    printf("(B2) r=%d\n", r);

    if (setjmp(bufferB) == 0) {
        r++;
        longjmp(bufferA, 1);
    }

    printf("(B3) r=%d\n", r);

    if (setjmp(bufferB) == 0) {
        r++;
        longjmp(bufferA, 1);
    }

    printf("(B4) r=%d never reach\n", r);
}

int main()
{
    printf("main\n");
    routineA();
    return 0;
}

我正在研究 C 的协程实现,并试图看看 longjmp 之后堆栈中发生了什么。

问题1:

使用 alloca(2048) 后,有什么魔力让 routineB 堆栈保持事件状态? 我听说 alloca 很邪恶,但为什么它使堆栈看起来像扩展的。 我应该这样使用它吗?

输出:

main
(A1)
(B1)
(A2) r=1
(B2) r=1
(A3) r=2
(B3) r=2
(A4) r=3

问题2:

删除alloca(2048)后。告诉编译器禁用优化(-O2)后,它给出了不同的结果。

-O0

main
(A1)
(B1)
(A2) r=1
(B2) r=6356584
(A3) r=2
(B3) r=6356584
(A4) r=3

-O2

main
(A1)
(B1)
(A2) r=1
(B2) r=0
(A3) r=1
(B3) r=0
(A4) r=1

如果它不是未定义的,如何使代码获得相同的行为?如果是,请忘记 Q2。

最佳答案

这里有一篇关于使用 setjmp/longjmp/alloca 实现 coros 的文章:https://fanf.livejournal.com/105413.html

这个想法是,为了让 B 在长跳回 A 时保留其完整上下文(不仅是寄存器(由 setjmp 保留),还包括本地堆栈变量),B 需要自己的堆栈或至少它需要确保 A 所做的任何事情都不会覆盖 B 的变量。

alloca 是一种无需深入汇编即可实现此目的的方法。 alloca 基本上会将 B 在堆栈上移动得比 A 更远,因此除非 A 使用深度递归或任何使其使用超过 2KiB(在本例中)的堆栈的内容,否则 A 和 B将保持它们的栈上局部变量分开。

(这种技术自然不严格符合 C,如果您在多个 malloc 堆栈之间使用来回跳转,情况会更糟。)

关于c - alloca() 和 setjmp 的作用是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50483136/

相关文章:

c - 在链表中存储结构数组

c - 如何打开我的 .sgrd 文件?

android - Kotlin:跳过协程

jvm - Kotlin 中的 COROUTINE_SUSPENDED 和 suspendCoroutineOrReturn

c++ - 运行时功能测试、setjmp、longjmp 和信号掩码

python - 哪种编程语言在其正式规范中具有非常短的上下文无关语法?

c - ncurses.h,wprintw 不起作用,即使在刷新后也是如此

使用workmanager的Android定期位置更新

c - 是否允许为一次 setjmp() 调用多次执行 longjmp()?

c++ - 为什么在包含 <csetjmp> 时 setjmp 不在 std 命名空间中?