ANSI C-Äquivalent von try/catch?

Lesezeit: 10 Minuten

Benutzer-Avatar
Helfer

Ich habe etwas C-Code, mit dem ich arbeite, und ich finde Fehler, wenn der Code ausgeführt wird, aber ich habe wenig Informationen darüber, wie man einen richtigen Try/Catch durchführt (wie in C# oder C++).

Zum Beispiel in C++ würde ich einfach tun:

try{
//some stuff
}
catch(...)
{
//handle error
}

aber in ANSI C bin ich ein bisschen verloren. Ich habe einige Online-Suchen ausprobiert, aber ich sehe nicht genügend Informationen darüber, wie ich es verwirklichen kann / dachte, ich würde hier fragen, falls mich jemand in die richtige Richtung weisen kann.

Hier ist der Code, mit dem ich arbeite (ziemlich einfache, rekursive Methode) und den ich gerne mit try/catch (oder einer gleichwertigen Fehlerbehandlungsstruktur) umschließen möchte.

Meine Hauptfrage ist jedoch einfach, wie man in ANSI C versucht / fängt … die Implementierung / das Beispiel muss nicht rekursiv sein.

void getInfo( int offset, myfile::MyItem * item )
{
    ll::String myOtherInfo = item->getOtherInfo();
    if( myOtherInfo.isNull() )
        myOtherInfo = "";
    ll::String getOne = "";
    myfile::Abc * abc = item->getOrig();
    if( abc != NULL )
    {
        getOne = abc->getOne();
    }
    for( int i = 0 ; i < offset ; i++ )
    {
             printf("found: %d", i);
    }
    if( abc != NULL )
        abc->release();
    int childCount = item->getChildCount();
    offset++;
    for( int i = 0 ; i < childCount ; i++ )
        getInfo( offset, item->getChild(i) );
    item->release();
}

  • nicemice.net/cexcept etwas, das nützlich sein könnte

    – anijhaw

    21. September 2010 um 16:59 Uhr

  • Dieser Code ist nicht C, Ansi oder etwas anderes. C hat das nicht :: Scope-Operator.

    – Steve Jessop

    21. September 2010 um 16:59 Uhr

  • C hat keinen Ausnahmebehandlungsmechanismus. Die gesamte Fehlerbehandlung erfolgt normalerweise mit Rückgabewerten und der errnum-Variablen. Übrigens, es wäre gut, einige detaillierte Expertenkommentare darüber zu erhalten, wie die Fehlerbehandlung in C richtig durchgeführt wird 🙂

    – Kel

    21. September 2010 um 17:02 Uhr


  • @Steve: Guter Punkt. Meine Antwort war speziell für C, nicht für irgendeine Form von C++, egal wie C-ähnlich.

    – David Thornley

    21. September 2010 um 17:10 Uhr

  • Die einzige mir bekannte Sprache, die den von @aiden geposteten Code kompilieren würde, ist C++. Und wenn es C++ ist, können Sie einfach verwenden try/catch. Wenn Sie tatsächlich ANSI-C-Code schreiben müssen, dann sieht es so aus, als müssten Sie Ihren Code tatsächlich umschreiben sein gültiges C zusätzlich zu den in den Antworten gemachten Vorschlägen.

    – jalf

    21. September 2010 um 19:05 Uhr

Benutzer-Avatar
Steve Jessop

Im Allgemeinen nicht.

Es ist möglich zu verwenden setjmp und longjmp etwas zu bauen, das try/catch ziemlich ähnlich ist, obwohl es in C keine Destruktoren oder Stack-Unwinding gibt, also kommt RAII nicht in Frage. Sie könnten RAII sogar mit einem sogenannten “Cleanup-Stack” annähern (siehe zum Beispiel Symbian/C++), obwohl dies keine sehr genaue Annäherung ist und eine Menge Arbeit bedeutet.

Der übliche Weg, Fehler oder Fehler in C anzuzeigen, besteht darin, einen Wert zurückzugeben, der den Erfolgsstatus angibt. Aufrufer untersuchen den Rückgabewert und handeln entsprechend. Siehe zum Beispiel die Standard-C-Funktionen: printf, read, openfür Ideen, wie Sie Ihre Funktionen spezifizieren können.

Beim Mischen von C- und C++-Code müssen Sie sicherstellen, dass eine C++-Ausnahme niemals C-Code erreicht. Fangen Sie beim Schreiben von C++-Funktionen, die von C aufgerufen werden, alles ab.

  • IME Der übliche Weg, Fehler oder Fehler in C anzuzeigen, besteht darin, einen Wert zurückzugeben, der vom Aufrufer ignoriert wird. :(

    – sbi

    21. September 2010 um 18:22 Uhr

  • @sbi: IME, einige Leute glauben immer noch, dass die “Rückgabe eines Werts” auch in C ++, Java und C # der richtige Weg ist … :(

    – Paercebal

    22. September 2010 um 11:32 Uhr

Benutzer-Avatar
Steve Townsend

C unterstützt keine Ausnahmebehandlung.

Es gibt Informationen zu einem Ansatz für dieses Problem hier. Das zeigt das Einfache setjmp/longjmp Ansatz, sondern bietet auch eine anspruchsvollere Alternative, die ausführlich behandelt wird.

  • Hmm, dritte Ablehnung heute. Irgendein besonderer Grund dafür? Ich dachte, das sah vielversprechend aus.

    – Steve Townsend

    21. September 2010 um 17:09 Uhr


  • +1, weil die Ablehnung falsch war … perfekter Link für die Frage

    – Hogan

    21. September 2010 um 17:12 Uhr

  • @ John Dibling – das versteht sich von selbst … Werde die Augen offen halten, vielleicht nur ein schlechter Tag.

    – Steve Townsend

    21. September 2010 um 17:15 Uhr

Es gibt die klassische Abwicklung gotos Muster:

FILE *if = fopen(...);
FILE *of = NULL;
if (if == NULL) return; 

of = fopen(...);
if (of == NULL) goto close_if;

/* ...code... */
if (something_is_wrong) goto close_of;

/* ... other code... */

close_of:
  fclose(of);
close_if:
  fclose(if);

return state;

Alternativ können Sie es auf begrenzte Weise fälschen, indem Sie den “try” -Code in einer anderen Funktion isolieren

int try_code(type *var_we_must_write, othertype var_we_only_read /*, ... */){
  /* ...code... */
  if (!some_condition) return 1;
  /* ...code... */
  if (!another_condition) return 2;
  /* ...code... */
  if (last_way_to_fail) return 4;
  return 0;
}

void calling_routine(){
  /* ... */
  if (try_code(&x,y/*, other state */) ) {
     /* do your finally here */
  }
 /* ... */
}

aber keiner der Ansätze ist völlig gleichwertig. Sie müssen alle Ressourcen selbst verwalten, Sie erhalten kein automatisches Rollback, bis ein Handler gefunden wird, und so weiter …

  • Eek! Das sieht für mich echt chaotisch aus. Ich bevorzuge meine Methode, die ich unten vorgeschlagen habe. Keine Notwendigkeit für goto!

    – James

    21. September 2010 um 19:30 Uhr

  • goto ist der richtige Weg (entschuldigen Sie das Wortspiel) in C. Mehrere Exit-Labels zu haben, scheint jedoch übertrieben zu sein. Es ist sauberer (wenn auch etwas weniger effizient), ein einzelnes Exit-Label zu haben und (falls erforderlich) zu überprüfen, welche Ressourcen zugewiesen wurden.

    – jamesdlin

    21. September 2010 um 19:41 Uhr


  • @jamesdlin: Die mehreren Labels kaufen dir etwas Bestimmtes: Jedes entspricht einer Ressource, die möglicherweise freigegeben werden muss. Sie können sie mit Bedingungen für die Freigaben vermeiden, aber in einigen Fällen (nicht in diesem Fall) sind zusätzliche Flags erforderlich. Geschmackssache soweit ich das beurteilen kann.

    – dmckee — Ex-Moderator-Kätzchen

    21. September 2010 um 19:50 Uhr


  • Ja, aber es kostet etwas zusätzliche Komplexität, und meiner Meinung nach ist es etwas schwieriger zu warten (weil die Reihenfolge wichtig ist). In den meisten Fällen sollten zusätzliche Flags (und zusätzliche Prüfungen) ohnehin nicht erforderlich sein; Aufräumfunktionen (nicht fclose) sollten im Allgemeinen no-ops sein, wenn sie auf Dummy-Werten aufgerufen werden (zB NULL).

    – jamesdlin

    21. September 2010 um 20:41 Uhr

  • @jamesdlin: Wenn ich diese Art der Bereinigung durchgeführt habe (im Allgemeinen eher in Assembler als in C), fand ich die Goto-Version sauberer und einfacher, wenn die potenziellen Ressourcen immer in derselben Reihenfolge zugewiesen wurden. Sie können leicht wissen, welche gotoweil Sie an jedem beliebigen Punkt im Code verstehen, welche Ressourcen Sie haben (hoffe ich).

    – Steve Jessop

    22. September 2010 um 15:58 Uhr


Ein nützlicher Codierungsstil, den ich gerne verwende, ist der folgende. Ich weiß nicht, ob es einen bestimmten Namen hat, aber ich bin darauf gestoßen, als ich einen Assembler-Code in den entsprechenden C-Code zurückentwickelte. Sie verlieren zwar eine Einrückungsebene, aber für mich ist das keine große Sache. Achten Sie auf den Rezensenten, der auf die Endlosschleife hinweist! 🙂

int SomeFunction() {
    int err = SUCCESS;

    do {
        err = DoSomethingThatMayFail();
        if (err != SUCCESS) {
            printf("DoSomethingThatMayFail() failed with %d", err);
            break;
        }

        err = DoSomethingElse();
        if (err != SUCCESS) {
            printf("DoSomethingElse() failed with %d", err);
            break;
        }

        // ... call as many functions as needed.

        // If execution gets there everything succeeded!
        return SUCCESS;
    while (false);

    // Something went wrong!
    // Close handles or free memory that may have been allocated successfully.

    return err;
}

Dies ist meine Implementierung eines Ausnahmebehandlungssystems in C: Ausnahmen4c.

Es wird von Makros angetrieben, die darauf aufbauen setjmp und longjmp und es ist 100% portabel ANSI C.

Dort Sie können auch eine Liste aller mir bekannten Implementierungen finden.

Eine mögliche Implementierung mit longjmp finden Sie in diesem Buch: C Schnittstellen und Implementierungen: Techniken zum Erstellen wiederverwendbarer Software – David Hanson

Und Sie können den Code finden hier und hier Als Beispiel für den im Buch verwendeten Stil:

außer.h

/* $Id$ */
#ifndef EXCEPT_INCLUDED
#define EXCEPT_INCLUDED
#include <setjmp.h>
#define T Except_T
typedef struct T {
    const char *reason;
} T;
typedef struct Except_Frame Except_Frame;
struct Except_Frame {
    Except_Frame *prev;
    jmp_buf env;
    const char *file;
    int line;
    const T *exception;
};
enum { Except_entered=0, Except_raised,
       Except_handled,   Except_finalized };
extern Except_Frame *Except_stack;
extern const Except_T Assert_Failed;
void Except_raise(const T *e, const char *file,int line);
#ifdef WIN32
#include <windows.h>

extern int Except_index;
extern void Except_init(void);
extern void Except_push(Except_Frame *fp);
extern void Except_pop(void);
#endif
#ifdef WIN32
/* $Id$ */
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
#define RERAISE Except_raise(Except_frame.exception, \
    Except_frame.file, Except_frame.line)
#define RETURN switch (Except_pop(),0) default: return
#define TRY do { \
    volatile int Except_flag; \
    Except_Frame Except_frame; \
    if (Except_index == -1) \
        Except_init(); \
    Except_push(&Except_frame);  \
    Except_flag = setjmp(Except_frame.env); \
    if (Except_flag == Except_entered) {
#define EXCEPT(e) \
        if (Except_flag == Except_entered) Except_pop(); \
    } else if (Except_frame.exception == &(e)) { \
        Except_flag = Except_handled;
#define ELSE \
        if (Except_flag == Except_entered) Except_pop(); \
    } else { \
        Except_flag = Except_handled;
#define FINALLY \
        if (Except_flag == Except_entered) Except_pop(); \
    } { \
        if (Except_flag == Except_entered) \
            Except_flag = Except_finalized;
#define END_TRY \
        if (Except_flag == Except_entered) Except_pop(); \
        } if (Except_flag == Except_raised) RERAISE; \
} while (0)
#else
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
#define RERAISE Except_raise(Except_frame.exception, \
    Except_frame.file, Except_frame.line)
