c语言 - alloca() 与 setjmp 的功能是什么?



这个问题来自 setjmp 和 longjmp 在 C 中的实际用法 和 如何在 C 中的 for 循环中实现协程 我问过。

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=%dn",r);
if (setjmp(bufferA) == 0) {
r++;
longjmp(bufferB, 1);
}
printf("(A3) r=%dn",r);
if (setjmp(bufferA) == 0) {
r++;
longjmp(bufferB, 1);
}
printf("(A4) r=%dn",r);
}
void routineB()
{
int r = 0;
printf("(B1)n");
if (setjmp(bufferB) == 0) {
r++;
longjmp(bufferA, 1);
}
printf("(B2) r=%dn", r);
if (setjmp(bufferB) == 0) {
r++;
longjmp(bufferA, 1);
}
printf("(B3) r=%dn", r);
if (setjmp(bufferB) == 0) {
r++;
longjmp(bufferA, 1);
}
printf("(B4) r=%d never reachn", r);
}
int main()
{
printf("mainn");
routineA();
return 0;
}

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

问题1:

是什么魔法让一堆routineB在使用alloca(2048)后活着? 我听说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堆栈之间来回跳转,那就更不严重了。

相关内容

  • 没有找到相关文章