C de setjmp ve longjmp'nin pratik kullanımı


100

Biri bana tam olarak nerede setjmp()ve longjmp()işlevlerin gömülü programlamada pratik olarak kullanılabileceğini açıklayabilir mi? Bunların hata işleme için olduğunu biliyorum. Ancak bazı kullanım durumlarını bilmek istiyorum.


Diğer programlamalarda olduğu gibi hata işleme için. Kullanımdaki farkı görmüyorum ???
Tony The Lion


Hız için? Evet. Çünkü a) bir döngüden daha yavaş çalışır ve b) kolayca optimize edilemediği için (bir veya iki gecikmeyi silmek gibi). Yani setjmp & longjmp açıkça kural!
TheBlastOne

Verilenlerden daha başka cevabı burada stackoverflow.com/questions/7334595/... Sen kullanabilir longjmp()bir gibi özellikle şeyler bir sinyal işleyici çıkmak BUS ERROR. Bu sinyal genellikle yeniden başlayamaz. Gömülü bir uygulama, güvenlik ve sağlam çalışma için bu durumu ele almak isteyebilir.
artless noise

Ve setjmpBSD ve Linux arasındaki performans farklılıkları ile ilgili olarak , kullanmayı öneren "Timing setjmp ve Joy of Standards" konusuna bakın sigsetjmp.
Ioannis Filippidis

Yanıtlar:


85

Hata işleme
Diğer birçok fonksiyonda iç içe geçmiş bir fonksiyonda derinlerde bir hata olduğunu ve hata işlemenin sadece üst seviye fonksiyonda anlamlı olduğunu varsayalım.

Aradaki tüm işlevlerin normal şekilde dönmesi ve daha sonraki işlemlerin anlamsız olduğunu veya hatta kötü olacağını belirlemek için dönüş değerlerini veya genel bir hata değişkenini değerlendirmek zorunda kalması çok sıkıcı ve garip olurdu.

Bu setjmp / longjmp'nin mantıklı olduğu bir durumdur. Bu durumlar, diğer dillerdeki (C ++, Java) istisnanın mantıklı olduğu duruma benzer.

Coroutines
Hata işlemenin yanı sıra, C'de setjmp / longjmp'ye ihtiyaç duyduğunuz başka bir durumu da düşünebilirim:

Coroutine'leri uygulamanız gerektiğinde durum budur .

İşte küçük bir demo örneği. Umarım bu, Sivaprasad Palas'ın bazı örnek kodlar için talebini karşılar ve TheBlastOne sorusuna setjmp / longjmp'nin düzeltmelerin uygulanmasını nasıl desteklediği sorusunu yanıtlar (gördüğüm kadarıyla standart dışı veya yeni bir davranışa dayanmamaktadır).

DÜZENLEME:
Bu aslında olması olabilir olan bir yapmak tanımsız davranış longjmp aşağı (MikeMB ait yorumu görmek; henüz doğrulamak için fırsatım olmadı gerçi) çağrı yığını.

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

Aşağıdaki şekil uygulama akışını göstermektedir:
yürütme akışı

Uyarı notu
setjmp / longjmp kullanılırken, genellikle dikkate alınmayan yerel değişkenlerin geçerliliği üzerinde bir etkisi olduğunu unutmayın.
Cf. Benim bu konu hakkında soru .


2
Setjmp hazırladığından ve longjmp mevcut çağrı kapsamından setjmp kapsamına geri atlamayı yürüttüğü için, bu eşgüdümlerin uygulanmasını nasıl destekler? O kadar uzun süredir devam eden rutin işleyişe nasıl devam edilir bilmiyorum.
TheBlastOne

2
@TheBlastOne Wikipedia makalesine bakın . Önünüzde kalırsanız infaza devam setjmpedebilirsiniz longjmp. Bu standart değildir.
Potatoswatter

