Wie wird eine Ausnahme in C ausgelöst?


97

Ich habe dies in Google eingegeben, aber nur in C ++ Howtos gefunden.

wie geht das in C?


6
C unterstützt keine Ausnahmebehandlung. Um eine Ausnahme in C auszulösen, müssen Sie etwas Plattformspezifisches wie die strukturierte Ausnahmebehandlung von Win32 verwenden. Um jedoch dabei zu helfen, müssen wir die Plattform kennen, die Sie interessiert.
Jerry Coffin

17
... und verwenden Sie keine strukturierte Win32-Ausnahmebehandlung.
Brian R. Bondy

3
Die Verwendung von setjmp () und longjmp () sollte theoretisch funktionieren, aber ich denke nicht, dass sich die Mühe lohnt.
Joseph Quinsey

Antworten:


75

In C gibt es keine Ausnahmen. In C werden die Fehler durch den zurückgegebenen Wert der Funktion, den Exit-Wert des Prozesses, Signale an den Prozess ( Program Error Signals (GNU libc) ) oder die Unterbrechung der CPU-Hardware (oder eine andere Benachrichtigung) benachrichtigt Fehler von der CPU, falls vorhanden) ( Wie der Prozessor den Fall der Division durch Null behandelt ).

Ausnahmen sind jedoch in C ++ und anderen Sprachen definiert. Die Ausnahmebehandlung in C ++ ist im C ++ - Standard "S.15 Ausnahmebehandlung" angegeben, es gibt keinen entsprechenden Abschnitt im C-Standard.


4
In C gibt es also garantiert keine Ausnahmen, wie?
httpinterpret

61
@httpinterpret: In C ist "garantiert", dass es keine Ausnahmen gibt, genauso wie "garantiert", dass es keine Vorlagen, keine Reflexion oder keine Einhörner gibt. Die Sprachspezifikation definiert so etwas einfach nicht. Es gibt jedoch setjmp / longjmp, mit dem eine Funktion beendet werden kann, ohne zurückzukehren. So könnten Programme ihre eigenen ausnahmeähnlichen Mechanismen erstellen, wenn sie dies wünschen, oder eine C-Implementierung könnte Erweiterungen des Standards definieren.
Steve Jessop

2
Ein Programm mit mainin einer .cDatei kann etwas C ++ enthalten, und daher können Ausnahmen im Programm ausgelöst und abgefangen werden, aber die C-Code-Teile wissen nichts davon, außer dass das Auslösen und Abfangen von Ausnahmen häufig von in C geschriebenen Funktionen abhängt die sich in den C ++ - Bibliotheken befinden. C wird verwendet, weil Sie nicht riskieren können, dass die aufgerufene Funktion throwselbst eine Ausnahme auslösen muss. Es kann jedoch einen Compiler / eine Bibliothek / ein Ziel geben, mit dem Ausnahmen ausgelöst / abgefangen werden können. Das Werfen einer Klasseninstanz hat jedoch eigene Probleme.
Strategie

61
@Steve: Bitte lassen Sie mich wissen, wenn Sie eine Sprache mit Einhörnern finden, ich habe jahrelang auf so etwas gewartet.
Brian R. Bondy

5
@ BrianR.Bondy Hier ist eine Sprache mit Einhörnern (ich garantiere es genauso wie es in C garantiert ist)
Tofandel

35

In C konnte man die Kombination der Verwendung setjmp()und longjmp()Funktionen, definiert in setjmp.h. Beispiel aus Wikipedia

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

static jmp_buf buf;

void second(void) {
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp 
                                //   was called - making setjmp now return 1
}

void first(void) {
    second();
    printf("first\n");          // does not print
}

