C'deki catch ifadelerini deneyin


105

Bugün başka dillerde var olan dene / yakala bloklarını düşünüyordum. Bir süre Google'da arandı, ancak sonuç yok. Bildiğim kadarıyla C'de dene / yakala diye bir şey yok. Ancak, onları "simüle etmenin" bir yolu var mı?
Elbette, assert ve başka hileler var, ancak dene / yakala gibi hiçbir şey, yükseltilmiş istisnayı da yakalayamaz. teşekkür ederim


4
İstisna benzeri mekanizmalar, yığın çözüldüğünde kaynakları otomatik olarak serbest bırakan bir mekanizma olmadan genel olarak kullanışlı olmayacaktır. C ++ RAII kullanır; Java, C #, Python vb. Çöp toplayıcıları kullanır. (Ve çöp toplayıcıların sadece hafızayı
boşalttığını

@jamesdlin, Neden C ile RAII yapamadık?
Pacerier

1
@Pacerier RAII, nesneler yok edildiğinde (yani yıkıcılar) işlevlerin otomatik olarak çağrılmasını gerektirir. Bunu C'de nasıl yapmayı öneriyorsun?
jamesdlin

Yanıtlar:


93

C'nin kendisi istisnaları desteklemez, ancak bunları bir dereceye kadar setjmpve longjmpçağrılarla simüle edebilirsiniz .

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened here\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjmp(s_jumpBuffer, 42);
}

Bu web sitesinde istisnaların nasıl simüle edileceğine dair güzel bir öğretici var setjmpvelongjmp


1
harika çözüm! Bu çözüm çapraz mı? Benim için MSVC2012'de çalıştı, ancak MacOSX Clang derleyicisinde işe yaramadı.
mannysz

1
bana ipucu verin: try catch cümlelerinin istisnaları yakalamanıza izin verdiğini düşündüm (sıfıra bölmek gibi). Bu işlev, yalnızca kendi kendinize attığınız istisnaları yakalamanıza izin veriyor gibi görünüyor. Longjmp'yi çağırarak gerçek istisnalar atılmaz değil mi? Böyle bir şey yapmak için bu kodu kullanırsam try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')}; işe yaramayacak mı?
Sam

Sıfıra bölünme, C ++ 'da bir istisna bile değildir, bunun üstesinden gelmek için, bölenin sıfır olmadığını kontrol etmeniz ve onu işlemeniz veya sıfıra bölünmüş bir formül çalıştırdığınızda atılan SIGFPE'yi işlemeniz gerekir.
James

25

Benzer hata işleme durumları için C'de goto kullanırsınız .
Bu, C'de elde edebileceğiniz istisnaların en yakın eşdeğeridir.


3
@JensGustedt Bu tam olarak goto'nun şu anda sıklıkla kullanıldığı şeydir ve mantıklı olduğu bir örnektir (setjmp / ljmp daha iyi bir alternatiftir, ancak label + goto genellikle daha fazla kullanılır).
Tomas Pruzina

1
@AoeAoe, muhtemelen gotodaha çok hata işleme için kullanılıyor, ama ne olmuş yani? Soru, hata işlemeyle ilgili değil, açıkça dene / yakala eşdeğerleriyle ilgilidir. gotoaynı işlevle sınırlı olduğu için denemek / yakalamak için eşdeğer değildir.
Jens Gustedt

@JensGustedt Nefret / gitme korkusuna ve onu kullanan kişilere tepki verdim (öğretmenlerim bana üniversitede de goto kullanımıyla ilgili korkunç hikayeler anlattı). [OT] Goto ile ilgili gerçekten, gerçekten riskli ve 'bulanık' olan tek şey 'geriye gitmektir', ancak bunu Linux VFS'de gördüm (git blame guy bunun performansın çok faydalı olduğuna yemin etti).
Tomas Pruzina

Modern, geniş çapta kabul gören, hakemli bir kaynakta kullanılan bir dene / yakala mekanizması olarak meşru kullanımları için systemctl kaynaklarına bakın goto. gotoBir "fırlatma" eşdeğeri ve finish"yakalama" eşdeğeri arayın .
Stewart

