Penggunaan praktis dari setjmp dan longjmp di C

99

Adakah yang bisa menjelaskan di mana tepatnya setjmp()dan longjmp()fungsi dapat digunakan secara praktis dalam pemrograman tertanam? Saya tahu bahwa ini untuk penanganan kesalahan. Tapi saya ingin tahu beberapa kasus penggunaan.

Pala
sumber
Untuk penanganan error seperti pada pemrograman lainnya. Saya tidak melihat perbedaan penggunaan ???
Tony The Lion
3
Dan tentu saja, thedailywtf.com/Articles/Longjmp--FOR-SPEED!!!.aspx
Daniel Fischer
Untuk kecepatan? Iya. Karena a) berjalan lebih lambat dari satu loop, dan b) karena tidak dapat dioptimalkan dengan mudah (seperti menghapus penundaan, atau dua). Jadi setjmp & longjmp jelas mengatur!
TheBlastOne
Jawaban lain selain yang diberikan ada di sini stackoverflow.com/questions/7334595/… Anda dapat menggunakannya longjmp()untuk keluar dari penangan sinyal, terutama hal-hal seperti a BUS ERROR. Sinyal ini biasanya tidak dapat dimulai ulang. Aplikasi yang disematkan mungkin ingin menangani kasus ini demi keamanan dan pengoperasian yang kuat.
kebisingan tanpa seni
Dan mengenai perbedaan performa setjmpantara BSD dan Linux, lihat "Timing setjmp, dan Joy of Standards" , yang menyarankan penggunaan sigsetjmp.
Ioannis Filippidis

Jawaban:

85

Penanganan kesalahan
Misalkan ada kesalahan jauh di dalam fungsi yang bersarang di banyak fungsi lain dan penanganan kesalahan hanya masuk akal di fungsi tingkat atas.

Akan sangat membosankan dan canggung jika semua fungsi di antaranya harus kembali normal dan mengevaluasi nilai yang dikembalikan atau variabel kesalahan global untuk menentukan bahwa pemrosesan lebih lanjut tidak masuk akal atau bahkan akan buruk.

Itu adalah situasi di mana setjmp / longjmp masuk akal. Situasi tersebut mirip dengan situasi di mana pengecualian dalam bahasa lain (C ++, Java) masuk akal.

Coroutines
Selain penanganan error, saya juga bisa memikirkan situasi lain di mana Anda memerlukan setjmp / longjmp di C:

Ini adalah kasus ketika Anda perlu mengimplementasikan coroutine .

Berikut adalah contoh demo kecil. Saya berharap ini memenuhi permintaan dari Sivaprasad Palas untuk beberapa kode contoh dan menjawab pertanyaan TheBlastOne bagaimana setjmp / longjmp mendukung implementasi corroutines (sejauh yang saya lihat itu tidak berdasarkan pada perilaku non-standar atau baru).

EDIT:
Bisa jadi itu benar-benar adalah perilaku undefined melakukan longjmp bawah callstack (lihat komentar dari MikeMB, meskipun saya belum memiliki kesempatan untuk memverifikasi bahwa).

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

Gambar berikut menunjukkan aliran eksekusi:
aliran eksekusi

Catatan peringatan
Saat menggunakan setjmp / longjmp ketahuilah bahwa mereka memiliki efek pada validitas variabel lokal yang sering tidak dipertimbangkan.
Cf. pertanyaan saya tentang topik ini .

