Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
setjmp和longjmp在C中的实际应用_C_Setjmp - Fatal编程技术网

setjmp和longjmp在C中的实际应用

setjmp和longjmp在C中的实际应用,c,setjmp,C,Setjmp,有人能告诉我在嵌入式编程中可以在哪里实际使用setjmp()和longjmp()函数吗?我知道这些是用于错误处理的。但是我想知道一些用例。错误处理 假设在嵌套在许多其他函数中的函数中有一个错误,并且错误处理仅在顶级函数中才有意义 如果中间的所有函数都必须正常返回并计算返回值或全局错误变量,以确定进一步处理没有意义或甚至不好,这将是非常繁琐和尴尬的 在这种情况下,setjmp/longjmp是有意义的。 这些情况类似于其他语言(C++,Java)中的异常有意义的情况 协同程序 除了错误处理之外,我

有人能告诉我在嵌入式编程中可以在哪里实际使用
setjmp()
longjmp()
函数吗?我知道这些是用于错误处理的。但是我想知道一些用例。

错误处理
假设在嵌套在许多其他函数中的函数中有一个错误,并且错误处理仅在顶级函数中才有意义

如果中间的所有函数都必须正常返回并计算返回值或全局错误变量,以确定进一步处理没有意义或甚至不好,这将是非常繁琐和尴尬的

在这种情况下,setjmp/longjmp是有意义的。 这些情况类似于其他语言(C++,Java)中的异常有意义的情况

协同程序
除了错误处理之外,我还可以考虑另一种情况,在C中需要setjmp/longjmp:

当您需要实施时就是这种情况

下面是一个小的演示示例。 我希望它能满足Sivapasad Palas对一些示例代码的要求,并回答Blastone的问题,即setjmp/longjmp如何支持腐蚀的实现(据我所知,它并不基于任何非标准或新行为)

编辑:
可能是在调用堆栈中执行
longjmp
实际上是未定义的行为(参见MikeMB的评论;尽管我还没有机会验证)

#包括
#包括
jmp_buf bufferA,bufferB;
void routineB();//远期申报
void routineA()
{
INTR;
printf(“(A1)\n”);
r=setjmp(bufferA);
如果(r==0)routineB();
printf(“(A2)r=%d\n”,r);
r=setjmp(bufferA);
如果(r==0)longjmp(bufferB,20001);
printf(“(A3)r=%d\n”,r);
r=setjmp(bufferA);
如果(r==0)longjmp(bufferB,20002);
printf(“(A4)r=%d\n”,r);
}
void routineB()
{
INTR;
printf(“(B1)\n”);
r=setjmp(bufferB);
如果(r==0)longjmp(bufferA,10001);
printf(“(B2)r=%d\n”,r);
r=setjmp(bufferB);
如果(r==0)longjmp(bufferA,10002);
printf(“(B3)r=%d\n”,r);
r=setjmp(bufferB);
如果(r==0)longjmp(bufferA,10003);
}
int main(int argc,字符**argv)
{
routineA();
返回0;
}
下图显示了执行流程:

警告注释
使用setjmp/longjmp时,请注意它们会影响通常未考虑的局部变量的有效性。

Cf.my.

理论上,您可以使用它们进行错误处理,这样您就可以跳出嵌套较深的调用链,而无需处理链中每个函数的错误处理

就像每一个聪明的理论一样,当遇到现实时,这一理论就会分崩离析。您的中间函数将分配内存、获取锁、打开文件并执行各种需要清理的操作。因此在实践中,
setjmp
/
longjmp
通常是个坏主意,除非在非常有限的情况下,您可以完全控制您的环境(某些嵌入式平台)


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

setjmp和
longjmp
的组合是“超级强度
转到
”。使用时要格外小心。然而,正如其他人所解释的,当您想要快速地
让我回到开头时,
longjmp
对于摆脱严重的错误情况非常有用,而不必为18层函数返回错误消息

然而,就像转到
,但更糟糕的是,你必须非常小心地使用它。一个
longjmp
将让您回到代码的开头。它不会影响在
setjmp
和返回到
setjmp
开始位置之间可能已更改的所有其他状态。因此,当您返回调用
setjmp
的位置时,分配、锁、半初始化的数据结构等仍然被分配、锁定和半初始化。这意味着,您必须真正关心您执行此操作的位置,即调用
longjmp
而不会导致更多问题。当然,如果下一步要做的是[在存储错误信息后]在嵌入式系统中“重新启动”——例如,在嵌入式系统中,您发现硬件处于不良状态,那么就可以了

