Try Catch in C

Muhammad Husnain 12 Oktober 2023
  1. Try-Catch in C
  2. Fügen Sie Finally zu Try-Catch in C hinzu
Try Catch in C

Try-Catch-Mechanismen sind in vielen Programmiersprachen wie Python, C++ und JavaScript üblich. Die allgemeine Struktur ist unten.

try {
  /*
  Insert some lines of code that will probably give you errors
  */
} catch {
  /*
  Write some code to handle the errors you're getting.
  */
}

Sie ermöglichen es Ihnen, Code zu schreiben, ohne jede Anweisung testen zu müssen. Wenn das im try-Block laufende Programm auf eine Ausnahme stößt, wird die Ausnahme an den catch-Block übergeben.

Wenn die Ausnahme mit einem Ausnahmetyp übereinstimmt, wird der Code innerhalb des catch-Blocks ausgeführt. Andernfalls wird die Ausnahme an den try-Block zurückgereicht.

Try-Catch in C

C unterstützt keine Ausnahmebehandlung. Zumindest hat es keinen eingebauten Mechanismus dafür.

Diese Anleitung zeigt eine mögliche Lösung, um die try-catch-Funktionalität in C bereitzustellen. Es sollte beachtet werden, dass die Lösung nicht notwendigerweise vollständig ist.

Ausnahmebehandlungssysteme sind nicht vollständig und sicher ohne einen Mechanismus, um Speicher freizugeben, wenn der Stack durchlaufen wurde, und C hat keinen Garbage Collector. Wir müssten wahrscheinlich auch Kontextmanager einbeziehen, um Speicher freizugeben.

Diese Lösung beabsichtigt nicht, einen vollständigen und umfassenden try-catch-Mechanismus bereitzustellen. Dieses Konzept kann technisch verwendet werden, um einige wenige Ausnahmen zu behandeln.

Wir werden die Lösung inkrementell erstellen und den Code aktualisieren. Wir werden zwei von C bereitgestellte Funktionen verwenden, longjmp und setjmp, die aus der Header-Datei setjmp.h abgerufen werden können.

Wir werden uns die Definitionen beider Funktionen genauer ansehen.

int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

setjmp akzeptiert eine Variable vom Typ jmp_buf. Wenn diese Funktion direkt aufgerufen wird, gibt sie 0 zurück.

longjmp nimmt zwei Variablen, und wenn longjmp mit derselben jmp_buf-Variablen aufgerufen wird, gibt die setjmp-Funktion denselben Wert zurück wie das zweite Argument von longjmp (val).

Die Variable env ist hier im Wesentlichen die Aufrufumgebung, die den Zustand der Register und die Position im Code darstellt, wenn der Funktionsaufruf erfolgt. Beim Aufruf von longjmp wird der Zustand in der aufrufenden Umgebung auf den Prozessor kopiert und der im val-Argument von longjmp gespeicherte Wert zurückgegeben.

Für einen einfachen Try-Catch-Block besteht die Idee darin, die Try-Anweisung auf die if-Anweisung abzubilden, und die Catch-Anweisung ist dann das else für die Bedingung. Hier können wir uns intelligent zunutze machen, dass setjmp unterschiedliche Werte zurückliefern kann.

Wenn die Funktion 0 zurückgibt, wissen wir, dass das einzige Stück Code, das ausgeführt wurde, der Code in unserem TRY-Block war. Wenn die Funktion etwas anderes zurückgibt, müssen wir mit demselben Zustand wie zu Beginn in unseren CATCH-Block gehen.

Wir können die Funktion longjmp aufrufen, wenn wir eine Ausnahme THROW.

Wie Sie im folgenden Code sehen werden, müssen wir auch den TRY-Block schließen. Wir erstellen eine ENDTRY-Funktion, die den schließenden Teil des do-while-Blocks bereitstellt.

Dies hilft uns auch dabei, mehrere TRY-Anweisungen innerhalb desselben Blocks zu erstellen. Es sollte beachtet werden, dass sie nicht verschachtelt werden können, da wir die Variable buf_state wiederverwenden werden.

Ein Beispiel für diese Implementierung ist unten.

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

