应该在 longjmp() 之后调用 free() 吗?

Should free() been called after longjmp()?

在这个简单的代码中,我应该注意默认情况下的内存释放以避免内存泄漏,还是我可以使用分配的内存? longjmp 是否也恢复内存分配?

#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, const char* argv)
{
jmp_buf jmpbuf;

int *p=NULL;

switch(setjmp(jmpbuf)){
case 0:
    p=(int *) malloc(10*sizeof(int));
    printf("%p\n",p);
    longjmp(jmpbuf,1);
    break;
default:
    printf("%p\n",p);
    free(p);
}


return 0;
}

不,longjmp 不会恢复动态内存分配。

longjmp 将恢复变量 "on the stack" 的 (在本例中为 "p")。但是,此示例中存在两个问题:

  • 在默认情况下,p 没有被赋值,释放它会出错。
  • 如果示例显示 p 正在分配一个示例,内存泄漏仍然无法修复,因为 longjmp 对此无能为力。

longjmp() 不会 为您释放内存,因此您必须手动 free() 它。这是使用 setjmp()/longjmp() 逃避深层调用堆栈的复杂性之一,因为您必须担心清理任何父函数使用的资源。请注意,在您的示例中,您 必须 p 声明为 volatile (int *volatile p = NULL;) to prevent it from potentially getting clobbered by longjmp(), since it's modified after the setjmp() call (see here 以获得解释)。

这是一个完全有效的程序,除了:

  1. 未将 p 声明为 volatile
  2. 声明 argvchar *

是的,如果在 longjmp() 之前分配了内存,则需要在 longjmp() 之后调用 free()

因为保证 setjmp 的 return 值在直接执行时将是 0,我们可以确定内存 将是 通过 case 0.

分配

并且,如果在 setjmp() 之后和调用 longjmp 之前需要更改局部变量,那么您需要将其声明为 volatile 以要求编译器不要优化和存储在寄存器中,但仅从堆栈到 store/load。

为什么?因为当 longjmp 加载保存的环境时,它会加载在直接 setjmp() 调用时保存的程序计数器、堆栈指针和寄存器中的值。如果编译器在 setjmp() 调用后优化变量并将其存储在寄存器中,则 longjmp 无法访问该值,因为它具有的是 时的寄存器值setjmp打电话。所以我们要求编译器不要保存在寄存器中,而是只从堆栈中保存 save/load。由于 longjmp 有堆栈指针,我们从堆栈中获取可访问变量的最新值。

更新代码:

#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    jmp_buf jmpbuf;
    volatile int *p;

    switch (setjmp(jmpbuf)) {
        case 0:
            p = malloc(10 * sizeof(int));
            printf("p = %p\n", (void *)p);
            longjmp(jmpbuf, 1);
            break;
        default:
            printf("p = %p\n", (void *)p);
            free((void *)p);
    }

    return 0;
}