Dadih
sumber
2
Sejak setjmp mempersiapkan, dan longjmp mengeksekusi lompatan keluar dari lingkup panggilan saat ini kembali ke lingkup setjmp, bagaimana hal itu akan mendukung implementasi coroutine? Saya tidak melihat bagaimana seseorang bisa melanjutkan pelaksanaan rutinitas yang sudah lama itu keluar.
TheBlastOne
2
@TheBlastOne Lihat artikel Wikipedia . Anda dapat melanjutkan eksekusi jika Anda setjmpsebelum Anda longjmp. Ini tidak standar.
Potatoswatter
11
Coroutine harus dijalankan di tumpukan terpisah, tidak sama seperti yang ditunjukkan dalam contoh Anda. Karena routineAdan routineBmenggunakan tumpukan yang sama, ini hanya berfungsi untuk coroutine yang sangat primitif. Jika routineApanggilan yang sangat bersarang routineCsetelah panggilan pertama ke routineBdan ini routineCberjalan routineBsebagai coroutine, maka routineBbahkan mungkin menghancurkan tumpukan kembali (tidak hanya variabel lokal) dari routineC. Jadi tanpa mengalokasikan tumpukan eksklusif (melalui alloca()setelah menelepon rountineB?) Anda akan mendapatkan masalah serius dengan contoh ini jika digunakan sebagai resep.
Tino
7
Harap sebutkan, dalam jawaban Anda bahwa melompat ke bawah callstack (dari A ke B) adalah perilaku yang tidak terdefinisi).
MikeMB
2
Memang tidak ditentukan. Anda harus membuat setiap fungsi berjalan pada tumpukan independennya sendiri untuk mengganti konteks dengan baik
Curious
19

Teorinya adalah Anda dapat menggunakannya untuk penanganan error sehingga Anda dapat keluar dari call chain yang sangat bertingkat tanpa perlu menangani error penanganan di setiap fungsi dalam chain.

Seperti setiap teori pintar, ini berantakan ketika bertemu dengan kenyataan. Fungsi perantara Anda akan mengalokasikan memori, mengambil kunci, membuka file, dan melakukan semua jenis hal berbeda yang memerlukan pembersihan. Jadi dalam praktiknya setjmp/ longjmpbiasanya merupakan ide yang buruk kecuali dalam keadaan yang sangat terbatas di mana Anda memiliki kendali penuh atas lingkungan Anda (beberapa platform tertanam).

Dalam pengalaman saya dalam banyak kasus kapan pun Anda berpikir bahwa menggunakan setjmp/ longjmpakan berfungsi, program Anda jelas dan cukup sederhana sehingga setiap panggilan fungsi perantara dalam rantai panggilan dapat melakukan penanganan kesalahan, atau sangat berantakan dan tidak mungkin untuk memperbaikinya yang harus Anda lakukan exitsaat Anda melakukannya. mengalami kesalahan.

Seni
sumber
3
Silakan lihat libjpeg. Seperti di C ++, sebagian besar kumpulan rutinitas C mengambil a struct *untuk beroperasi pada sesuatu sebagai kolektif. Alih-alih menyimpan alokasi memori fungsi menengah Anda sebagai penduduk lokal, mereka dapat disimpan dalam struktur. Ini memungkinkan seorang longjmp()penangan untuk membebaskan memori. Juga, ini tidak memiliki begitu banyak tabel pengecualian yang gagal sehingga semua kompiler C ++ masih menghasilkan 20 tahun setelah fakta.
kebisingan tanpa seni
Like every clever theory this falls apart when meeting reality.Memang, alokasi sementara dan sejenisnya membuat longjmp()rumit, karena Anda kemudian harus setjmp()beberapa kali dalam tumpukan panggilan (sekali untuk setiap fungsi yang perlu melakukan semacam pembersihan sebelum keluar, yang kemudian perlu "memunculkan kembali pengecualian" dengan longjmp()konteks yang awalnya diterima). Ini menjadi lebih buruk jika sumber daya tersebut diubah setelah setjmp(), karena Anda harus mendeklarasikannya volatileuntuk mencegah longjmp()dari clobber mereka.
sevko
10

Kombinasi setjmpdan longjmpadalah "kekuatan super goto". Gunakan dengan hati-hati EXTREME. Namun, seperti yang dijelaskan orang lain, a longjmpsangat berguna untuk keluar dari situasi kesalahan yang parah, ketika Anda ingin get me back to the beginningcepat, daripada harus menuangkan kembali pesan kesalahan untuk 18 lapisan fungsi.

Namun, sama seperti goto, tetapi lebih buruk, Anda harus BENAR-BENAR berhati-hati dalam menggunakan ini. A longjmphanya akan membuat Anda kembali ke awal kode. Ini tidak akan memengaruhi semua status lain yang mungkin telah berubah antara setjmpdan kembali ke setjmpawal. Jadi alokasi, kunci, struktur data setengah diinisialisasi, dll, masih dialokasikan, terkunci dan setengah diinisialisasi ketika Anda kembali ke tempat setjmpdipanggil. Ini berarti, Anda harus benar-benar memperhatikan tempat Anda melakukan ini, bahwa BENAR-BENAR boleh menelepon longjmptanpa menyebabkan LEBIH BANYAK masalah. Tentu saja, jika hal berikutnya yang Anda lakukan adalah "reboot" [setelah menyimpan pesan tentang kesalahan, mungkin] - dalam sistem tertanam di mana Anda telah menemukan bahwa perangkat keras dalam keadaan buruk, misalnya, baiklah.

Saya juga telah melihat setjmp/ longjmpdigunakan untuk menyediakan mekanisme penguliran yang sangat dasar. Tapi itu kasus yang cukup istimewa - dan jelas bukan cara kerja utas "standar".

Sunting: Tentu saja seseorang dapat menambahkan kode ke "menangani pembersihan", dengan cara yang sama seperti C ++ menyimpan titik pengecualian dalam kode yang dikompilasi dan kemudian mengetahui apa yang memberikan pengecualian dan apa yang perlu dibersihkan. Ini akan melibatkan semacam tabel penunjuk fungsi dan menyimpan "jika kita melompat keluar dari bawah sini, panggil fungsi ini, dengan argumen ini". Sesuatu seperti ini:

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

Dengan sistem ini, Anda dapat melakukan "penanganan pengecualian lengkap seperti C ++". Tapi itu cukup berantakan, dan bergantung pada kode yang ditulis dengan baik.

Mats Petersson
sumber
+1, tentu saja secara teori Anda dapat menerapkan penanganan pengecualian bersih dengan memanggil setjmpuntuk menjaga setiap inisialisasi, ala C ++… dan perlu disebutkan bahwa menggunakannya untuk threading tidak standar.
Potatoswatter
8

Karena Anda menyebutkan tertanam, saya pikir perlu diperhatikan kasus non-penggunaan : ketika standar pengkodean Anda melarangnya. Misalnya MISRA (MISRA-C: 2004: Aturan 20.7) dan JFS (AV Aturan 20): "Makro setjmp dan fungsi longjmp tidak boleh digunakan."

Clement J.
sumber
8

setjmpdan longjmpbisa sangat berguna dalam pengujian unit.

Misalkan kita ingin menguji modul berikut:

#include <stdlib.h>

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

Biasanya, jika fungsi yang akan diuji memanggil fungsi lain, Anda bisa mendeklarasikan fungsi stub untuk dipanggil yang akan meniru fungsi sebenarnya untuk menguji aliran tertentu. Namun dalam kasus ini, panggilan fungsi exityang tidak kembali. Rintisan harus meniru perilaku ini. setjmpdan longjmpdapat melakukannya untuk Anda.

Untuk menguji fungsi ini, kita dapat membuat program uji berikut:

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

Dalam contoh ini, Anda menggunakan setjmpsebelum memasukkan fungsi untuk menguji, kemudian di stubbed exitAnda memanggil longjmpuntuk kembali langsung ke kasus pengujian Anda.

Perhatikan juga bahwa redefined exitmemiliki variabel khusus yang diperiksa untuk melihat apakah Anda benar-benar ingin keluar dari program dan memanggil _exituntuk melakukannya. Jika Anda tidak melakukan ini, program pengujian Anda mungkin tidak berhenti dengan bersih.

dbush.dll
sumber
7