int main() {   
    if ( ! setjmp(buf) ) {
        first();                // when executed, setjmp returns 0
    } else {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}

Hinweis: Ich würde Ihnen tatsächlich raten, sie nicht zu verwenden, da sie mit C ++ schrecklich funktionieren (Destruktoren lokaler Objekte würden nicht aufgerufen) und es wirklich schwer zu verstehen ist, was los ist. Geben Sie stattdessen einen Fehler zurück.


Ich habe gesehen, dass setjump / longjump in C ++ - Programmen nicht richtig funktioniert, selbst wenn keine Objekte zerstört wurden, wenn Ausnahmen verwendet wurden. Ich benutze sie selten in C-Programmen.
Strategie

Dies war ein interessanter Ansatz mit setjmp. on-time.com/ddj0011.htm Aber ja, im Grunde müssen Sie sie selbst erfinden, wenn Sie Out-of-Band-Code ausführen möchten, ohne den Stapel abzuwickeln.
Dwayne Robinson

Ich bin mir nicht sicher, warum die Einschränkung, sie in C ++ zu verwenden, wenn es um C geht, und C ++ sowieso Ausnahmen hat. In jedem Fall ist es wichtig , dass die OP weiß , dass in der Reihenfolge von der setjmp / longjmp Implementierung zu halten Ihr Bein schießen, immer daran denken , dass Sie zunächst notwendig , die Fehlerbehandlung (bis es einrichten), und dass Fehlerbehandlung zu besuchen muss zweimal zurückkehren.

1
Die Ausnahmebehandlung war übrigens etwas, von dem ich wirklich gehofft hatte, dass es in C11 gelandet wäre. Trotz allgemeiner Überzeugung ist OOP nicht erforderlich, um ordnungsgemäß zu funktionieren, und C leidet endlos unter jedem Funktionsaufruf, der eine erfordert if(). Ich kann keine Gefahr darin sehen; kann noch jemand hier?

@ user4229245 Ich habe den (anekdotischen) Eindruck, dass alles, was "für Sie getan" wurde, so weit wie möglich aus der c-Sprachspezifikation herausgehalten wird, um c für die Bare-Metal- und Maschinenprogrammierung relevant zu halten, wo selbst Status-Quo-Grundlagen wie 8-Bit-Bytes nicht vorhanden sind Nicht universell, nur meine Gedanken, ich bin kein C-Spec-Autor
ThorSummoner


20

In C gibt es keinen integrierten Ausnahmemechanismus. Sie müssen Ausnahmen und deren Semantik simulieren . Dies wird normalerweise erreicht, indem man sich auf setjmpund verlässt longjmp.

Es gibt einige Bibliotheken, und ich implementiere noch eine. Es heißt Ausnahmen4c ; Es ist tragbar und kostenlos. Sie können es sich ansehen und es mit anderen Alternativen vergleichen, um herauszufinden , welche am besten zu Ihnen passt.


Ich habe Ihr Codebeispiel gelesen und ein ähnliches Projekt mit einer Struktur gestartet, aber eine UUID anstelle einer Zeichenfolge verwendet, um jede "Ausnahme" zu identifizieren. Ihr Projekt scheint sehr vielversprechend.
Umlcat


Kennen Sie (oder jemand anderes) einen Vergleich zwischen den verschiedenen Ausnahmepaketen?
Marcel Waldvogel

11

C kann eine C ++ - Ausnahme auslösen, es handelt sich sowieso um Maschinencodes. Zum Beispiel in bar.c.

// begin bar.c
#include <stdlib.h>
#include <stdint.h>
extern void *__cxa_allocate_exception(size_t thrown_size);
extern void __cxa_throw (void *thrown_exception, void* *tinfo, void (*dest) (void *) );
extern void * _ZTIl; // typeinfo of long
int bar1()
{
   int64_t * p = (int64_t*)__cxa_allocate_exception(8);
   *p = 1976;
   __cxa_throw(p,&_ZTIl,0);
  return 10;
}
// end bar.c

in a.cc,

#include <stdint.h>
#include <cstdio>
extern "C" int bar1();
void foo()
{
  try{
    bar1();
  }catch(int64_t x){
    printf("good %ld",x);
  }
}
int main(int argc, char *argv[])
{
  foo();
  return 0;
}

um es zu kompilieren

gcc -o bar.o -c bar.c && g++ a.cc bar.o && ./a.out

Ausgabe

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html enthält weitere Informationen zu __cxa_throw.

Ich bin nicht sicher, ob es portabel ist oder nicht, und ich teste es mit 'gcc-4.8.2' unter Linux.


2
Was zum Teufel ist "_ZTIl" ??? Ich kann nirgendwo einen Verweis darauf finden und es ist ein völliges Rätsel, wie es C-Code ermöglichen würde, auf std :: type_info zuzugreifen. Bitte hilf mir!
AreusAstarte

1
_ZTIIbedeutet typeinfo for longz echo '_ZTIl' | c++filt . Es ist das g ++ Mangling-Schema.
wcy

und nur zum guten Teil sollten Sie das AC-Symbol im Catch-Block aufrufen, um c ++ so weit wie möglich zu vermeiden: P (PS, danke, dass Sie ein hervorragendes Beispiel gegeben haben!)
ThorSummoner

8

Diese Frage ist super alt, aber ich bin nur darauf gestoßen und dachte, ich würde eine Technik teilen: durch Null teilen oder einen Nullzeiger dereferenzieren.

Die Frage ist einfach "wie man wirft", nicht wie man fängt oder sogar wie man eine bestimmte Art von Ausnahme wirft. Ich hatte vor Ewigkeiten eine Situation, in der wir eine Ausnahme von C auslösen mussten, um in C ++ abgefangen zu werden. Insbesondere hatten wir gelegentlich Berichte über "reine virtuelle Funktionsaufruf" -Fehler und mussten die _purecall-Funktion der C-Laufzeit davon überzeugen, etwas auszulösen. Also haben wir unsere eigene _purecall-Funktion hinzugefügt, die durch Null geteilt wird, und boom. Wir haben eine Ausnahme bekommen, die wir in C ++ abfangen und sogar etwas Stapelspaß verwenden können, um zu sehen, wo etwas schief gelaufen ist.


@AreusAstarte nur für den Fall, dass jemandem wirklich eine C-Division durch Null gezeigt werden muss:int div_by_nil(int x) { return x/0; }

7

Bei Win mit MSVC gibt es __try ... __except ...aber es ist wirklich schrecklich und Sie möchten es nicht verwenden, wenn Sie es möglicherweise vermeiden können. Besser zu sagen, dass es keine Ausnahmen gibt.


1
Tatsächlich werden C ++ - Ausnahmen auch auf der Oberseite des SEH unter der Haube erstellt.
Calmarius

@ Calmarius Was ist SEH ?
Marcel Waldvogel

1
@MarcelWaldvogel Strukturierte Ausnahmebehandlung (Windows-Methode zur Behandlung von CPU-Ausnahmen).
Calmarius


3

Wie in zahlreichen Threads erwähnt, ist die "Standard" -Methode dazu die Verwendung von setjmp / longjmp. Ich habe eine weitere solche Lösung unter https://github.com/psevon/exceptions-and-raii-in-c veröffentlicht. Dies ist meines Wissens die einzige Lösung, die auf der automatischen Bereinigung der zugewiesenen Ressourcen beruht. Es implementiert eindeutige und gemeinsam genutzte Smartpointers und ermöglicht Zwischenfunktionen, Ausnahmen durchzulassen, ohne sie abzufangen, und ihre lokal zugewiesenen Ressourcen weiterhin ordnungsgemäß zu bereinigen.


2

C unterstützt keine Ausnahmen. Sie können versuchen, Ihren C-Code mit Visual Studio oder G ++ als C ++ zu kompilieren und prüfen, ob er unverändert kompiliert wird. Die meisten C-Anwendungen werden ohne größere Änderungen als C ++ kompiliert, und Sie können dann die Syntax try ... catch verwenden.


0

Wenn Sie Code mit dem Happy Path-Entwurfsmuster (dh für eingebettete Geräte) schreiben, können Sie die Verarbeitung von Ausnahmefehlern (auch bekannt als Deffering oder endgültige Emulation) mit dem Operator "goto" simulieren.

int process(int port)
{
    int rc;
    int fd1;
    int fd2;

    fd1 = open("/dev/...", ...);
    if (fd1 == -1) {
      rc = -1;
      goto out;
    }

    fd2 = open("/dev/...", ...);
    if (fd2 == -1) {
      rc = -1;
      goto out;
    }

    // Do some with fd1 and fd2 for example write(f2, read(fd1))

    rc = 0;

   out:

    //if (rc != 0) {
        (void)close(fd1);
        (void)close(fd2);
    //}

    return rc;
}

Es ist eigentlich kein Ausnahmebehandler, aber es bietet Ihnen eine Möglichkeit, Fehler beim Beenden der Funktion zu behandeln.

PS Sie sollten vorsichtig sein, wenn Sie nur aus denselben oder mehreren tieferen Bereichen gehen und niemals die Variablendeklaration überspringen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.