Jak zgłosić wyjątek w C?

102

Wpisałem to w Google, ale znalazłem tylko instrukcje w C ++,

jak to zrobić w C?

httpinterpret
źródło
6
C nie obsługuje obsługi wyjątków. Aby zgłosić wyjątek w C, musisz użyć czegoś specyficznego dla platformy, na przykład strukturalnej obsługi wyjątków Win32 - ale aby udzielić pomocy, musimy znać platformę, na której Ci zależy.
Jerry Coffin
18
... i nie używaj strukturalnej obsługi wyjątków Win32.
Brian R. Bondy
4
Używanie setjmp () i longjmp () powinno teoretycznie działać, ale nie sądzę, aby było warte zachodu.
Joseph Quinsey

Odpowiedzi:

78

Nie ma wyjątków w C. W C błędy są zgłaszane przez zwróconą wartość funkcji, wartość wyjścia procesu, sygnały do ​​procesu ( Sygnały Błędów Programu (GNU libc) ) lub przerwanie sprzętowe CPU (lub inne powiadomienie błąd z procesora, jeśli istnieje) ( Jak procesor obsługuje przypadek dzielenia przez zero ).

Jednak wyjątki są zdefiniowane w C ++ i innych językach. Obsługa wyjątków w C ++ jest określona w standardzie C ++ „S.15 Obsługa wyjątków”, w standardzie C nie ma odpowiedniej sekcji.

Brian R. Bondy
źródło
4
Więc w C jest gwarantowane, że nie będzie wyjątków, jak?
httpinterpret
62
@httpinterpret: w C jest „gwarantowane”, że nie ma wyjątków w ten sam sposób, w jaki „gwarantuje się”, że nie ma szablonów, ani odbić, ani jednorożców. Specyfikacja języka po prostu niczego takiego nie definiuje. Istnieje jednak setjmp / longjmp, którego można użyć do wyjścia z funkcji bez powrotu. Zatem programy mogą, jeśli chcą, budować własne mechanizmy podobne do wyjątków, lub implementacja w C może definiować rozszerzenia standardu.
Steve Jessop
2
Program ze mainw .cpliku może zawierać pewne C ++, a zatem wyjątki mogą być wyrzucane i złapany w programie, ale porcje kodu C pozostaną nieświadomi to wszystko dzieje się poza tym rzucaniu wyjątków i łapanie często opierają się na funkcji napisanych w C które znajdują się w bibliotekach C ++. C jest używane, ponieważ nie można ryzykować, że wywoływana funkcja throwmusi sama zgłosić wyjątek. Jednak może istnieć specyficzny dla kompilatora / biblioteki / celu sposób zgłaszania / przechwytywania wyjątków. Ale rzucenie instancji klasy będzie miało własne problemy.
nategoose
61
@Steve: Proszę, daj mi znać, jeśli znajdziesz język z jednorożcami, czekałem na coś takiego od lat.
Brian R. Bondy
5
@ BrianR.Bondy Oto język z jednorożcami (gwarantuję to tak samo jak w C)
Tofandel
38

W C możesz użyć kombinacji funkcji setjmp()i longjmp(), zdefiniowanych w setjmp.h. Przykład z Wikipedii

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

Uwaga: właściwie radziłbym ci ich nie używać, ponieważ działają okropnie z C ++ (destruktory lokalnych obiektów nie zostałyby wywołane) i naprawdę trudno jest zrozumieć, co się dzieje. Zamiast tego zwróć jakiś błąd.

vava
źródło
Widziałem, że setjump / longjump nie działa poprawnie w programach C ++, nawet jeśli żadne obiekty nie wymagały zniszczenia, gdy zostały użyte wyjątki. Rzadko używam ich w programach C.
nategoose
To było ciekawe podejście przy użyciu setjmp. on-time.com/ddj0011.htm Ale tak, w zasadzie musisz je sam wymyślić, jeśli chcesz wykonywać kod poza pasmem bez rozwijania stosu.
Dwayne Robinson
Nie jestem pewien, dlaczego zastrzeżenie dotyczące używania ich w C ++, gdy pytanie dotyczy C, a C ++ i tak ma wyjątki. W każdym razie ważne jest, aby OP wiedział, że aby uniemożliwić implementacji setjmp / longjmp odstrzelenie nogi, zawsze pamiętaj, że najpierw musisz odwiedzić program obsługi błędów (aby go skonfigurować) i ten moduł obsługi błędów musi wrócić dwa razy.
1
Nawiasem mówiąc, obsługa wyjątków była czymś, co naprawdę miałem nadzieję, że trafi do C11. Pomimo powszechnego przekonania, nie wymaga on OOP do prawidłowego działania, a C cierpi bez końca z powodu każdego wywołania funkcji wymagającego if(). Nie widzę w tym niebezpieczeństwa; czy może tu ktoś jeszcze?
@ user4229245 Mam (anegdotyczne) wrażenie, że wszystko, co „zrobiono dla ciebie”, jest trzymane poza specyfikacją języka c tak bardzo, jak to możliwe, szczególnie po to, aby c było odpowiednie dla programowania od podstaw i maszyn, gdzie nawet podstawowe status quo, takie jak ośmiobitowe bajty, nie są Nie uniwersalne, tylko moje myśli, nie jestem autorem specyfikacji c
ThorSummoner
21

Zwykły stary C tak naprawdę nie obsługuje wyjątków natywnie.

Możesz użyć alternatywnych strategii obsługi błędów, takich jak:

  • zwracanie kodu błędu
  • zwracanie FALSEi używanie last_errorzmiennej lub funkcji.

Zobacz http://en.wikibooks.org/wiki/C_Programming/Error_handling .