14

Tamam, buna cevap vermekten kendimi alamadım. Öncelikle bunu C'de simüle etmenin iyi bir fikir olduğunu düşünmediğimi söylememe izin verin, çünkü bu gerçekten C'ye yabancı bir kavram.

Biz yapabilirsiniz kullanmak kullanacağım C ++ deneme / atmak / catch sınırlı versiyonunu vermek önişlemci ve yerel yığın değişkenleri kötüye.

Sürüm 1 (yerel kapsam atar)

#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

Sürüm 1 yalnızca yerel bir atıştır (işlevin kapsamını bırakamaz). C99'un değişkenleri kodda bildirme yeteneğine dayanır (işlevde ilk şey try ise C89'da çalışmalıdır).

Bu işlev sadece yerel bir değişken yapar, böylece bir hata olup olmadığını bilir ve catch bloğuna atlamak için bir goto kullanır.

Örneğin:

#include <stdio.h>
#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

int main(void)
{
    try
    {
        printf("One\n");
        throw();
        printf("Two\n");
    }
    catch(...)
    {
        printf("Error\n");
    }
    return 0;
}

Bu şöyle bir sonuç verir:

int main(void)
{
    bool HadError=false;
    {
        printf("One\n");
        HadError=true;
        goto ExitJmp;
        printf("Two\n");
    }
ExitJmp:
    if(HadError)
    {
        printf("Error\n");
    }
    return 0;
}

Sürüm 2 (kapsam atlama)

#include <stdbool.h>
#include <setjmp.h>

jmp_buf *g__ActiveBuf;

#define try jmp_buf __LocalJmpBuff;jmp_buf *__OldActiveBuf=g__ActiveBuf;bool __WasThrown=false;g__ActiveBuf=&__LocalJmpBuff;if(setjmp(__LocalJmpBuff)){__WasThrown=true;}else
#define catch(x) g__ActiveBuf=__OldActiveBuf;if(__WasThrown)
#define throw(x) longjmp(*g__ActiveBuf,1);

Sürüm 2 çok daha karmaşıktır ancak temelde aynı şekilde çalışır. Mevcut fonksiyondan uzun bir atlama ile try bloğuna atlar. Try bloğu daha sonra kod bloğunu catch bloğuna atlamak için bir if / else kullanır ve bu da yakalanması gerekip gerekmediğini görmek için yerel değişkeni kontrol eder.

Örnek tekrar genişledi:

jmp_buf *g_ActiveBuf;

int main(void)
{
    jmp_buf LocalJmpBuff;
    jmp_buf *OldActiveBuf=g_ActiveBuf;
    bool WasThrown=false;
    g_ActiveBuf=&LocalJmpBuff;

    if(setjmp(LocalJmpBuff))
    {
        WasThrown=true;
    }
    else
    {
        printf("One\n");
        longjmp(*g_ActiveBuf,1);
        printf("Two\n");
    }
    g_ActiveBuf=OldActiveBuf;
    if(WasThrown)
    {
        printf("Error\n");
    }
    return 0;
}

Bu global bir işaretçi kullanır, böylece longjmp () en son çalıştırılan try'yi bilir. Biz edilir kullanarak çocuk fonksiyonları da bir deneme / yakalama bloğu olabilir böylece yığını kötüye.

Bu kodu kullanmanın birkaç olumsuz yönü vardır (ancak eğlenceli bir zihinsel egzersizdir):

  • Çağrılan hiçbir yapısökücü olmadığından ayrılmış belleği serbest bırakmaz.
  • Bir kapsamda 1'den fazla dene / yakala olamazsın (yuvalama yok)
  • C ++ gibi istisnaları veya diğer verileri gerçekten atamazsınız
  • İş parçacığı hiç güvenli değil
  • Diğer programcıları başarısızlık için ayarlıyorsunuz, çünkü muhtemelen hack'i fark etmeyecekler ve bunları C ++ deneme / yakalama blokları gibi kullanmayı deneyecekler.