Saya telah menulis mekanisme penanganan pengecualian seperti Java di C menggunakan setjmp(), longjmp()dan fungsi sistem. Ini menangkap pengecualian khusus tetapi juga memberi sinyal suka SIGSEGV. Ini menampilkan blok penanganan pengecualian bersarang tak terbatas, yang bekerja di seluruh panggilan fungsi, dan mendukung dua implementasi threading yang paling umum. Ini memungkinkan Anda untuk menentukan hierarki hierarki kelas pengecualian yang menampilkan pewarisan waktu tautan, dan catchpernyataan berjalan di pohon ini untuk melihat apakah perlu menangkap atau meneruskan.

Berikut adalah contoh tampilan kode menggunakan ini:

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

Dan inilah bagian dari file include yang mengandung banyak logika:

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

Ada juga modul C yang berisi logika untuk penanganan sinyal dan beberapa pembukuan.

Sangat sulit untuk diterapkan. Saya dapat memberi tahu Anda dan saya hampir berhenti. Saya benar-benar mendorong untuk membuatnya sedekat mungkin dengan Java; Saya terkejut melihat seberapa jauh saya hanya dengan C.

Beri saya teriakan jika Anda tertarik.

arti-penting
sumber
1
Saya terkejut ini bisa terjadi tanpa dukungan kompiler aktual untuk pengecualian khusus. Tapi yang sangat menarik adalah bagaimana sinyal diubah menjadi pengecualian.
Paul Stelian
Saya akan menanyakan satu hal: bagaimana dengan pengecualian yang akhirnya tidak pernah tertangkap? Bagaimana main () keluar?
Paul Stelian
1
@PaulStelian Dan, inilah jawaban Anda tentang bagaimana main()keluar dari pengecualian tak tertangkap. Harap beri suara positif pada jawaban ini :-)
artinya-penting
1
@PaulStelian Ah, saya mengerti apa yang Anda maksud sekarang. Pengecualian run-time yang tidak tertangkap Saya yakin telah dimunculkan lagi sehingga jawaban umum (bergantung platform) berlaku. Tidak tertangkap pengecualian khusus dicetak dan diabaikan. Lihat Progagationbagian di README Saya telah memposting kode April 1999 saya ke GitHub (lihat link di jawaban yang diedit). Coba lihat; itu adalah kacang yang sulit untuk dipecahkan. Akan menyenangkan mendengar apa yang Anda pikirkan.
arti-penting
2
Telah melihat README, cukup bagus di sana. Jadi pada dasarnya ini menyebar ke blok percobaan terluar dan dilaporkan, mirip dengan fungsi asinkron JavaScript. Bagus. Saya akan melihat kode sumbernya sendiri nanti.
Paul Stelian
1

Penggunaan setjmp / longjmp yang paling penting adalah bahwa ia bertindak sebagai "lompatan goto non-lokal". Perintah Goto (dan ada kasus langka di mana Anda perlu menggunakan goto over untuk dan sementara loop) paling aman digunakan dalam lingkup yang sama. Jika Anda menggunakan goto untuk melompati cakupan (atau lintas alokasi otomatis), kemungkinan besar Anda akan merusak tumpukan program Anda. setjmp / longjmp menghindari ini dengan menyimpan info tumpukan di lokasi yang ingin Anda lompat. Kemudian, saat Anda melompat, info tumpukan ini dimuat. Tanpa fitur ini, programmer C kemungkinan besar harus beralih ke pemrograman assembly untuk menyelesaikan masalah yang hanya bisa diselesaikan oleh setjmp / longjmp. Terima kasih Tuhan itu ada. Semua yang ada di perpustakaan C sangat penting. Anda akan tahu kapan Anda membutuhkannya.

AndreGraveler
sumber
1
"Semua yang ada di perpustakaan C sangat penting." Ada banyak hal yang tidak digunakan lagi dan hal-hal yang tidak pernah bagus, seperti lokal.
qwr
0

Selain penanganan error, hal lain yang dapat Anda lakukan dan tidak disebutkan sebelumnya adalah mengimplementasikan penghitungan rektursif ekor di C dengan cara yang cerdas.

Ini sebenarnya bagaimana diimplementasikan kelanjutan dalam C tanpa mengubah kode masukan dalam gaya penerusan lanjutan.

alinsoar
sumber