Lucas Jones
źródło
Również interfejs API systemu Windows ma tę samą metodę obsługi błędów. na przykład GetLastError()w Windows API.
BattleTested
20

W C nie ma wbudowanego mechanizmu wyjątków; musisz symulować wyjątki i ich semantykę. Zwykle osiąga się to, polegając na setjmpilongjmp .

W okolicy jest sporo bibliotek, a ja wdrażam kolejną. Nazywa się exceptions4c ; jest przenośny i bezpłatny. Możesz przyjrzeć się temu i porównać z innymi alternatywami, aby sprawdzić, która najbardziej Ci odpowiada.

Guillermo Calvo
źródło
Czytałem przykład kodu, zacząłem podobny projekt ze strukturą, ale używam uuid zamiast ciągu do identyfikacji każdego „wyjątku”. Twój projekt wydaje się bardzo obiecujący.
umlcat
Alternatywy link powinien teraz wskazywać github.com/guillermocalvo/exceptions4c/wiki/alternatives
Marcel Waldvogel
Czy wiesz (lub ktokolwiek inny) o porównaniu różnych pakietów wyjątków?
Marcel Waldvogel
11

C może zgłosić wyjątek C ++, są to i tak kody maszynowe. Na przykład w 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

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

aby go skompilować

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

wynik

good 1976

Więcej szczegółowych informacji można znaleźć w witrynie http://mentorembedded.github.io/cxx-abi/abi-eh.html__cxa_throw .

Nie jestem pewien, czy jest przenośny, czy nie, i testuję go z 'gcc-4.8.2' w systemie Linux.

wcy
źródło
2
Co to do cholery jest "_ZTIl" ??? Nie mogę nigdzie znaleźć żadnego odniesienia do niego i jest kompletną tajemnicą, w jaki sposób kod C mógłby mieć dostęp do std :: type_info. Proszę pomóż mi!
AreusAstarte
1
_ZTIIoznacza typeinfo for longnp echo '_ZTIl' | c++filt . Jest to schemat zniekształcenia g ++.
wcy
i na wszelki wypadek wywołaj symbol ac w bloku catch, aby uniknąć c ++ tak bardzo, jak to możliwe: P (PS dziękuję za udostępnienie doskonałego prawdziwego przykładu!)
ThorSummoner
8

To pytanie jest bardzo stare, ale po prostu się na nie natknąłem i pomyślałem, że podzielę się techniką: dzielenie przez zero lub odwoływanie się do pustego wskaźnika.

Pytanie brzmi po prostu „jak wyrzucić”, a nie jak wyłapać, czy nawet jak wyrzucić określony typ wyjątku. Wieki temu miałem sytuację, w której musieliśmy wyzwolić wyjątek z C, aby został przechwycony w C ++. W szczególności od czasu do czasu otrzymywaliśmy raporty o błędach „czystego wywołania funkcji wirtualnej” i musieliśmy przekonać funkcję _purecall środowiska wykonawczego języka C do rzucenia czegoś. Więc dodaliśmy naszą własną funkcję _purecall, która jest podzielona przez zero i bum, otrzymaliśmy wyjątek, który mogliśmy złapać w C ++, a nawet wykorzystać trochę stosu, aby zobaczyć, gdzie coś poszło nie tak.

Joe
źródło
@AreusAstarte na wypadek, gdyby komuś naprawdę trzeba było pokazać dzielenie C przez zero:int div_by_nil(int x) { return x/0; }
7

W Win z MSVC jest __try ... __except ...to naprawdę okropne i nie chcesz go używać, jeśli możesz tego uniknąć. Lepiej powiedzieć, że nie ma wyjątków.

Donal Fellows
źródło
1
W rzeczywistości wyjątki C ++ są budowane na szczycie SEH również pod maską.
Calmarius,
@Calmarius Co to jest SEH ?
Marcel Waldvogel
1
@MarcelWaldvogel Structured Exception Handling (sposób obsługi wyjątków procesora w systemie Windows).
Calmarius
6

C nie ma wyjątków.

Istnieją różne hackerskie implementacje, które próbują to zrobić (jeden przykład na: http://adomas.org/excc/ ).

Joe
źródło
3

Jak wspomniano w wielu wątkach, „standardowym” sposobem jest użycie setjmp / longjmp. Kolejne takie rozwiązanie zamieściłem na https://github.com/psevon/exceptions-and-raii-in-c Jest to według mojej wiedzy jedyne rozwiązanie, które polega na automatycznym czyszczeniu przydzielonych zasobów. Implementuje unikalne i współdzielone inteligentne wskaźniki i umożliwia pośredniczącym funkcjom przepuszczanie wyjątków bez przechwytywania i nadal prawidłowo oczyszczane lokalnie przydzielone zasoby.

user3510229
źródło
2

C nie obsługuje wyjątków. Możesz spróbować skompilować swój kod w C jako C ++ z Visual Studio lub G ++ i sprawdzić, czy skompiluje się tak, jak jest. Większość aplikacji C skompiluje się jako C ++ bez większych zmian, a następnie możesz użyć składni try ... catch.

Mahmoud Al-Qudsi
źródło
0

Jeśli piszesz kod z wzorcem projektowym Happy path (np. Dla urządzenia osadzonego), możesz zasymulować przetwarzanie błędów wyjątków (aka deffering lub wreszcie emulacja) za pomocą operatora „goto”.

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

W rzeczywistości nie obsługuje on obsługi wyjątków, ale umożliwia obsługę błędu przy wyjściu funkcji.

PS Powinieneś uważać, aby używać goto tylko z tych samych lub więcej głębokich zakresów i nigdy nie przeskakiwać deklaracji zmiennych.

Vitold S.
źródło