güzel alternatif çözümler.
HaseeB Mir

sürüm 1 iyi bir fikir, ancak bu __HadError değişkeninin sıfırlanması veya kapsamının ayarlanması gerekir. Aksi takdirde, aynı blokta birden fazla try-catch kullanamazsınız. Belki gibi küresel bir işlev kullanın bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}. Ancak yerel değişken de yeniden tanımlanır, böylece işler biraz kontrolden çıkar.
flamewave000

Evet, aynı işlevde bir deneme yakalama ile sınırlıdır. Değişkenden daha büyük bir sorun, ancak aynı işlevde yinelenen etiketlere sahip olamayacağınız için etikettir.
Paul Hutchinson

10

C99'da, yerel olmayan kontrol akışı için setjmp/ kullanabilirsiniz longjmp.

Tek bir kapsam içinde , bu örnekte olduğugoto gibi , çoklu kaynak tahsisi ve çoklu çıkışların varlığında C için genel, yapılandırılmış kodlama modeli kullanır . Bu, C ++ 'ın kaputun altındaki otomatik nesnelerin yıkıcı çağrılarını nasıl uyguladığına benzer ve buna özenle bağlı kalırsanız, karmaşık işlevlerde bile belirli bir temizlik derecesi için size izin vermelidir.


5

Diğer cevaplardan bazıları basit durumları kapsarken setjmpve longjmpgerçek bir uygulamada gerçekten önemli olan iki endişe var.

  1. Try / catch bloklarının yuvalanması. Sizin için tek bir global değişken kullanmak jmp_bufbunların çalışmamasına neden olur.
  2. Diş açma. Sizin için tek bir global değişken jmp_buf, bu durumda her türlü acıya neden olacaktır.

Bunların çözümü, jmp_bufilerledikçe güncellenen bir iş parçacığı yerel yığınını korumaktır . (Sanırım lua'nın dahili olarak kullandığı şey budur).

Yani bunun yerine (JaredPar'ın harika cevabından)

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjump(s_jumpBuffer, 42);
}

Şunun gibi bir şey kullanırsınız:

#define MAX_EXCEPTION_DEPTH 10;
struct exception_state {
  jmp_buf s_jumpBuffer[MAX_EXCEPTION_DEPTH];
  int current_depth;
};

int try_point(struct exception_state * state) {
  if(current_depth==MAX_EXCEPTION_DEPTH) {
     abort();
  }
  int ok = setjmp(state->jumpBuffer[state->current_depth]);
  if(ok) {
    state->current_depth++;
  } else {
    //We've had an exception update the stack.
    state->current_depth--;
  }
  return ok;
}

void throw_exception(struct exception_state * state) {
  longjump(state->current_depth-1,1);
}

void catch_point(struct exception_state * state) {
    state->current_depth--;
}

void end_try_point(struct exception_state * state) {
    state->current_depth--;
}

__thread struct exception_state g_exception_state; 

void Example() { 
  if (try_point(&g_exception_state)) {
    catch_point(&g_exception_state);
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
    end_try_point(&g_exception_state);
  }
}

void Test() {
  // Rough equivalent of `throw`
  throw_exception(g_exception_state);
}

Yine bunun daha gerçekçi bir versiyonu, hata bilgisini exception_statedaha iyi işlemek için saklamanın bir yolunu içerir MAX_EXCEPTION_DEPTH(belki tamponu büyütmek için realloc kullanmak veya bunun gibi bir şey).

SORUMLULUK REDDİ: Yukarıdaki kod herhangi bir test yapılmadan yazılmıştır. Tamamen, bir şeyleri nasıl yapılandıracağınıza dair bir fikir edinmeniz içindir. Farklı sistemler ve farklı derleyicilerin iş parçacığı yerel depolamasını farklı şekilde uygulaması gerekecektir. Kod muhtemelen hem derleme hatalarını hem de mantık hatalarını içerir - bu nedenle, istediğiniz gibi kullanmakta özgür olsanız da, kullanmadan önce TEST EDİN;)


