首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >setjmp和longjmp在C中的实际使用

setjmp和longjmp在C中的实际使用
EN

Stack Overflow用户
提问于 2013-02-04 19:05:44
回答 5查看 52.6K关注 0票数 116

谁能给我解释一下,在嵌入式编程中,setjmp()longjmp()函数的具体用法是什么?我知道这些是用于错误处理的。但我想知道一些用例。

EN

回答 5

Stack Overflow用户

发布于 2013-02-04 19:42:15

其原理是,您可以将它们用于错误处理,这样您就可以跳出深度嵌套的调用链,而无需处理链中每个函数的错误。

就像每个聪明的理论一样,当遇到现实时,这一点就会崩溃。你的中间函数会分配内存,抓取锁,打开文件,做各种需要清理的事情。因此,在实践中,setjmp/longjmp通常不是一个好主意,除非在非常有限的情况下,您可以完全控制您的环境(一些嵌入式平台)。

根据我的经验,在大多数情况下,当您认为使用setjmp/longjmp可以工作时,您的程序足够清晰和简单,以至于调用链中的每个中间函数调用都可以进行错误处理,或者它是如此混乱和不可能修复,当您遇到错误时,您应该使用exit

票数 21
EN

Stack Overflow用户

发布于 2013-02-04 19:48:23

setjmplongjmp的结合就是“超强goto”。使用时要格外小心。然而,正如其他人所解释的那样,当您想要快速get me back to the beginning时,longjmp对于摆脱严重的错误情况非常有用,而不是必须为18层函数返回错误消息。

然而,就像goto一样,但更糟糕的是,你必须非常小心地使用它。一个longjmp会让你回到代码的开头。它不会影响在setjmp和返回到setjmp开始的位置之间可能发生更改的所有其他状态。因此,当您返回到调用setjmp的地方时,分配、锁、半初始化的数据结构等仍然是分配的、锁定的和半初始化的。这意味着,你必须真正关心你做这件事的地方,在不引起更多问题的情况下调用longjmp真的很好。当然,如果您下一步要做的事情是在存储有关错误的消息后“重新启动”,例如,在您发现硬件处于糟糕状态的嵌入式系统中,那么一切正常。

我还看到setjmp/longjmp被用来提供非常基本的线程机制。但这是非常特殊的情况--而且绝对不是“标准”线程的工作方式。

编辑:人们当然可以添加代码来“处理清理”,就像C++在编译后的代码中存储异常点,然后知道是什么引起了异常,什么是需要清理的。这将涉及到某种类型的函数指针表,并存储“如果我们从下面跳出来,用这个参数调用这个函数”。如下所示:

代码语言:javascript
复制
struct 
{
    void (*destructor)(void *ptr);
};


void LockForceUnlock(void *vlock)
{
   LOCK* lock = vlock;
}


LOCK func_lock;


void func()
{
   ref = add_destructor(LockForceUnlock, mylock);
   Lock(func_lock)
   ... 
   func2();   // May call longjmp. 

   Unlock(func_lock);
   remove_destructor(ref);
}

使用这个系统,你可以做“像C++一样完整的异常处理”。但它相当混乱,并且依赖于代码编写得很好。

票数 11
EN

Stack Overflow用户

发布于 2016-07-25 07:50:33

setjmplongjmp在单元测试中非常有用。

假设我们想要测试以下模块:

代码语言:javascript
复制
#include <stdlib.h>

int my_div(int x, int y)
{
    if (y==0) exit(2);
    return x/y;
}

通常,如果要测试的函数调用另一个函数,则可以声明一个存根函数供其调用,该函数将模拟实际函数测试某些流的操作。但是,在这种情况下,该函数调用不返回的exit。存根需要以某种方式模拟这种行为。setjmplongjmp可以为您做到这一点。

为了测试这个功能,我们可以创建以下测试程序:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <setjmp.h>

// redefine assert to set a boolean flag
#ifdef assert
#undef assert
#endif
#define assert(x) (rslt = rslt && (x))

// the function to test
int my_div(int x, int y);

// main result return code used by redefined assert
static int rslt;

// variables controling stub functions
static int expected_code;
static int should_exit;
static jmp_buf jump_env;

// test suite main variables
static int done;
static int num_tests;
static int tests_passed;

//  utility function
void TestStart(char *name)
{
    num_tests++;
    rslt = 1;
    printf("-- Testing %s ... ",name);
}

//  utility function
void TestEnd()
{
    if (rslt) tests_passed++;
    printf("%s\n", rslt ? "success" : "fail");
}

// stub function
void exit(int code)
{
    if (!done)
    {
        assert(should_exit==1);
        assert(expected_code==code);
        longjmp(jump_env, 1);
    }
    else
    {
        _exit(code);
    }
}

// test case
void test_normal()
{
    int jmp_rval;
    int r;

    TestStart("test_normal");
    should_exit = 0;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(12,3);
    }

    assert(jmp_rval==0);
    assert(r==4);
    TestEnd();
}

// test case
void test_div0()
{
    int jmp_rval;
    int r;

    TestStart("test_div0");
    should_exit = 1;
    expected_code = 2;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(2,0);
    }

    assert(jmp_rval==1);
    TestEnd();
}

int main()
{
    num_tests = 0;
    tests_passed = 0;
    done = 0;
    test_normal();
    test_div0();
    printf("Total tests passed: %d\n", tests_passed);
    done = 1;
    return !(tests_passed == num_tests);
}

在本例中,在输入要测试的函数之前使用setjmp,然后在存根exit中调用longjmp直接返回到测试用例。

还要注意,重新定义的exit有一个特殊的变量,它会检查您是否真的想要退出程序,并调用_exit来执行此操作。如果您不这样做,您的测试程序可能不会干净地退出。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14685406

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档