11
Coroutinlerin, örneğinizde gösterildiği gibi değil, ayrı yığınlar üzerinde çalışması gerekir. De routineAve routineBaynı yığın kullanımı, sadece çok ilkel değiş tokuş eden kavramlar için çalışır. Eğer routineAaramaları derinden iç içe routineCilk çağrıdan sonra routineBve bu routineCkoşular routineBeşyordamın olarak, daha sonra routineBda geri dönüş yığınını (sadece yerel değişkenleri) yok edecektir routineC. Bu nedenle, özel bir yığın ayırmadan ( alloca()aradıktan sonra rountineBmı?) Bir tarif olarak kullanılırsa bu örnekle ciddi sorun yaşarsınız.
Tino

7
Lütfen cevabınızda çağrı yığınından aşağı atlamanın (A'dan B'ye) tanımsız bir davranış olduğunu belirtin).
MikeMB

2
Gerçekten tanımsızdır. Bağlamları zarif bir şekilde değiştirmek için her işlevi kendi bağımsız yığını üzerinde çalıştırmanız gerekecek
Meraklı

19

Teori, bunları hata işleme için kullanabilmenizdir, böylece zincirdeki her işlevdeki hataları ele almaya gerek kalmadan derinlemesine iç içe geçmiş çağrı zincirinden atlayabilirsiniz.

Her zeki teori gibi, gerçekle karşılaşıldığında bu da parçalanır. Ara işlevleriniz bellek ayırır, kilitleri alır, dosyaları açar ve temizleme gerektiren her türlü farklı şeyi yapar. Dolayısıyla, pratikte setjmp/ longjmportamınız üzerinde tam kontrole sahip olduğunuz çok sınırlı durumlar dışında (bazı gömülü platformlar) genellikle kötü bir fikirdir.

Deneyimlerime göre çoğu durumda kullanmanın setjmp/ longjmpişe yarayacağını düşündüğünüzde, programınız, çağrı zincirindeki her ara işlev çağrısının hata işleme yapabileceği kadar açık ve basittir veya yaptığınız exitzaman yapmanız gereken o kadar dağınık ve düzeltilmesi imkansızdır. hatayla karşılaşır.


3
Lütfen bakın libjpeg. C ++ 'da olduğu gibi, çoğu C rutini koleksiyonunun bir struct *şey üzerinde toplu olarak çalışması için a gerekir. Ara fonksiyon bellek ayırmalarınızı yerel olarak saklamak yerine, yapıda saklanabilirler. Bu, bir longjmp()işleyicinin belleği boşaltmasına izin verir . Ayrıca, bu, tüm C ++ derleyicilerinin olaydan 20 yıl sonra hala oluşturduğu çok fazla istisna tablosuna sahip değildir.
artless noise

Like every clever theory this falls apart when meeting reality.Aslında, geçici ayırma ve benzerleri longjmp()işi zorlaştırır , çünkü setjmp()çağrı yığınında birden çok kez yapmanız gerekir (çıkmadan önce bir tür temizleme gerçekleştirmesi gereken her işlev için bir kez, ardından "istisnayı yeniden yükseltmek" gerekir. tarafındanlongjmp() bağlamına ing başlangıçta) aldığını. Bu kaynaklar daha sonra değiştirilirse daha da kötüleşir setjmp(), çünkü onları patlatmalarını volatileönlemek için bunları beyan longjmp()etmeniz gerekir.
sevko

10

Kombinasyonu setjmpve longjmp"süper gücüdür goto". ÇOK dikkatli kullanın. Bununla birlikte, diğerlerinin açıkladığı gibi, a longjmp, get me back to the beginning18 işlev katmanı için bir hata mesajı vermek zorunda kalmadan, hızlı bir şekilde yapmak istediğinizde kötü bir hata durumundan kurtulmak için çok kullanışlıdır .

Ancak, tıpkı gotoama daha da kötüsü, bunu nasıl kullandığınıza GERÇEKTEN dikkatli olmalısınız. A longjmpsizi kodun başına geri götürecektir. setjmpBaşladığı yere geri dönmekle arasında değişmiş olabilecek diğer tüm durumları etkilemeyecektir setjmp. Böylece tahsisler, kilitler, yarı başlatılmış veri yapıları, vb., Çağrıldığı yere geri döndüğünüzde hala tahsis edilir, kilitlenir ve yarı başlatılır setjmp. Bu, bunu yaptığınız yerleri gerçekten önemsemeniz gerektiği anlamına gelir, longjmpDAHA FAZLA soruna neden olmadan aramanız GERÇEKTEN uygundur . Elbette, yapacağınız bir sonraki şey "yeniden başlatmak" ise [belki de hata hakkında bir mesaj kaydettikten sonra] - örneğin donanımın kötü durumda olduğunu keşfettiğiniz gömülü bir sistemde, o zaman tamam.

Ayrıca çok temel diş açma mekanizmaları sağladığını gördüm setjmp/ longjmpkullandım. Ama bu oldukça özel bir durum - ve kesinlikle "standart" iş parçacığı nasıl çalışmaz.

Düzenleme: Elbette, C ++ 'nın istisna noktalarını derlenen kodda saklar ve sonra neyin bir istisna verdiğini ve neyin temizlenmesi gerektiğini bilmesi gibi, "temizleme ile başa çıkmak" için kod eklenebilir. Bu, bir tür işlev işaretçisi tablosunu ve "eğer buradan aşağıdan atlarsak, bu işlevi bu argümanla çağırın" ı depolamayı içerir. Bunun gibi bir şey:

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

Bu sistemle, "C ++ gibi tam istisna işleme" yapabilirsiniz. Ancak oldukça dağınıktır ve kodun iyi yazılmış olmasına bağlıdır.


+1, tabii ki teorik olarak setjmpher başlatmayı korumak için çağrı yaparak temiz bir istisna işlemi uygulayabilirsiniz , bir la C ++… ve bunu iş parçacığı için kullanmanın standart olmadığını belirtmeye değer.
Potatoswatter

8

Gömülü bahsettiğiniz için, kullanım dışı bir duruma dikkat çekmeye değer olduğunu düşünüyorum : kodlama standardınız bunu yasakladığında. Örneğin MISRA (MISRA-C: 2004: Kural 20.7) ve JFS (AV Kural 20): "setjmp makrosu ve longjmp işlevi kullanılmamalıdır."


8

setjmpve longjmpbirim testinde çok faydalı olabilir.

Aşağıdaki modülü test etmek istediğimizi varsayalım:

#include <stdlib.h>

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

Normalde, test etme işlevi başka bir işlevi çağırırsa, gerçek işlevin belirli akışları test etmek için ne yaptığını taklit edecek bir saplama işlevi bildirebilirsiniz. Ancak bu durumda, exitgeri dönmeyen fonksiyon çağırır . Saplamanın bir şekilde bu davranışı taklit etmesi gerekiyor. setjmpvelongjmp bunu sizin için yapabilir.

Bu işlevi test etmek için aşağıdaki test programını oluşturabiliriz:

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

Bu örnekte, setjmptest etmek için işleve girmeden önce öğesini kullanırsınız , ardından exitaradığınız stubbed'delongjmp doğrudan test geri dönmek için .

Ayrıca, yeniden tanımlanmış exitözel bir değişkene sahip olduğunu ve programdan gerçekten çıkmak isteyip istemediğinizi görmek için kontrol ettiğini ve _exitbunu yapmak için çağrı yaptığını unutmayın. Bunu yapmazsanız, test programınız temiz bir şekilde kapanmayabilir.


6

Ben yazdım istisna Java benzeri taşıma mekanizmasını kullanarak C setjmp(), longjmp()ve sistem fonksiyonlarını. Özel istisnaları yakalar ama aynı zamanda SIGSEGV. Fonksiyon çağrılarında çalışan ve en yaygın iki iş parçacığı uygulamasını destekleyen istisna işleme bloklarının sonsuz yuvalanmasına sahiptir. Bağlantı zamanı devralma özelliğine sahip istisna sınıflarından oluşan bir ağaç hiyerarşisi tanımlamanıza olanak tanır ve catchifade, yakalanması veya aktarılması gerekip gerekmediğini görmek için bu ağacı yürütür.

