En tant que bon programmeur, vous devez écrire des codes robustes capables de gérer chaque résultat de son programme. Cependant, presque toutes les fonctions de la bibliothèque C renverront 0 ou -1 ou NULL en cas d'erreur.
Il est parfois évident qu'une vérification d'erreur est nécessaire, par exemple lorsque vous essayez d'ouvrir un fichier. Mais j'ignore souvent la vérification des erreurs dans des fonctions telles que printf
ou même malloc
parce que je ne me sens pas nécessaire.
if(fprintf(stderr, "%s", errMsg) < 0){
perror("An error occurred while displaying the previous error.");
exit(1);
}
Est-ce une bonne pratique de simplement ignorer certaines erreurs ou existe-t-il un meilleur moyen de gérer toutes les erreurs?
c
error-handling
Derek 會 功夫
la source
la source
try
instruction. Vous n'avez donc pas à vérifier chaque appel ou opération. (Notez également que certaines langues sont plus aptes que d'autres à détecter des erreurs simples telles que la déréférence null ou l'index de tableau hors limites.)errno
! Au cas où vous ne le sauriez pas, s'il est vrai que "presque toutes les fonctions de la bibliothèque C renverront 0 ou -1 ouNULL
en cas d'erreur", elles définissent également laerrno
variable globale , à laquelle vous pouvez accéder en#include <errno.h>
lisant simplement la valeur deerrno
. Ainsi, par exemple, siopen
(2) renvoie-1
, vous souhaiterez peut-être vérifier sierrno == EACCES
, ce qui indiquerait une erreur d'autorisation ouENOENT
si le fichier demandé n'existe pas.try
/catch
, bien que vous puissiez le simuler avec des sauts.Réponses:
En général, le code devrait traiter des conditions exceptionnelles chaque fois que cela est approprié. Oui, ceci est une déclaration vague.
Dans les langages de niveau supérieur avec gestion des exceptions logicielles, cela est souvent indiqué comme "attrapez l'exception dans la méthode où vous pouvez réellement faire quelque chose à ce sujet". Si une erreur de fichier se produit, vous pouvez peut-être laisser la pile s'afficher dans le code de l'interface utilisateur qui peut en réalité indiquer à l'utilisateur "que votre fichier n'a pas pu être sauvegardé sur le disque". Le mécanisme d'exception engloutit efficacement "chaque petite erreur" et la gère implicitement à l'endroit approprié.
En C, vous n'avez pas ce luxe. Il existe plusieurs façons de gérer les erreurs, dont certaines sont des fonctionnalités de langage / bibliothèque, d'autres des pratiques de codage.
Ignorer certaines erreurs? Peut être. Par exemple, il est raisonnable de supposer que l'écriture sur la sortie standard n'échouera pas. En cas d'échec, comment diriez-vous à l'utilisateur, de toute façon? Oui, il est judicieux d’ignorer certaines erreurs ou de coder de manière défensive pour les éviter. Par exemple, recherchez zéro avant de diviser.
Il existe des moyens de gérer toutes les erreurs, ou du moins la plupart d'entre elles:
En cascade
if
s:Testez la première condition, par exemple en ouvrant ou en créant un fichier, puis supposez que les opérations suivantes aboutissent.
Le code robuste est bon, et il faut vérifier et gérer les erreurs. La méthode la mieux adaptée à votre code dépend de son rôle, de la gravité de la défaillance, etc., et vous seul pouvez réellement y répondre. Cependant, ces méthodes sont testées au combat et utilisées dans divers projets open source où vous pouvez jeter un coup d'œil à la façon dont le code réel vérifie les erreurs.
la source
stdout
peut être redirigé vers un fichier, ou même ne pas exister selon le système.stdout
n’est pas un flux "écrire sur la console", c’est généralement cela, mais ce n’est pas nécessairement le cas.stdout
... évident :-)dump_database > backups/db_dump.txt
ne parvient pas à écrire sur la sortie standard à un moment donné, je ne voudrais pas qu'elle continue et se termine avec succès. (Non pas que les bases de données soient sauvegardées de cette façon, mais le problème est toujours valable)Oui, mais vous savez quelle fonction vous avez appelée, n'est-ce pas?
En fait, vous avez beaucoup d'informations que vous pourriez mettre dans un message d'erreur. Vous savez quelle fonction a été appelée, le nom de la fonction qui l'a appelée, quels paramètres ont été transmis et la valeur de retour de la fonction. C'est beaucoup d'informations pour un message d'erreur très informatif.
Vous n'êtes pas obligé de faire cela pour chaque appel de fonction. Mais la première fois que vous voyez le message d'erreur "Une erreur s'est produite lors de l'affichage de l'erreur précédente", alors que vous aviez réellement besoin d'informations utiles, ce sera la dernière fois que vous verrez ce message d'erreur car vous allez immédiatement changer le message d'erreur à quelque chose d'informatif qui vous aidera à résoudre le problème.
la source
if (!myfunc()) {/*proceed*/}
. 0 n'était pas une erreur, et tout ce qui n'est pas zéro est une sorte d'erreur et chaque erreur a son propre code différent de zéro. La façon dont un de mes amis l’a exprimé était «il n’ya qu’une vérité, mais beaucoup de mensonges». "0" doit être "vrai" dans leif()
test et tout élément non nul doit être "faux".if(function()) { // do something }
se lit comme "si la fonction s'exécute sans erreur, alors fait quelque chose." ou encore mieux, "si la fonction s'exécute avec succès, faites quelque chose". Sérieusement, ceux qui comprennent la convention ne sont pas déconcertés. Retournerfalse
(ou 0) lorsqu'une erreur se produit ne permettrait pas l'utilisation de codes d'erreur. Zéro signifie faux en C parce que c'est comme ça que les mathématiques fonctionnent. Voir programmers.stackexchange.com/q/198284TLDR; vous ne devriez presque jamais ignorer les erreurs.
Le langage C ne dispose pas d’une bonne fonctionnalité de gestion des erreurs, laissant à chaque développeur de bibliothèque l’implémentation de ses propres solutions. Des langages plus modernes ont des exceptions intégrées qui rendent ce problème particulier beaucoup plus facile à gérer.
Mais quand vous êtes coincé avec C, vous n'avez aucun de ces avantages. Malheureusement, vous devrez simplement payer le prix chaque fois que vous appelez une fonction pour laquelle il existe un risque faible d'échec. Sinon, vous subirez des conséquences bien pires, telles que l’écrasement involontaire de données en mémoire. Donc, en règle générale, vous devez toujours rechercher les erreurs.
Si vous ne vérifiez pas le retour de
fprintf
votre ordinateur, il est très probable que vous laissiez un bogue qui, dans le meilleur des cas, ne ferait pas ce que l'utilisateur attend et dans le pire des cas, explose tout en vol. Il n'y a aucune excuse pour vous saper de cette façon.Cependant, en tant que développeur C, vous devez également faciliter la maintenance du code. Il est donc parfois possible d'ignorer des erreurs par souci de clarté si (et seulement si) elles ne constituent pas une menace pour le comportement général de l'application. .
C'est le même problème que cela:
Si vous voyez cela sans aucun bon commentaire dans le bloc de capture vide, c'est certainement un problème. Il n'y a aucune raison de penser qu'un appel à
malloc()
s'exécutera avec succès 100% du temps.la source
malloc()
est une fonction sur laquelle vous voulez absolument vérifier les valeurs de retour!La question n'est pas réellement spécifique à la langue, mais plutôt à l'utilisateur. Pensez à la question du point de vue de l'utilisateur. L'utilisateur fait quelque chose, comme taper le nom du programme sur une ligne de commande et appuyer sur Entrée. Qu'attend l'utilisateur? Comment peuvent-ils savoir si quelque chose s'est mal passé? Peuvent-ils se permettre d'intercéder en cas d'erreur?
Dans de nombreux types de code, ces vérifications sont excessives. Cependant, dans les codes critiques de sécurité à haute fiabilité, tels que ceux des réacteurs nucléaires, la vérification des erreurs pathologiques et les chemins de récupération planifiés font partie de la nature quotidienne du travail. Prendre le temps de demander "Que se passe-t-il si X échoue? Comment puis-je revenir à un état sûr?" Dans un code moins fiable, comme celui des jeux vidéo, vous pouvez vous en tirer avec beaucoup moins de vérification d'erreur.
Une autre approche similaire consiste à déterminer dans quelle mesure vous pouvez réellement améliorer l’état en détectant l’erreur. Je ne peux pas compter le nombre de programmes C ++ qui capturent fièrement des exceptions, uniquement pour les redistribuer, car ils ne savaient pas vraiment quoi en faire ... mais ils savaient qu'ils étaient censés gérer les exceptions. De tels programmes ne tiraient aucun profit de l'effort supplémentaire. N'ajoutez que le code de vérification d'erreur qui, selon vous, pourrait mieux gérer la situation que la simple vérification du code d'erreur. Cela étant dit, C ++ a des règles spécifiques pour gérer les exceptions qui se produisent lors de la gestion des exceptions afin de les intercepter de manière plus significative (et par là, j'entends appeler terminate () pour m'assurer que le bûcher funéraire que vous avez construit vous-même s'allume. dans sa propre gloire)
Comment pathologique pouvez-vous être? J'ai travaillé sur un programme qui définit un "SafetyBool" dont les valeurs vraies et fausses ont été choisies avec soin pour avoir une distribution uniforme de 1 et de 0, et qui ont été choisies de manière à ce que l'une des nombreuses défaillances matérielles (basculement d'un bit, bus de données) traces brisées, etc.) n’a pas incité un booléen à être mal interprété. Inutile de dire que je ne prétends pas qu'il s'agisse d'une pratique de programmation à usage général pouvant être utilisée dans un programme ancien.
la source
la source
Un peu abstrait résume la question. Et ce n'est pas nécessairement pour le langage C.
Pour les programmes plus importants, vous auriez une couche d'abstraction; peut-être une partie du moteur, une bibliothèque ou dans un cadre. Cette couche ne se soucient de temps , vous obtenez des données valides ou la sortie serait une valeur par défaut:
0
,-1
,null
etc.Ensuite, il y a une couche qui serait votre interface avec la couche abstraite, qui ferait beaucoup de traitement des erreurs et peut-être d'autres choses comme des injections de dépendances, l'écoute d'événements, etc.
Et plus tard, vous aurez votre couche d’implémentation concrète où vous définissez les règles et gérez la sortie.
Selon moi, il est parfois préférable d'exclure complètement le traitement des erreurs d'une partie du code, car cette partie ne fait tout simplement pas ce travail. Et puis avoir un processeur qui évaluerait la sortie et indiquerait une erreur.
Ceci est principalement fait pour séparer les responsabilités qui mènent à la lisibilité du code et à une meilleure évolutivité.
la source
En général, à moins que vous n'ayez une bonne raison de ne pas vérifier cette condition d'erreur, vous devriez le faire. La seule bonne raison à laquelle je puisse penser pour ne pas rechercher une condition d'erreur est lorsqu'il est impossible de faire quelque chose de significatif en cas d'échec. C'est un obstacle très difficile à rencontrer, car il existe toujours une option viable: sortir avec élégance.
la source