Cách sử dụng thực tế của setjmp và longjmp trong C


98

Bất cứ ai có thể giải thích cho tôi nơi chính xác setjmp()và các longjmp()chức năng có thể được sử dụng thực tế trong lập trình nhúng? Tôi biết rằng những điều này là để xử lý lỗi. Nhưng tôi muốn biết một số trường hợp sử dụng.


Để xử lý lỗi như trong bất kỳ lập trình nào khác. Tôi không thấy sự khác biệt trong cách sử dụng ???
Tony The Lion


Đối với tốc độ? Đúng. Bởi vì a) nó chạy chậm hơn một vòng lặp, và b) bởi vì nó không thể được tối ưu hóa một cách dễ dàng (như xóa một hoặc hai độ trễ). Vì vậy, setjmp & longjmp quy tắc rõ ràng!
TheBlastOne

Một câu trả lời khác ngoài những câu được đưa ra là ở đây stackoverflow.com/questions/7334595/… Bạn có thể sử dụng longjmp()để thoát khỏi trình xử lý tín hiệu, đặc biệt là những thứ như a BUS ERROR. Tín hiệu này thường không thể khởi động lại. Một ứng dụng nhúng có thể muốn xử lý trường hợp này để đảm bảo an toàn và hoạt động mạnh mẽ.
tiếng ồn không có nghệ thuật,

Và liên quan đến sự khác biệt về hiệu suất setjmpgiữa BSD và Linux, hãy xem "Bộ định thời gian và Niềm vui của các tiêu chuẩn" , đề xuất sử dụng sigsetjmp.
Ioannis Filippidis

Câu trả lời:


84

Xử lý lỗi
Giả sử có một lỗi nằm sâu trong một hàm được lồng trong nhiều hàm khác và việc xử lý lỗi chỉ có ý nghĩa trong hàm cấp cao nhất.

Sẽ rất tẻ nhạt và khó xử nếu tất cả các hàm ở giữa phải trả về bình thường và đánh giá các giá trị trả về hoặc một biến lỗi toàn cục để xác định rằng việc xử lý thêm không có ý nghĩa hoặc thậm chí sẽ tệ.

Đó là một tình huống mà setjmp / longjmp có ý nghĩa. Những tình huống đó tương tự như tình huống mà ngoại lệ trong các ngôn ngữ khác (C ++, Java) có ý nghĩa.

Coroutines
Bên cạnh việc xử lý lỗi, tôi cũng có thể nghĩ đến một tình huống khác mà bạn cần setjmp / longjmp trong C:

Đó là trường hợp bạn cần triển khai các coroutines .

Đây là một ví dụ demo nhỏ. Tôi hy vọng nó sẽ đáp ứng yêu cầu từ Sivaprasad Palas về một số mã ví dụ và trả lời câu hỏi của TheBlastOne về cách setjmp / longjmp hỗ trợ việc triển khai corroutines (nhiều như tôi thấy nó không dựa trên bất kỳ hành vi không chuẩn hoặc mới nào).

CHỈNH SỬA:
Có thể thực sự là một hành vi không xác định để thực hiện một cuộc gọi longjmp xuống (xem nhận xét của MikeMB; mặc dù tôi vẫn chưa có cơ hội để xác minh điều đó).

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

jmp_buf bufferA, bufferB;

void routineB(); // forward declaration 