İşte bunu kullanarak kodun nasıl göründüğüne dair bir örnek:

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

Ve burada içerme dosyasının çok fazla mantık içeren bir kısmı:

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

Ayrıca sinyal işleme ve bazı defter tutma mantığını içeren bir C modülü de var.

Uygulamak son derece zordu size söyleyebilirim ve neredeyse bırakıyordum. Java'yı olabildiğince yakın hale getirmek için gerçekten çabaladım; Sadece C ile ne kadar ileri gittiğimi şaşırtıcı buldum.

Eğer ilgilenirsen bana seslen.


1
Özel istisnalar için gerçek derleyici desteği olmadan bunun mümkün olmasına şaşırdım. Ancak asıl ilginç olan, sinyallerin nasıl istisnalara dönüştüğüdür.
Paul Stelian

Bir şey soracağım: Ya asla yakalanmayan istisnalar? Main () nasıl çıkacak?
Paul Stelian

1
@PaulStelian Ve iştemain() yakalanmamış muafiyetten nasıl çıkacağınıza dair cevabınız . Lütfen bu yanıtı destekleyin :-)
anlam-meseleler

1
@PaulStelian Ah, ne demek istediğini şimdi anladım. Yakalanmayan çalışma zamanı istisnalarının, genel (platforma bağlı) yanıtın geçerli olması için yeniden gündeme getirildiğine inanıyorum. Yakalanmayan özel istisnalar basıldı ve göz ardı edildi. BENİOKU'dakiProgagation bölüme bakın Nisan 1999 kodumu GitHub'a gönderdim (düzenlenen cevaptaki bağlantıya bakın). Bir göz atın; kırılması zor bir cevizdi. Ne düşündüğünü duymak güzel olur.
anlam meseleleri

2
README'ye kısa bir göz attım, oldukça güzel. Yani temelde en dıştaki try bloğuna yayılır ve JavaScript'in eşzamansız işlevlerine benzer şekilde rapor edilir. Güzel. Kaynak kodun kendisine daha sonra bakacağım.
Paul Stelian

1

Kesinlikle, setjmp / longjmp'nin en önemli kullanımı, "yerel olmayan bir gitme" olarak hareket etmesidir. Goto komutu (ve döngüler için ve sırasında goto kullanmanız gereken nadir durumlar vardır) aynı kapsamda en güvenli şekilde kullanılır. Kapsamlar arasında (veya otomatik ayırmada) atlamak için goto kullanırsanız, büyük olasılıkla programınızın yığınını bozarsınız. setjmp / longjmp, yığın bilgisini atlamak istediğiniz konuma kaydederek bunu önler. Sonra zıpladığınızda bu yığın bilgisini yükler. Bu özellik olmasaydı, C programcıları büyük olasılıkla yalnızca setjmp / longjmp'nin çözebileceği sorunları çözmek için montaj programlamaya yönelmek zorunda kalacaklardı. Tanrıya şükür var. C kütüphanesindeki her şey son derece önemlidir. Ne zaman ihtiyacın olduğunu bileceksin.


1
"C kütüphanesindeki her şey son derece önemlidir." Yerel ayarlar gibi hiçbir zaman iyi olmayan bir sürü kullanımdan kaldırılmış şey var.
qwr

0

Hata işlemenin yanı sıra, yapabileceğiniz ve daha önce bahsedilmeyen diğer bir şey de kuyruk geri dönüşüm hesaplamasını C'de akıllı bir şekilde uygulamaktır.

Bu aslında devam geçiş stilinde girdi kodunu dönüştürmeden C'deki devamların nasıl gerçekleştirildiğidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.