Come lanciare un'eccezione in C?


102

L'ho digitato in google ma ho trovato solo howtos in C ++,

come si fa in C?


6
C non supporta la gestione delle eccezioni. Per lanciare un'eccezione in C, è necessario utilizzare qualcosa di specifico della piattaforma come la gestione strutturata delle eccezioni di Win32, ma per dare un aiuto in tal senso, avremo bisogno di conoscere la piattaforma che ti interessa.
Jerry Coffin

18
... e non utilizzare la gestione delle eccezioni strutturata Win32.
Brian R. Bondy,

4
L'uso di setjmp () e longjmp () dovrebbe, in teoria, funzionare, ma non penso che valga la pena.
Joseph Quinsey,

Risposte:


78

Non ci sono eccezioni in C. In C gli errori vengono notificati dal valore restituito dalla funzione, dal valore di uscita del processo, dai segnali al processo ( Segnali di errore del programma (GNU libc) ) o dall'interruzione hardware della CPU (o altra notifica errore dalla CPU, se presente) ( come il processore gestisce il caso di divisione per zero ).

Le eccezioni sono tuttavia definite in C ++ e in altri linguaggi. La gestione delle eccezioni in C ++ è specificata nello standard C ++ "Gestione delle eccezioni S.15", non esiste una sezione equivalente nello standard C.


4
Quindi in C è garantito che non ci saranno eccezioni, come?
httpinterpretare

62
@httpinterpret: in C è "garantito" che non ci sono eccezioni nello stesso modo in cui è "garantito" che non ci sono modelli, o riflessione, o unicorni. La specifica del linguaggio semplicemente non definisce nulla di simile. C'è setjmp / longjmp, però, che può essere usato per uscire da una funzione senza tornare indietro. Quindi i programmi potrebbero costruire i propri meccanismi simili alle eccezioni, se lo desiderano, oppure un'implementazione C potrebbe definire estensioni allo standard.
Steve Jessop,

2
Un programma con mainin un .cfile può includere un po 'di C ++, e quindi le eccezioni potrebbero essere lanciate e catturate nel programma, ma le porzioni di codice C rimarranno all'oscuro di tutto ciò che accade tranne che il lancio e la cattura di eccezioni spesso si basano su funzioni scritte in C che risiedono nelle librerie C ++. C viene utilizzato perché non puoi rischiare che la funzione chiamata throwdebba lanciare un'eccezione stessa. Tuttavia, potrebbe esserci un modo specifico per compilatore / libreria / destinazione per generare / rilevare eccezioni. Ma lanciare un'istanza di classe avrà i suoi problemi.
nategoose

61
@ Steve: Per favore fammi sapere se trovi una lingua con gli unicorni, ho aspettato una cosa del genere per anni.
Brian R. Bondy

5
@ BrianR.Bondy Ecco una lingua con gli unicorni (lo garantisco allo stesso modo in cui è garantito in C)
Tofandel

38

In C puoi usare la combinazione delle funzioni setjmp()e longjmp(), definita in setjmp.h. Esempio da 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;
}

Nota: in realtà ti consiglierei di non usarli poiché funzionano male con C ++ (i distruttori di oggetti locali non verrebbero chiamati) ed è davvero difficile capire cosa sta succedendo. Restituisci invece una sorta di errore.


Ho visto setjump / longjump non funzionare correttamente nei programmi C ++ anche quando non sono stati utilizzati oggetti che necessitavano di distruzione quando sono state utilizzate eccezioni. Li uso raramente nei programmi C.
nategoose

Questo è stato un approccio interessante usando setjmp. on-time.com/ddj0011.htm Ma sì, fondamentalmente devi inventarli tu stesso se vuoi l'esecuzione di codice fuori banda senza srotolare lo stack.
Dwayne Robinson

Non sono sicuro del motivo per cui l'avvertenza di usarli in C ++ quando la domanda riguarda C e C ++ ha comunque delle eccezioni. In ogni caso, è importante che l'OP sappia che per evitare che l'implementazione di setjmp / longjmp ti spari, tieni sempre presente che devi prima visitare il gestore degli errori (per configurarlo) e quel gestore degli errori deve tornare due volte.

1
A proposito, la gestione delle eccezioni era qualcosa che speravo davvero sarebbe finita in C11. Nonostante la credenza comune, non richiede OOP per funzionare correttamente e C soffre all'infinito per ogni chiamata di funzione che richiede un file if(). Non ci vedo pericolo; qualcun altro qui?

@ user4229245 Sono sotto l'impressione (aneddotica) che qualsiasi cosa "fatto per te" sia tenuto fuori dalle specifiche del linguaggio c il più possibile specificamente per mantenere c rilevante per la programmazione bare metal e della macchina dove anche i fondamenti dello status quo come i byte a otto bit non lo sono t universale, solo i miei pensieri però, non sono un autore di specifiche c
ThorSummoner

21

Il vecchio C normale non supporta effettivamente le eccezioni in modo nativo.

È possibile utilizzare strategie di gestione degli errori alternative, come:

  • restituendo un codice di errore
  • restituendo FALSEe utilizzando una last_errorvariabile o una funzione.

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


Anche Windows API ha lo stesso metodo per gestire gli errori. per esempio GetLastError()nell'API di Windows.
BattleTested il

20

Non esiste un meccanismo di eccezione integrato in C; è necessario simulare le eccezioni e la loro semantica. Questo di solito si ottiene facendo affidamento su setjmpe longjmp.

Ci sono parecchie librerie in giro e ne sto implementando un'altra ancora. Si chiama exceptions4c ; è portatile e gratuito. Puoi dare un'occhiata e confrontarlo con altre alternative per vedere quale si adatta di più.


Ho letto un esempio di codice, ho avviato un progetto simile con una struttura, ma utilizza un uuid invece di una stringa per identificare ogni "eccezione". Il tuo progetto sembra molto promettente.
umlcat

Il collegamento delle alternative ora dovrebbe puntare a github.com/guillermocalvo/exceptions4c/wiki/alternatives
Marcel Waldvogel

Sapete (o qualcun altro) di un confronto tra i diversi pacchetti di eccezioni?
Marcel Waldvogel

11

C è in grado di generare eccezioni C ++, sono comunque codici macchina. Ad esempio, 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;
}

per compilarlo

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

produzione

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html contiene informazioni più dettagliate su__cxa_throw .

Non sono sicuro che sia portabile o meno e lo provo con "gcc-4.8.2" su Linux.


2
Che diavolo è "_ZTIl" ??? Non riesco a trovare alcun riferimento ad esso da nessuna parte ed è un mistero completo come renderebbe possibile per il codice C avere accesso a std :: type_info. Mi aiuti per favore!
AreusAstarte

1
_ZTIIsignifica typeinfo for long, ad es echo '_ZTIl' | c++filt . È lo schema di mutilazione g ++.
wcy

e solo per buona misura assicurati di chiamare il simbolo ac nel blocco catch per evitare il più possibile c ++: P (PS grazie per aver condiviso un eccellente esempio reale!)
ThorSummoner

8

Questa domanda è super vecchia, ma mi sono imbattuto in essa e ho pensato di condividere una tecnica: dividere per zero o dereferenziare un puntatore nullo.

La domanda è semplicemente "come lanciare", non come catturare, o anche come lanciare un tipo specifico di eccezione. Anni fa ho avuto una situazione in cui dovevamo attivare un'eccezione da C per essere catturati in C ++. In particolare, abbiamo avuto rapporti occasionali di errori di "chiamata di funzione virtuale pura" e dovevamo convincere la funzione _purecall del runtime C a lanciare qualcosa. Quindi abbiamo aggiunto la nostra funzione _purecall divisa per zero e boom, abbiamo ottenuto un'eccezione che potevamo catturare in C ++ e persino usare un po 'di stack divertente per vedere dove le cose sono andate storte.


@AreusAstarte nel caso in cui qualcuno abbia davvero bisogno di mostrare una divisione C per zero: int div_by_nil(int x) { return x/0; }

7

Su Win con MSVC c'è __try ... __except ...ma è davvero orribile e non vuoi usarlo se puoi evitarlo. Meglio dire che non ci sono eccezioni.


1
In realtà le eccezioni C ++ sono costruite sulla parte superiore di SEH anche sotto il cofano.
Calmarius

@Calmarius Cos'è SEH ?
Marcel Waldvogel il

1
@MarcelWaldvogel Structured Exception Handling (il modo in cui Windows gestisce le eccezioni della CPU).
Calmarius il


3

Come menzionato in numerosi thread, il modo "standard" di farlo è usare setjmp / longjmp. Ho pubblicato un'altra soluzione simile su https://github.com/psevon/exceptions-and-raii-in-c Questa è, per quanto ne so, l'unica soluzione che si basa sulla pulizia automatica delle risorse allocate. Implementa smartpointer unici e condivisi e consente alle funzioni intermedie di far passare le eccezioni senza intercettarle e di ripulire adeguatamente le risorse allocate localmente.


2

C non supporta le eccezioni. Puoi provare a compilare il tuo codice C come C ++ con Visual Studio o G ++ e vedere se verrà compilato così com'è. La maggior parte delle applicazioni C verrà compilata come C ++ senza grandi modifiche, quindi è possibile utilizzare la sintassi try ... catch.


0

Se scrivi codice con Happy path design pattern (cioè per dispositivi embedded) puoi simulare l'elaborazione degli errori di eccezione (aka deffering o finalmente emulazione) con l'operatore "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;
}

In realtà non gestisce le eccezioni, ma ti offre un modo per gestire l'errore all'uscita dalla funzione.

PS Dovresti fare attenzione a usare goto solo dagli stessi o più ambiti profondi e non saltare mai la dichiarazione delle variabili.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.