4

Hızlı bir hakkında arama verimleri gibi çözümler kludgey bu diğerleri de gibi kullanımının setjmp / longjmp. C ++ / Java'nın try / catch kadar basit ve zarif bir şey yoktur. Ada'nın istisnasının kendimi halletmesine oldukça yanım

Her şeyi if ifadeleriyle kontrol edin :)


4

Bu setjmp/longjmpC ile yapılabilir . P99 , bunun için oldukça rahat bir araç setine sahiptir ve bu da C11'in yeni diş modeliyle tutarlıdır.


2

Bu, setjmp / longjmp kullanmaktan daha performanslı olan C'de hata işlemeyi yapmanın başka bir yoludur. Ne yazık ki, MSVC ile çalışmayacak, ancak yalnızca GCC / Clang kullanmak bir seçenekse, o zaman bunu düşünebilirsiniz. Spesifik olarak, bir etiketin adresini almanıza, onu bir değerde saklamanıza ve koşulsuz olarak ona atlamanıza izin veren "değer olarak etiketle" uzantısını kullanır. Bunu bir örnek kullanarak sunacağım:

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    /* Declare an error handler variable. This will hold the address
       to jump to if an error occurs to cleanup pending resources.
       Initialize it to the err label which simply returns an
       error value (NULL in this example). The && operator resolves to
       the address of the label err */
    void *eh = &&err;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    if (!engine)
        goto *eh; /* this is essentially your "throw" */

    /* Now make sure that if we throw from this point on, the memory
       gets deallocated. As a convention you could name the label "undo_"
       followed by the operation to rollback. */
    eh = &&undo_malloc;

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    if (!engine->window)
        goto *eh;   /* The neat trick about using approach is that you don't
                       need to remember what "undo" label to go to in code.
                       Simply go to *eh. */

    eh = &&undo_window_open;

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

İsterseniz, kendi hata işleme sisteminizi etkin bir şekilde uygulayarak tanımlardaki ortak kodu yeniden düzenleyebilirsiniz.

/* Put at the beginning of a function that may fail. */
#define declthrows void *_eh = &&err

/* Cleans up resources and returns error result. */
#define throw goto *_eh

/* Sets a new undo checkpoint. */
#define undo(label) _eh = &&undo_##label

/* Throws if [condition] evaluates to false. */
#define check(condition) if (!(condition)) throw

/* Throws if [condition] evaluates to false. Then sets a new undo checkpoint. */
#define checkpoint(label, condition) { check(condition); undo(label); }

Sonra örnek olur

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    declthrows;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    checkpoint(malloc, engine);

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    checkpoint(window_open, engine->window);

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

2

Uyarı: Aşağıdaki çok hoş değil ama işi yapıyor.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    unsigned int  id;
    char         *name;
    char         *msg;
} error;

#define _printerr(e, s, ...) fprintf(stderr, "\033[1m\033[37m" "%s:%d: " "\033[1m\033[31m" e ":" "\033[1m\033[37m" " ‘%s_error’ " "\033[0m" s "\n", __FILE__, __LINE__, (*__err)->name, ##__VA_ARGS__)
#define printerr(s, ...) _printerr("error", s, ##__VA_ARGS__)
#define printuncaughterr() _printerr("uncaught error", "%s", (*__err)->msg)

#define _errordef(n, _id) \
error* new_##n##_error_msg(char* msg) { \
    error* self = malloc(sizeof(error)); \
    self->id = _id; \
    self->name = #n; \
    self->msg = msg; \
    return self; \
} \
error* new_##n##_error() { return new_##n##_error_msg(""); }

#define errordef(n) _errordef(n, __COUNTER__ +1)