#define TRY            \
  do {                 \
    jmp_buf buf_state; \
    if (!setjmp(buf_state)) {
#define CATCH \
  }           \
  else {
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW longjmp(buf_state, 1)

int main() {
  TRY {
    printf("Testing Try statement \n");
    THROW;
    printf(
        "Statement should not appear, as the THROW block has already thrown "
        "the exception \n");
  }
  CATCH { printf("Got Exception \n"); }
  ENDTRY;

  return 0;
}

Ausgabe:

Testing Try statement
Got Exception

Für praktische Systeme reicht dies nicht aus. Wir brauchen verschiedene Arten von Ausnahmen.

Das obige Beispiel unterstützt nur einen Ausnahmetyp. Auch hier können wir die unterschiedlichen Rückgabewerte von setjmp verwenden.

Anstatt if-else zu verwenden, werden wir dies mit einem switch-case umschalten.

Das Design ist wie folgt: Die TRY-Anweisung wird eine switch-Anweisung verwenden, und CATCH wird ein Makro mit einem Parameter sein, der den Ausnahmetyp darstellt. Eine Bedingung jeder CATCH-Anweisung wird sein, dass sie den vorherigen case mit einem break abschliessen muss.

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

#define TRY                      \
  do {                           \
    jmp_buf buf_state;           \
    switch (setjmp(buf_state)) { \
      case 0:
#define CATCH(x) \
  break;         \
  case x:
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW(x) longjmp(buf_state, x)

#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)

int main() {
  TRY {
    printf("Inside Try statement \n");
    THROW(EXCEPTION2);
    printf("This does not appear as exception has already been called \n");
  }
  CATCH(EXCEPTION1) { printf("Exception 1 called \n"); }
  CATCH(EXCEPTION2) { printf("Exception 2 called \n"); }
  CATCH(EXCEPTION3) { printf("Exception 3 called \n"); }
  ENDTRY;

  return 0;
}

Ausgabe:

Inside Try statement
Exception 2 called

Fügen Sie Finally zu Try-Catch in C hinzu

Wir müssen einen FINALLY-Block für eine vollständige funktionale Try-Catch-Implementierung hinzufügen. Der finally-Block wird im Allgemeinen ausgeführt, nachdem die try- und catch-Blöcke fertig sind.

Es wird unabhängig davon ausgeführt, ob eine Ausnahme ausgelöst wird oder nicht.

Wie würden wir das umsetzen? Die Kernidee besteht darin, den default-Fall des switch-Falls zu verwenden, um den FINALLY-Block zu implementieren.

Allerdings würde der switch-case den default-Case nicht ausführen, wenn im Normalfall eine Exception aufgerufen wurde.

Wir werden einen ähnlichen Mechanismus wie Duffs Gerät verwenden. Wir werden im Wesentlichen eine switch-case-Anweisung mit einer do-while-Anweisung verflechten.

Die Logik dafür ist ungefähr so.

switch (an expression) {
  case 0:
    while (1) {
      // code for case 0
      break;
      case 1:
        // code for case 1
        break;
    }
  default:
    // code for default case
}

Wir verwenden eines der umstrittensten Features von C: Schalter, die vor jedem Case-Label nicht automatisch unterbrochen werden. Hier wird die while-Anweisung in den switch-case verschachtelt, wenn ein break aufgerufen wird; es verlässt die while-Schleife und fährt fort, die Fälle zu durchlaufen.

Im Kontext unseres Codes (wie unten gezeigt) sind die switch-Fälle alle unsere Ausnahmen, und der default-Fall befindet sich in unserem FINALLY-Block. Natürlich wird es auf den Fall Standard fallen, da der Ausnahmecode bereits ausgeführt worden wäre.

Dies wird im folgenden Code gezeigt.

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

#define TRY                      \
  do {                           \
    jmp_buf buf_state;           \
    switch (setjmp(buf_state)) { \
      case 0:                    \
        while (1) {
#define CATCH(x) \
  break;         \
  case x:
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW(x) longjmp(buf_state, x)
#define FINALLY \
  break;        \
  }             \
  default:
#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)

int main() {
  TRY {
    printf("Inside Try statement \n");
    THROW(EXCEPTION2);
    printf("This does not appear as exception has already been called \n");
  }
  CATCH(EXCEPTION1) { printf("Exception 1 called \n"); }
  CATCH(EXCEPTION2) { printf("Exception 2 called \n"); }
  CATCH(EXCEPTION3) { printf("Exception 3 called \n"); }
  FINALLY { printf("This will always be called! \n"); }
  ENDTRY;

  return 0;
}

Ausgabe:

Inside Try statement
Exception 2 called
This will always be called!

Damit ist die Anleitung zum Erstellen eines try-catch-Systems in C abgeschlossen. Natürlich gibt es hier mögliche Speicherprobleme und einige Einschränkungen (z. B. fehlende Unterstützung für verschachtelte try-catch-Systeme), aber dies ist a funktionale try-catch-Implementierung in C.

Muhammad Husnain avatar Muhammad Husnain avatar

Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.

LinkedIn

Verwandter Artikel - C Exception