#define RETURN switch (Except_stack = Except_stack->prev,0) default: return
#define TRY do { \
    volatile int Except_flag; \
    Except_Frame Except_frame; \
    Except_frame.prev = Except_stack; \
    Except_stack = &Except_frame;  \
    Except_flag = setjmp(Except_frame.env); \
    if (Except_flag == Except_entered) {
#define EXCEPT(e) \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
    } else if (Except_frame.exception == &(e)) { \
        Except_flag = Except_handled;
#define ELSE \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
    } else { \
        Except_flag = Except_handled;
#define FINALLY \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
    } { \
        if (Except_flag == Except_entered) \
            Except_flag = Except_finalized;
#define END_TRY \
        if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
        } if (Except_flag == Except_raised) RERAISE; \
} while (0)
#endif
#undef T
#endif

außer.c

static char rcsid[] = "$Id$" "\n$Id$";
#include <stdlib.h>
#include <stdio.h>
#include "assert.h"
#include "except.h"
#define T Except_T
Except_Frame *Except_stack = NULL;
void Except_raise(const T *e, const char *file,
    int line) {
#ifdef WIN32
    Except_Frame *p;

    if (Except_index == -1)
        Except_init();
    p = TlsGetValue(Except_index);
#else
    Except_Frame *p = Except_stack;
#endif
    assert(e);
    if (p == NULL) {
        fprintf(stderr, "Uncaught exception");
        if (e->reason)
            fprintf(stderr, " %s", e->reason);
        else
            fprintf(stderr, " at 0x%p", e);
        if (file && line > 0)
            fprintf(stderr, " raised at %s:%d\n", file, line);
        fprintf(stderr, "aborting...\n");
        fflush(stderr);
        abort();
    }
    p->exception = e;
    p->file = file;
    p->line = line;
#ifdef WIN32
    Except_pop();
#else
    Except_stack = Except_stack->prev;
#endif
    longjmp(p->env, Except_raised);
}
#ifdef WIN32
_CRTIMP void __cdecl _assert(void *, void *, unsigned);
#undef assert
#define assert(e) ((e) || (_assert(#e, __FILE__, __LINE__), 0))