我还看到了
setjmp
/
longjmp
用于提供非常基本的线程机制。但这是一个非常特殊的情况——而且肯定不是“标准”线程的工作方式

<>编辑:当然可以添加代码来处理“清理”,就像C++在编译代码中存储异常点一样,然后知道什么会给出异常和需要清理的内容。这将涉及某种函数指针表并存储“如果我们从下面跳出来,用这个参数调用这个函数”。大概是这样的:

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++一样完成异常处理”。但它相当混乱,并且依赖于代码编写良好

既然您提到嵌入式,我认为值得注意的是一个非用例:当您的编码标准禁止它时。例如,MISRA(MISRA-C:2004:规则20.7)和(AV规则20):“不应使用setjmp宏和longjmp函数。”

setjmp
longjmp
在单元测试中非常有用

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

#include <stdlib.h>

int my_div(int x, int y)
{
    if (y==0) exit(2);
    return x/y;
}
在本例中,在进入要测试的函数之前,使用
setjmp
,然后在存根
exit
y中
#include <stdlib.h>

int my_div(int x, int y)
{
    if (y==0) exit(2);
    return x/y;
}
#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);
}
try
{
    *((int *)0) = 0;    /* may not be portable */
}
catch (SegmentationFault, e)
{
    long f[] = { 'i', 'l', 'l', 'e', 'g', 'a', 'l' };
    ((void(*)())f)();   /* may not be portable */
}
finally
{
    return(1 / strcmp("", ""));
}
#ifndef _EXCEPT_H
#define _EXCEPT_H

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include "Lifo.h"
#include "List.h"

#define SETJMP(env)             sigsetjmp(env, 1)
#define LONGJMP(env, val)       siglongjmp(env, val)
#define JMP_BUF                 sigjmp_buf

typedef void (* Handler)(int);

typedef struct _Class *ClassRef;        /* exception class reference */
struct _Class
{
    int         notRethrown;            /* always 1 (used by throw()) */
    ClassRef    parent;                 /* parent class */
    char *      name;                   /* this class name string */
    int         signalNumber;           /* optional signal number */
};

typedef struct _Class Class[1];         /* exception class */

typedef enum _Scope                     /* exception handling scope */
{
    OUTSIDE = -1,                       /* outside any 'try' */
    INTERNAL,                           /* exception handling internal */
    TRY,                                /* in 'try' (across routine calls) */
    CATCH,                              /* in 'catch' (idem.) */
    FINALLY                             /* in 'finally' (idem.) */
} Scope;

typedef enum _State                     /* exception handling state */
{
    EMPTY,                              /* no exception occurred */
    PENDING,                            /* exception occurred but not caught */
    CAUGHT                              /* occurred exception caught */
} State;

typedef struct _Except                  /* exception handle */
{
    int         notRethrown;            /* always 0 (used by throw()) */
    State       state;                  /* current state of this handle */
    JMP_BUF     throwBuf;               /* start-'catching' destination */
    JMP_BUF     finalBuf;               /* perform-'finally' destination */
    ClassRef    class;                  /* occurred exception class */
    void *      pData;                  /* exception associated (user) data */
    char *      file;                   /* exception file name */
    int         line;                   /* exception line number */
    int         ready;                  /* macro code control flow flag */
    Scope       scope;                  /* exception handling scope */
    int         first;                  /* flag if first try in function */
    List *      checkList;              /* list used by 'catch' checking */
    char*       tryFile;                /* source file name of 'try' */
    int         tryLine;                /* source line number of 'try' */

    ClassRef    (*getClass)(void);      /* method returning class reference */
    char *      (*getMessage)(void);    /* method getting description */
    void *      (*getData)(void);       /* method getting application data */
    void        (*printTryTrace)(FILE*);/* method printing nested trace */
} Except;

typedef struct _Context                 /* exception context per thread */
{
    Except *    pEx;                    /* current exception handle */
    Lifo *      exStack;                /* exception handle stack */
    char        message[1024];          /* used by ExceptGetMessage() */
    Handler     sigAbrtHandler;         /* default SIGABRT handler */
    Handler     sigFpeHandler;          /* default SIGFPE handler */
    Handler     sigIllHandler;          /* default SIGILL handler */
    Handler     sigSegvHandler;         /* default SIGSEGV handler */
    Handler     sigBusHandler;          /* default SIGBUS handler */
} Context;

extern Context *        pC;
extern Class            Throwable;

#define except_class_declare(child, parent) extern Class child
#define except_class_define(child, parent)  Class child = { 1, parent, #child }

except_class_declare(Exception,           Throwable);
except_class_declare(OutOfMemoryError,    Exception);
except_class_declare(FailedAssertion,     Exception);
except_class_declare(RuntimeException,    Exception);
except_class_declare(AbnormalTermination, RuntimeException);  /* SIGABRT */
except_class_declare(ArithmeticException, RuntimeException);  /* SIGFPE */
except_class_declare(IllegalInstruction,  RuntimeException);  /* SIGILL */
except_class_declare(SegmentationFault,   RuntimeException);  /* SIGSEGV */
except_class_declare(BusError,            RuntimeException);  /* SIGBUS */


#ifdef  DEBUG

#define CHECKED                                                         \
        static int checked

#define CHECK_BEGIN(pC, pChecked, file, line)                           \
            ExceptCheckBegin(pC, pChecked, file, line)

#define CHECK(pC, pChecked, class, file, line)                          \
                 ExceptCheck(pC, pChecked, class, file, line)

#define CHECK_END                                                       \
            !checked

#else   /* DEBUG */

#define CHECKED
#define CHECK_BEGIN(pC, pChecked, file, line)           1
#define CHECK(pC, pChecked, class, file, line)          1
#define CHECK_END                                       0

#endif  /* DEBUG */


#define except_thread_cleanup(id)       ExceptThreadCleanup(id)

#define try                                                             \
    ExceptTry(pC, __FILE__, __LINE__);                                  \
    while (1)                                                           \
    {                                                                   \
        Context *       pTmpC = ExceptGetContext(pC);                   \
        Context *       pC = pTmpC;                                     \
        CHECKED;                                                        \
                                                                        \
        if (CHECK_BEGIN(pC, &checked, __FILE__, __LINE__) &&            \
            pC->pEx->ready && SETJMP(pC->pEx->throwBuf) == 0)           \
        {                                                               \
            pC->pEx->scope = TRY;                                       \
            do                                                          \
            {

#define catch(class, e)                                                 \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        else if (CHECK(pC, &checked, class, __FILE__, __LINE__) &&      \
                 pC->pEx->ready && ExceptCatch(pC, class))              \
        {                                                               \
            Except *e = LifoPeek(pC->exStack, 1);                       \
            pC->pEx->scope = CATCH;                                     \
            do                                                          \
            {

#define finally                                                         \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        if (CHECK_END)                                                  \
            continue;                                                   \
        if (!pC->pEx->ready && SETJMP(pC->pEx->finalBuf) == 0)          \
            pC->pEx->ready = 1;                                         \
        else                                                            \
            break;                                                      \
    }                                                                   \
    ExceptGetContext(pC)->pEx->scope = FINALLY;                         \
    while (ExceptGetContext(pC)->pEx->ready > 0 || ExceptFinally(pC))   \
        while (ExceptGetContext(pC)->pEx->ready-- > 0)

#define throw(pExceptOrClass, pData)                                    \
    ExceptThrow(pC, (ClassRef)pExceptOrClass, pData, __FILE__, __LINE__)

#define return(x)                                                       \
    {                                                                   \
        if (ExceptGetScope(pC) != OUTSIDE)                              \
        {                                                               \
            void *      pData = malloc(sizeof(JMP_BUF));                \
            ExceptGetContext(pC)->pEx->pData = pData;                   \
            if (SETJMP(*(JMP_BUF *)pData) == 0)                         \
                ExceptReturn(pC);                                       \
            else                                                        \
                free(pData);                                            \
        }                                                               \
        return x;                                                       \
    }

#define pending                                                         \
    (ExceptGetContext(pC)->pEx->state == PENDING)

extern Scope    ExceptGetScope(Context *pC);
extern Context *ExceptGetContext(Context *pC);
extern void     ExceptThreadCleanup(int threadId);
extern void     ExceptTry(Context *pC, char *file, int line);
extern void     ExceptThrow(Context *pC, void * pExceptOrClass,
                            void *pData, char *file, int line);
extern int      ExceptCatch(Context *pC, ClassRef class);
extern int      ExceptFinally(Context *pC);
extern void     ExceptReturn(Context *pC);
extern int      ExceptCheckBegin(Context *pC, int *pChecked,
                                 char *file, int line);
extern int      ExceptCheck(Context *pC, int *pChecked, ClassRef class,
                            char *file, int line);


#endif  /* _EXCEPT_H */