#define try(try_block, err, err_name, catch_block) { \
    error * err_name = NULL; \
    error ** __err = & err_name; \
    void __try_fn() try_block \
    __try_fn(); \
    void __catch_fn() { \
        if (err_name == NULL) return; \
        unsigned int __##err_name##_id = new_##err##_error()->id; \
        if (__##err_name##_id != 0 && __##err_name##_id != err_name->id) \
            printuncaughterr(); \
        else if (__##err_name##_id != 0 || __##err_name##_id != err_name->id) \
            catch_block \
    } \
    __catch_fn(); \
}

#define throw(e) { *__err = e; return; }

_errordef(any, 0)

Kullanım:

errordef(my_err1)
errordef(my_err2)

try ({
    printf("Helloo\n");
    throw(new_my_err1_error_msg("hiiiii!"));
    printf("This will not be printed!\n");
}, /*catch*/ any, e, {
    printf("My lovely error: %s %s\n", e->name, e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err2_error_msg("my msg!"));
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printerr("%s", e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err1_error());
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printf("Catch %s if you can!\n", e->name);
})

Çıktı:

Helloo
My lovely error: my_err1 hiiiii!

Helloo
/home/naheel/Desktop/aa.c:28: error: my_err2_error my msg!

Helloo
/home/naheel/Desktop/aa.c:38: uncaught error: my_err1_error 

Bunun iç içe geçmiş işlevleri kullandığını ve __COUNTER__. Gcc kullanıyorsanız güvenli tarafta olacaksınız.


1

Redis, dene / yakala simülasyonu için goto'yu kullanıyor, IMHO çok temiz ve zarif:

/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
    char tmpfile[256];
    FILE *fp;
    rio rdb;
    int error = 0;

    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
    fp = fopen(tmpfile,"w");
    if (!fp) {
        redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
            strerror(errno));
        return REDIS_ERR;
    }

    rioInitWithFile(&rdb,fp);
    if (rdbSaveRio(&rdb,&error) == REDIS_ERR) {
        errno = error;
        goto werr;
    }

    /* Make sure data will not remain on the OS's output buffers */
    if (fflush(fp) == EOF) goto werr;
    if (fsync(fileno(fp)) == -1) goto werr;
    if (fclose(fp) == EOF) goto werr;

    /* Use RENAME to make sure the DB file is changed atomically only
     * if the generate DB file is ok. */
    if (rename(tmpfile,filename) == -1) {
        redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
        unlink(tmpfile);
        return REDIS_ERR;
    }
    redisLog(REDIS_NOTICE,"DB saved on disk");
    server.dirty = 0;
    server.lastsave = time(NULL);
    server.lastbgsave_status = REDIS_OK;
    return REDIS_OK;

werr:
    fclose(fp);
    unlink(tmpfile);
    redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
    return REDIS_ERR;
}

Kod bozuldu. errnoüç aramadan sonra değil, yalnızca başarısız sistem çağrısından hemen sonra kullanılmalıdır.
06'da yanıtlanıyor

Bu kod, hata işleme mantığını birden çok yerde çoğaltır ve fclose (fp) işlevini birden çok kez çağırmak gibi yanlış şeyler yapabilir. Birden çok etiket kullanmak ve bu etiketleri kullanarak (tüm hatalar için yalnızca bir tane yerine) hala geri kazanılması gerekenleri kodlamak ve ardından hatanın kodun neresinde oluştuğuna bağlı olarak doğru hata işleme noktasına atlamak çok daha iyi olacaktır.
jschultz410

1

C'de, açık hata işleme için if + goto'nun manuel kullanımı yoluyla otomatik "nesne düzeltme" ile birlikte istisnaları "simüle edebilirsiniz".

Genellikle aşağıdaki gibi C kodu yazarım (hata işlemeyi vurgulamak için kaynatılır):

#include <assert.h>

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    if ( ( ret = foo_init( f ) ) )
        goto FAIL;

    if ( ( ret = goo_init( g ) ) )
        goto FAIL_F;

    if ( ( ret = poo_init( p ) ) )
        goto FAIL_G;

    if ( ( ret = loo_init( l ) ) )
        goto FAIL_P;

    assert( 0 == ret );
    goto END;

    /* error handling and return */

    /* Note that we finalize in opposite order of initialization because we are unwinding a *STACK* of initialized objects */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

Bu tamamen standart ANSI C'dir, hata işlemeyi ana hat kodunuzdan ayırır, C ++ 'nın yaptığı gibi başlatılan nesnelerin (manuel olarak) yığın çözülmesine izin verir ve burada ne olduğu tamamen açıktır. Her noktada açıkça başarısızlık testi yaptığınız için, bir hatanın meydana gelebileceği her yere belirli bir günlük kaydı veya hata işlemeyi eklemeyi kolaylaştırır.

Biraz makro büyüsüne aldırış etmezseniz, yığın izleriyle hataları günlüğe kaydetmek gibi diğer şeyleri yaparken bunu daha kısa hale getirebilirsiniz. Örneğin:

#include <assert.h>
#include <stdio.h>
#include <string.h>

#define TRY( X, LABEL ) do { if ( ( X ) ) { fprintf( stderr, "%s:%d: Statement '" #X "' failed! %d, %s\n", __FILE__, __LINE__, ret, strerror( ret ) ); goto LABEL; } while ( 0 )

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    TRY( ret = foo_init( f ), FAIL );
    TRY( ret = goo_init( g ), FAIL_F );
    TRY( ret = poo_init( p ), FAIL_G );
    TRY( ret = loo_init( l ), FAIL_P );

    assert( 0 == ret );
    goto END;

    /* error handling and return */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

Elbette bu, C ++ istisnaları + yıkıcılar kadar zarif değil. Örneğin, birden çok hata işleme yığınını bir işlevin içine bu şekilde yerleştirmek çok temiz değildir. Bunun yerine, muhtemelen hataları benzer şekilde işleyen kendi içinde alt işlevlere ayırmak istersiniz, açıkça böyle başlat + sonlandırmak istersiniz.

Bu aynı zamanda yalnızca tek bir işlev içinde çalışır ve daha yüksek seviyeli arayanlar benzer açık hata işleme mantığını uygulamadıkça yığını atlamaya devam etmez, oysa bir C ++ istisnası uygun bir işleyici bulana kadar yığını atlamaya devam eder. Ayrıca rastgele bir tür atmanıza da izin vermez, bunun yerine yalnızca bir hata kodu verir.

Bu şekilde sistematik olarak kodlama (yani - tek bir giriş ve tek çıkış noktası ile), ne olursa olsun çalıştırılacak pre ve post ("nihayet") mantığını eklemeyi de çok kolaylaştırır. "Nihayet" mantığınızı END etiketinin arkasına koyarsınız.


1
Çok hoş. Benzer bir şey yapma eğilimindeyim. goto bu senaryo için harika. Tek fark, son "END'e git" ihtiyacını görmüyorum, sadece o noktada bir başarı dönüşü ekliyorum, geri kalanından sonra bir başarısız dönüş.
Neil Roy

1
Teşekkürler @NeilRoy END'e gitme nedenim, işlevlerimin büyük çoğunluğunun tek bir giriş noktasına ve tek bir çıkış noktasına sahip olmasını sevmemdir. Bu şekilde, herhangi bir işleve "nihayet" mantığını eklemek istersem, endişelenmeye gerek kalmadan her zaman kolayca yapabilirim, bir yerlerde gizlenen başka gizli geri dönüşler vardır. :)
jschultz410


-1

Belki ana dil değil (maalesef), ancak APL'de ⎕EA operasyonu var (Execute Alternate'in kısaltması).

Kullanım: 'Y' ⎕EA 'X' burada X ve Y dizeler veya işlev adları olarak sağlanan kod parçacıklarıdır.

X bir hatayla karşılaşırsa, bunun yerine Y (genellikle hata işleme) yürütülür.


2
Merhaba mappo, StackOverflow'a hoş geldiniz. İlginç olsa da, soru özellikle bunu C'de yapmakla ilgiliydi. Yani bu , soruyu
luser droog
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.