void routineA()
{
    int r ;

    printf("(A1)\n");

    r = setjmp(bufferA);
    if (r == 0) routineB();

    printf("(A2) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20001);

    printf("(A3) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20002);

    printf("(A4) r=%d\n",r);
}

void routineB()
{
    int r;

    printf("(B1)\n");

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10001);

    printf("(B2) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10002);

    printf("(B3) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10003);
}


int main(int argc, char **argv) 
{
    routineA();
    return 0;
}

Hình sau cho thấy quy trình thực thi:
luồng thực hiện

Lưu ý cảnh báo
Khi sử dụng setjmp / longjmp, hãy lưu ý rằng chúng có ảnh hưởng đến tính hợp lệ của các biến cục bộ thường không được xem xét.
Cf câu hỏi của tôi về chủ đề này .


2
Vì setjmp chuẩn bị và longjmp thực hiện bước nhảy ra khỏi phạm vi cuộc gọi hiện tại trở lại phạm vi setjmp, điều đó sẽ hỗ trợ việc triển khai các coroutines như thế nào? Tôi không hiểu làm thế nào mà người ta có thể tiếp tục thực hiện quy trình mà lâu nay không có.
TheBlastOne

2
@TheBlastOne Xem bài viết trên Wikipedia . Bạn có thể tiếp tục thực hiện nếu bạn setjmptrước bạn longjmp. Đây là không chuẩn.
Potatoswatter

10
Coroutines cần phải chạy trên các ngăn xếp riêng biệt, không chạy trên cùng một ngăn xếp như trong ví dụ của bạn. Như routineAroutineBsử dụng cùng một ngăn xếp, nó chỉ hoạt động cho các coroutines rất sơ khai. Nếu routineAcác cuộc gọi lồng nhau sâu routineCsau lần gọi đầu tiên đến routineBvà điều này routineCchạy routineBdưới dạng chương trình đăng quang, thì routineBthậm chí có thể phá hủy ngăn xếp trả về (không chỉ các biến cục bộ) của routineC. Vì vậy, nếu không phân bổ một ngăn xếp độc quyền (thông qua alloca()sau khi gọi rountineB?), Bạn sẽ gặp rắc rối nghiêm trọng với ví dụ này nếu được sử dụng như một công thức.
Tino

7
Vui lòng đề cập, trong câu trả lời của bạn rằng nhảy xuống callstack (từ A đến B) là hành vi không xác định).
MikeMB

1
Và trong chú thích 248) nó viết: "Ví dụ: bằng cách thực hiện một câu lệnh trả về hoặc bởi vì một lệnh gọi longjmp khác đã gây ra chuyển sang một lệnh gọi setjmp trong một hàm trước đó trong tập hợp các lệnh gọi lồng nhau." Vì vậy, việc gọi một hàm longjmp ra khỏi một hàm đến một điểm xa hơn nữa callstack cũng chấm dứt hành động đó và do đó nhảy trở lại nó sau đó là UB.
MikeMB

18

Lý thuyết là bạn có thể sử dụng chúng để xử lý lỗi để bạn có thể nhảy ra khỏi chuỗi cuộc gọi lồng nhau sâu sắc mà không cần phải xử lý lỗi trong mọi chức năng trong chuỗi.

Giống như mọi lý thuyết thông minh, điều này sẽ sụp đổ khi gặp thực tế. Các chức năng trung gian của bạn sẽ phân bổ bộ nhớ, lấy khóa, mở tệp và thực hiện tất cả các việc khác nhau yêu cầu dọn dẹp. Vì vậy, trong thực tế setjmp/ longjmpthường là một ý tưởng tồi trừ những trường hợp rất hạn chế khi bạn có toàn quyền kiểm soát môi trường của mình (một số nền tảng nhúng).

Theo kinh nghiệm của tôi trong hầu hết các trường hợp bất cứ khi nào bạn nghĩ rằng việc sử dụng setjmp/ longjmpsẽ hoạt động, chương trình của bạn đủ rõ ràng và đơn giản để mọi lệnh gọi hàm trung gian trong chuỗi cuộc gọi có thể thực hiện xử lý lỗi hoặc nó rất lộn xộn và không thể sửa chữa mà bạn nên làm exitkhi gặp lỗi.


3
Hãy nhìn vào libjpeg. Giống như trong C ++, hầu hết các tập hợp các quy trình C đều có struct *nhiệm vụ hoạt động trên một cái gì đó như một tập thể. Thay vì lưu trữ phân bổ bộ nhớ chức năng trung gian của bạn dưới dạng cục bộ, chúng có thể được lưu trữ trong cấu trúc. Điều này cho phép một longjmp()trình xử lý giải phóng bộ nhớ. Ngoài ra, điều này không có quá nhiều bảng ngoại lệ bị thổi phồng mà tất cả các trình biên dịch C ++ vẫn tạo ra 20 năm sau thực tế.
không khéo tiếng ồn

Like every clever theory this falls apart when meeting reality.Thật vậy, phân bổ tạm thời và những thứ tương tự làm cho việc nhập trở nên longjmp()phức tạp, vì sau đó bạn phải thực hiện setjmp()nhiều lần trong ngăn xếp cuộc gọi (một lần cho mọi hàm cần thực hiện một số loại dọn dẹp trước khi nó thoát, sau đó cần phải "nâng lại ngoại lệ" bằng cách longjmp()nhập vào ngữ cảnh mà nó đã nhận được ban đầu). Nó thậm chí còn tồi tệ hơn nếu những tài nguyên đó được sửa đổi sau setjmp()vì bạn phải khai báo chúng làvolatile để ngăn chặn việc chặn longjmp()chúng.
sevko

10

Sự kết hợp của setjmplongjmplà "siêu sức mạnh goto". Sử dụng với sự chăm sóc CỰC KỲ. Tuy nhiên, như những người khác đã giải thích, a longjmprất hữu ích để thoát khỏi tình huống lỗi khó chịu, khi bạn muốn get me back to the beginningnhanh chóng, thay vì phải gửi lại một thông báo lỗi cho 18 lớp chức năng.

Tuy nhiên, giống như goto, nhưng tệ hơn, bạn phải THỰC SỰ cẩn thận khi sử dụng nó. A longjmpsẽ chỉ đưa bạn trở lại phần đầu của mã. Nó sẽ không ảnh hưởng đến tất cả các trạng thái khác có thể đã thay đổi giữa setjmpvà quay lại nơi setjmpbắt đầu. Vì vậy, phân bổ, khóa, cấu trúc dữ liệu được khởi tạo một nửa, v.v., vẫn được cấp phát, khóa và khởi tạo một nửa khi bạn quay lại nơi setjmpđược gọi. Điều này có nghĩa là, bạn phải thực sự quan tâm đến những nơi bạn làm điều này, rằng nó THỰC SỰ ok để gọi longjmpmà không gây ra NHIỀU vấn đề. Tất nhiên, nếu điều tiếp theo bạn làm là "khởi động lại" [sau khi lưu trữ thông báo về lỗi, có lẽ] - trong một hệ thống nhúng mà bạn đã phát hiện ra rằng phần cứng đang ở trạng thái xấu, chẳng hạn, thì tốt.

Tôi cũng đã thấy setjmp/ longjmpsử dụng để cung cấp các cơ chế phân luồng rất cơ bản. Nhưng đó là trường hợp khá đặc biệt - và chắc chắn không phải là cách các luồng "tiêu chuẩn" hoạt động.

Chỉnh sửa: Tất nhiên người ta có thể thêm mã để "giải quyết việc dọn dẹp", giống như cách C ++ lưu trữ các điểm ngoại lệ trong mã đã biên dịch và sau đó biết điều gì đã tạo ngoại lệ và điều gì cần dọn dẹp. Điều này sẽ liên quan đến một số loại bảng con trỏ hàm và lưu trữ "nếu chúng ta nhảy ra từ bên dưới đây, hãy gọi hàm này, với đối số này". Một cái gì đó như thế này:

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);
}