int Except_index = -1;
void Except_init(void) {
    BOOL cond;

    Except_index = TlsAlloc();
    assert(Except_index != TLS_OUT_OF_INDEXES);
    cond = TlsSetValue(Except_index, NULL);
    assert(cond == TRUE);
}

void Except_push(Except_Frame *fp) {
    BOOL cond;

    fp->prev = TlsGetValue(Except_index);
    cond = TlsSetValue(Except_index, fp);
    assert(cond == TRUE);
}

void Except_pop(void) {
    BOOL cond;
    Except_Frame *tos = TlsGetValue(Except_index);

    cond = TlsSetValue(Except_index, tos->prev);
    assert(cond == TRUE);
}
#endif

Benutzer-Avatar
David Thornley

Wenn Sie einen Sprung über mehrere Ebenen machen möchten, schauen Sie nach oben setjmp() und longjmp(). Sie können als primitiver Ausnahmewurf verwendet werden. Das setjmp() Die Funktion richtet eine Rückkehr zum Ort ein und gibt einen Statuswert zurück. Das longjmp() Die Funktion geht zum Return-to-Ort und liefert den Statuswert. Sie können eine erstellen catch Funktion, indem sie danach aufgerufen wird setjmp() abhängig vom Statuswert.

Verwenden Sie sie aus welchem ​​Grund auch immer nicht in C++. Sie führen kein Stack-Unwinding durch und rufen keine Destruktoren auf.

1353080cookie-checkANSI C-Äquivalent von try/catch?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy