Comment lancer une exception en C?

100

J'ai tapé ceci dans google mais je n'ai trouvé que des howtos en C ++,

comment le faire en C?

httpinterpret
la source
6
C ne prend pas en charge la gestion des exceptions. Pour lancer une exception en C, vous devez utiliser quelque chose de spécifique à la plate-forme, comme la gestion structurée des exceptions de Win32 - mais pour vous aider, nous aurons besoin de connaître la plate-forme qui vous intéresse.
Jerry Coffin
18
... et n'utilisez pas la gestion des exceptions structurée Win32.
Brian R. Bondy
4
Utiliser setjmp () et longjmp () devrait, en théorie, fonctionner, mais je ne pense pas que cela en vaille la peine.
Joseph Quinsey

Réponses:

77

Il n'y a pas d'exceptions en C.En C, les erreurs sont notifiées par la valeur renvoyée de la fonction, la valeur de sortie du processus, les signaux au processus ( Signaux d'erreur de programme (GNU libc) ) ou l'interruption matérielle du processeur (ou autre notification erreur de la CPU s'il y en a) ( Comment le processeur gère le cas de division par zéro ).

Les exceptions sont définies en C ++ et dans d'autres langages. La gestion des exceptions en C ++ est spécifiée dans la norme C ++ "S.15 Exception handling", il n'y a pas de section équivalente dans la norme C.

Brian R. Bondy
la source
4
Donc en C, c'est garanti qu'il n'y aura pas d'exceptions, comment?
httpinterpret
62
@httpinterpret: en C, c'est "garanti" qu'il n'y a pas d'exceptions de la même manière qu'il est "garanti" qu'il n'y a pas de modèles, ou pas de réflexion, ou pas de licornes. La spécification du langage ne définit tout simplement rien de tel. Il existe cependant setjmp / longjmp, qui peut être utilisé pour quitter une fonction sans retourner. Ainsi, les programmes peuvent construire leurs propres mécanismes de type exception s'ils le souhaitent, ou une implémentation en C peut définir des extensions au standard.
Steve Jessop
2
Un programme avec maindans un .cfichier peut inclure du C ++, et donc des exceptions peuvent être levées et interceptées dans le programme, mais les parties de code C resteront ignorantes de tout cela, sauf que le lancement et la capture d'exceptions reposent souvent sur des fonctions écrites en C qui résident dans les bibliothèques C ++. C est utilisé car vous ne pouvez pas risquer que la fonction appelée do throwdoive lever une exception elle-même. Il peut cependant y avoir un moyen spécifique au compilateur / bibliothèque / cible de lancer / intercepter des exceptions. Mais lancer une instance de classe aura ses propres problèmes.
nategoose
61
@Steve: Faites-moi savoir si vous trouvez une langue avec des licornes, j'attends une telle chose depuis des années.
Brian R. Bondy
5
@ BrianR.Bondy Voici un langage avec des licornes (je le garantis de la même manière qu'il est garanti en C)
Tofandel
37

En C, vous pouvez utiliser la combinaison des fonctions setjmp()et longjmp(), définies dans setjmp.h. Exemple de 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;
}

Remarque: je vous conseillerais en fait de ne pas les utiliser car ils fonctionnent mal avec C ++ (les destructeurs d'objets locaux ne seraient pas appelés) et il est vraiment difficile de comprendre ce qui se passe. Renvoyez plutôt une sorte d'erreur.

vava
la source
J'ai vu setjump / longjump ne pas fonctionner correctement dans les programmes C ++ même si aucun objet nécessitant une destruction lorsque des exceptions étaient utilisées. Je les utilise rarement dans les programmes C.
nategoose
C'était une approche intéressante utilisant setjmp. on-time.com/ddj0011.htm Mais oui, fondamentalement, vous devez les inventer vous-même si vous voulez une exécution de code hors bande sans dérouler la pile.
Dwayne Robinson
Je ne sais pas pourquoi la mise en garde concernant leur utilisation en C ++ lorsque la question concerne C et C ++ a de toute façon des exceptions. Dans tous les cas, il est important que l'OP sache que pour empêcher l'implémentation setjmp / longjmp de vous tirer dessus, gardez toujours à l'esprit que vous devez d' abord visiter le gestionnaire d'erreurs (pour le configurer), et ce gestionnaire d'erreurs doit revenir deux fois.
1
Au fait, la gestion des exceptions était quelque chose que j'espérais vraiment qu'il se retrouverait dans C11. Malgré la croyance commune, il ne nécessite pas de POO pour fonctionner correctement, et C souffre indéfiniment de chaque appel de fonction nécessitant un if(). Je n'y vois pas de danger; quelqu'un d'autre peut-il ici?
@ user4229245 J'ai l'impression (anecdotique) que tout ce qui est "fait pour vous" est autant que possible exclu des spécifications du langage c, spécifiquement pour garder c pertinent pour le bare metal et la programmation machine où même les fondamentaux du statu quo comme huit octets sont n 't universel, juste mes pensées tho, je ne suis pas un auteur de c spec
ThorSummoner
21

L'ancien C ordinaire ne prend pas en charge les exceptions de manière native.

Vous pouvez utiliser d'autres stratégies de gestion des erreurs, telles que:

  • renvoyer un code d'erreur
  • renvoyer FALSEet utiliser une last_errorvariable ou une fonction.

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

Lucas Jones
la source
Windows api a également la même méthode pour gérer les erreurs. par exemple GetLastError()dans l'API Windows.
BattleTested
20

Il n'y a pas de mécanisme d'exception intégré en C; vous devez simuler les exceptions et leur sémantique. Ceci est généralement réalisé en s'appuyant sur setjmpet longjmp.

Il y a pas mal de bibliothèques autour, et j'en implémente encore une autre. Cela s'appelle exceptions4c ; c'est portable et gratuit. Vous pouvez l'examiner et le comparer à d' autres alternatives pour voir celle qui vous convient le mieux.

Guillermo Calvo
la source
Je vous ai lu un exemple de code, j'ai commencé un projet similaire avec une structure, mais utilise un uuid au lieu d'une chaîne pour identifier chaque "exception". Votre projet semble très prometteur.
umlcat
Le lien des alternatives devrait maintenant pointer vers github.com/guillermocalvo/exceptions4c/wiki/alternatives
Marcel Waldvogel le
Connaissez-vous (ou quelqu'un d'autre) une comparaison entre les différents packages d'exceptions?
Marcel Waldvogel le
11

C est capable de lancer une exception C ++, ce sont de toute façon des codes machine. Par exemple, dans 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

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

pour le compiler

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

production

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html a plus d'informations sur__cxa_throw .

Je ne sais pas s'il est portable ou non, et je le teste avec 'gcc-4.8.2' sous Linux.

wcy
la source
2
Qu'est-ce que c'est que "_ZTIl" ??? Je ne trouve aucune référence à cela nulle part et c'est un mystère complet comment cela permettrait au code C d'avoir accès à std :: type_info. Aidez-moi, s'il vous plaît!
AreusAstarte
1
_ZTIIsignifie typeinfo for long, par exemple echo '_ZTIl' | c++filt . C'est le schéma de mutilation g ++.
wcy le
et juste pour faire bonne mesure, assurez-vous d'appeler le symbole ac dans le bloc catch pour éviter autant que possible le c ++: P (PS merci d'avoir partagé un excellent exemple réel!)
ThorSummoner
8

Cette question est très ancienne, mais je suis juste tombée dessus et j'ai pensé partager une technique: diviser par zéro ou déréférencer un pointeur nul.

La question est simplement «comment lancer», pas comment attraper, ni même comment lancer un type spécifique d'exception. J'ai eu une situation il y a longtemps où nous devions déclencher une exception de C pour être interceptée dans C ++. Plus précisément, nous avons eu des rapports occasionnels d'erreurs d '"appel de fonction virtuelle pure", et avons dû convaincre la fonction _purecall du runtime C de lancer quelque chose. Nous avons donc ajouté notre propre fonction _purecall qui divisée par zéro et boom, nous avons obtenu une exception que nous pourrions attraper sur C ++, et même utiliser une pile amusante pour voir où les choses ont mal tourné.

Joe
la source
@AreusAstarte juste au cas où quelqu'un aurait vraiment besoin de voir une division C par zéro:int div_by_nil(int x) { return x/0; }
7

Sur Win avec MSVC, il y a __try ... __except ...mais c'est vraiment horrible et vous ne voulez pas l'utiliser si vous pouvez l'éviter. Mieux vaut dire qu'il n'y a pas d'exceptions.

Boursiers Donal
la source
1
En fait, les exceptions C ++ sont également construites au sommet du SEH sous le capot.
Calmarius
@Calmarius Qu'est-ce que SEH ?
Marcel Waldvogel le
1
@MarcelWaldvogel Structured Exception Handling (la manière de Windows de gérer les exceptions CPU).
Calmarius le
6

C n'a pas d'exceptions.

Il existe diverses implémentations hacky qui tentent de le faire (un exemple sur: http://adomas.org/excc/ ).

Joe
la source
3

Comme mentionné dans de nombreux threads, la manière "standard" de faire cela est d'utiliser setjmp / longjmp. J'ai posté une autre solution de ce type sur https://github.com/psevon/exceptions-and-raii-in-c C'est à ma connaissance la seule solution qui repose sur le nettoyage automatique des ressources allouées. Il implémente des pointeurs intelligents uniques et partagés, et permet aux fonctions intermédiaires de laisser passer les exceptions sans intercepter tout en ayant leurs ressources allouées localement correctement nettoyées.

user3510229
la source
2

C ne prend pas en charge les exceptions. Vous pouvez essayer de compiler votre code C en C ++ avec Visual Studio ou G ++ et voir s'il se compilera tel quel. La plupart des applications C se compileront en C ++ sans modifications majeures, et vous pouvez ensuite utiliser la syntaxe try ... catch.

Mahmoud Al-Qudsi
la source
0

Si vous écrivez du code avec le modèle de conception Happy path (c'est-à-dire pour un périphérique intégré), vous pouvez simuler le traitement des erreurs d'exception (alias deffering ou enfin émulation) avec l'opérateur "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;
}

Il ne fait pas réellement de gestionnaire d'exceptions, mais il vous permet de gérer les erreurs à la sortie de la fonction.

PS Vous devez faire attention à n'utiliser goto qu'à partir de portées identiques ou plus profondes et ne jamais sauter de déclaration de variable.

Vitold S.
la source