Với hệ thống này, bạn có thể thực hiện "xử lý ngoại lệ hoàn chỉnh như C ++". Nhưng nó khá lộn xộn và phụ thuộc vào mã được viết tốt.


+1, tất nhiên về lý thuyết, bạn có thể thực hiện xử lý ngoại lệ sạch bằng cách gọi setjmpđể bảo vệ mọi lần khởi tạo, một la C ++… và đáng nói là sử dụng nó để phân luồng là không chuẩn.
Potatoswatter

8

Vì bạn đề cập đến nhúng, tôi nghĩ cần lưu ý một trường hợp không sử dụng : khi tiêu chuẩn mã hóa của bạn cấm nó. Ví dụ: MISRA (MISRA-C: 2004: Quy tắc 20.7) và JFS (Quy tắc AV 20): "Macro setjmp và hàm longjmp sẽ không được sử dụng."


8

setjmplongjmp có thể rất hữu ích trong kiểm thử đơn vị.

Giả sử chúng ta muốn kiểm tra mô-đun sau:

#include <stdlib.h>

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

Thông thường, nếu hàm để kiểm tra gọi một hàm khác, bạn có thể khai báo một hàm sơ khai để nó gọi hàm này sẽ bắt chước những gì hàm thực thực hiện để kiểm tra các luồng nhất định. Tuy nhiên, trong trường hợp này, hàm exitkhông trả về. Sơ khai cần phải mô phỏng bằng cách nào đó hành vi này. setjmplongjmpcó thể làm điều đó cho bạn.

Để kiểm tra chức năng này, chúng ta có thể tạo chương trình kiểm tra sau:

#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);
}

Trong ví dụ này, bạn sử dụng setjmptrước khi nhập hàm để kiểm tra, sau đó trong phần sơ đồ exitbạn gọi longjmpđể quay lại trực tiếp trường hợp thử nghiệm của mình.

Cũng lưu ý rằng biến được xác định lại exitcó một biến đặc biệt mà nó sẽ kiểm tra xem bạn có thực sự muốn thoát khỏi chương trình hay không và gọi _exitđể làm như vậy. Nếu bạn không làm điều này, chương trình thử nghiệm của bạn có thể không thoát sạch.


6

Tôi đã viết một Java như ngoại lệ cơ chế xử lý trong C sử dụng setjmp(), longjmp()và chức năng hệ thống. Nó bắt các ngoại lệ tùy chỉnh nhưng cũng có các tín hiệu như SIGSEGV. Nó có tính năng lồng vô hạn các khối xử lý ngoại lệ, hoạt động trên các lệnh gọi hàm và hỗ trợ hai triển khai luồng phổ biến nhất. Nó cho phép bạn xác định một hệ thống phân cấp cây của các lớp ngoại lệ có tính năng kế thừa thời gian liên kết vàcatch câu lệnh đi qua cây này để xem liệu nó có cần bắt hoặc truyền lại hay không.

Đây là ví dụ về cách mã trông như thế này:

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("", ""));
}

Và đây là một phần của tệp bao gồm chứa rất nhiều logic:

#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 */

Ngoài ra còn có một mô-đun C chứa logic để xử lý tín hiệu và một số sổ sách kế toán.

Tôi có thể nói với bạn là cực kỳ khó khăn để thực hiện và tôi gần như bỏ việc. Tôi thực sự đã cố gắng làm cho nó gần giống với Java nhất có thể; Tôi thấy thật ngạc nhiên khi chỉ với C.

Hãy hét lên cho tôi nếu bạn quan tâm.


1
Tôi ngạc nhiên là điều này có thể thực hiện được mà không cần hỗ trợ trình biên dịch thực tế cho các trường hợp ngoại lệ tùy chỉnh. Nhưng điều thực sự thú vị là cách tín hiệu chuyển đổi thành ngoại lệ.
Paul Stelian

Tôi sẽ hỏi một điều: những trường hợp ngoại lệ cuối cùng không bao giờ bị bắt thì sao? Main () sẽ thoát như thế nào?
Paul Stelian

1
@PaulStelian Và, đây là câu trả lời của bạn về cách main()thoát ra khi không thận trọng. Vui lòng ủng hộ câu trả lời này :-)
ý nghĩa-vấn đề

1
@PaulStelian Ah, tôi hiểu ý bạn rồi. Tôi tin rằng các trường hợp ngoại lệ thời gian chạy không được phát hiện lại được đưa ra để áp dụng câu trả lời chung (phụ thuộc vào nền tảng). Các ngoại lệ tùy chỉnh không bắt được đã được in và bỏ qua. Xem Progagationphần trong README Tôi đã đăng mã tháng 4 năm 1999 của mình lên GitHub (xem liên kết trong câu trả lời đã chỉnh sửa). Có một cái nhìn; nó là một hạt cứng để bẻ. Rất vui được nghe những gì bạn nghĩ.
ý nghĩa-vấn đề

2
Đã có một cái nhìn ngắn về README, khá đẹp ở đó. Vì vậy, về cơ bản nó truyền đến khối try ngoài cùng và được báo cáo, giống như các hàm không đồng bộ của JavaScript. Đẹp. Tôi sẽ xem xét chính mã nguồn sau.
Paul Stelian

1

Bỏ qua, cách sử dụng quan trọng nhất của setjmp / longjmp là nó hoạt động "nhảy goto không cục bộ". Lệnh Goto (và hiếm có trường hợp nào bạn cần sử dụng vòng lặp for và while) được sử dụng nhiều nhất-an toàn trong cùng một phạm vi. Nếu bạn sử dụng goto để nhảy qua các phạm vi (hoặc phân bổ tự động), rất có thể bạn sẽ làm hỏng ngăn xếp chương trình của mình. setjmp / longjmp tránh điều này bằng cách lưu thông tin ngăn xếp tại vị trí bạn muốn chuyển đến. Sau đó, khi bạn nhảy, nó sẽ tải thông tin ngăn xếp này. Nếu không có tính năng này, các lập trình viên C rất có thể sẽ phải chuyển sang lập trình hợp ngữ để giải quyết các vấn đề mà chỉ setjmp / longjmp mới có thể giải quyết được. Cảm ơn Chúa nó tồn tại. Mọi thứ trong thư viện C đều cực kỳ quan trọng. Bạn sẽ biết khi nào bạn cần.


"Mọi thứ trong thư viện C đều cực kỳ quan trọng." Có rất nhiều thứ không dùng nữa và những thứ không bao giờ tốt, như ngôn ngữ.
qwr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.