使用volatile与longjmp/setjmp的安全用法

3

我考虑使用基于setjmp / longjmp的TRY / CATCH宏来处理错误。否则,我的一些非常结构化的函数将被丑陋的if语句和循环标志所破坏。

代码示例如下:

int trycatchtest(int i)
{
    int result = 0;
    volatile int error = 100;
    volatile uint32_t *var = NULL;
    TRY
    {
        error = 0;
        var = os_malloc(4);
        *var = 11;
        if (i) THROW( i );
    }
    FINALLY
    {
        result = *var;
    }
    END;
    return result;
}

THROW实际上是一个宏

#define TRY do { jmp_buf buf; switch( setjmp(buf) ) { case 0:     while(1) {
#define FINALLY break; } default: {
#define END break; } } } while(0)
#define THROW(x) longjmp(buf, x)

问题:
当抛出异常(例如i = 1)时,指针var被重置为NULL,尽管我使用了volatile关键字,这应该避免将其用于寄存器。从调试器中可以看到它仍在寄存器中而不是内存中。
我犯了一个错误吗?
编辑:
我将声明var更改为
uint32_t * volatile var = NULL;

这个有效;-)

我不是很清楚有什么区别:

volatile uint32_t * var = NULL;

这意味着VALUE是易变的,而前面的声明使指针易变。

当我编译这段代码并且用 printf("%d\n", trycatchtest(1)); 输出时,它返回了 11... 你是怎么编译的,用了什么标志? - tversteeg
我同意@tversteeg的观点,这在我的机器上也可以运行。这是哪个编译器/平台?os_malloc是如何定义的,可能你正在做一些嵌入式/RTOS相关的工作? - vgru
请查看我的上面编辑! - MichaelW
这是一些非常丑陋和危险的代码。不要写这样的垃圾,它比“on error goto”模式更糟糕。而这反过来又比“函数返回错误代码”更糟糕。 - Lundin
我同意@Lundin的观点,"on error goto"模式(例如在Linux内核源代码中广泛使用)比这个糟糕的方法更可取。你的trycatchtest函数似乎存在内存泄漏问题,尽管你只是用它来测试你的宏。由于buf是一个局部变量,这些宏在嵌套函数调用中不起作用,那么为什么你需要使用setjmp/longjmp来实现这些宏呢? - Ian Abbott
回复我上面的评论,我明白为什么你需要使用setjmp/longjmp来实现这些宏,而不是使用goto和本地标签,这是因为标签具有函数作用域,而不是块作用域。但我仍然不会使用这些宏! - Ian Abbott
1个回答

3
u32 *volatile var 使指针本身成为易变的,而 volatile u32 *var 告诉编译器该地址处的数据是易变的。因此,在后一种情况下,由于指针本身不是易变的,如果将其优化到像 result = NULL; 这样的default 情况,我不会感到惊讶。

编译器可能无法预料 setjmp 的巫术,这些操作比goto更加混乱。


关于C语言中最佳错误处理风格的争论永无止境。由于我只在大型测试例程中使用宏进行错误处理,它们为我简化了生活。 - MichaelW
https://dev59.com/X2Uq5IYBdhLWcg3wN90t - MichaelW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接