GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi fclose(NULL) de la glibc provoque une erreur de segmentation au lieu de renvoyer une erreur ?

fclose nécessite comme argument un FILE pointeur obtenu soit par fopen , l'un des flux standards stdin , stdout , ou stderr , ou d'une autre manière définie par l'implémentation. Un pointeur nul n'en fait pas partie, donc le comportement n'est pas défini, tout comme fclose((FILE *)0xdeadbeef) serait. NULL n'est pas spécial en C; mis à part le fait qu'il est garanti de ne pas être égal à n'importe quel pointeur valide, c'est comme n'importe quel autre pointeur invalide, et son utilisation invoque un comportement indéfini sauf lorsque l'interface que vous le transmettez aux documents dans le cadre de son contrat qui NULL a une signification particulière.

De plus, revenir avec une erreur serait un comportement valide (puisque le comportement est indéfini de toute façon) mais nuisible pour une implémentation, car il cache le comportement indéfini . Le résultat préférable de l'appel d'un comportement indéfini est toujours un plantage, car il met en évidence l'erreur et vous permet de la corriger. La plupart des utilisateurs de fclose ne vérifiez pas une valeur de retour d'erreur, et je parierais que la plupart des gens sont assez stupides pour passer NULL à fclose ne seront pas assez intelligents pour vérifier la valeur de retour de fclose . Un argument pourrait être avancé que les gens devraient vérifier la valeur de retour de fclose en général, car le vidage final peut échouer, mais ce n'est pas nécessaire pour les fichiers ouverts uniquement en lecture, ou si fflush a été appelé manuellement avant fclose (ce qui est de toute façon un idiome plus intelligent car il est plus facile de gérer l'erreur alors que le fichier est toujours ouvert).


fclose(NULL) devrait réussir. free(NULL) réussit, car cela facilite l'écriture du code de nettoyage.

Malheureusement, ce n'est pas ainsi qu'il a été défini. Par conséquent, vous ne pouvez pas utiliser fclose(NULL) dans les programmes portables. (Par exemple, voir http://pubs.opengroup.org/onlinepubs/9699919799/).

Comme d'autres l'ont mentionné, vous ne voulez généralement pas de retour d'erreur si vous passez NULL au mauvais endroit. Vous voulez un message d'avertissement, au moins sur les versions de débogage/test. Le déréférencement NULL vous donne un message d'avertissement immédiat, et la possibilité de collecter une trace qui identifie l'erreur de programmation :). Pendant que vous programmez, une erreur de segmentation est la meilleure erreur que vous puissiez obtenir. C a beaucoup plus d'erreurs subtiles, qui prennent beaucoup plus de temps à déboguer...

Il est possible d'abuser des retours d'erreurs pour augmenter la robustesse contre les erreurs de programmation. Cependant, si vous craignez qu'un plantage logiciel ne perde des données, notez qu'exactement la même chose peut se produire, par ex. si votre matériel perd de la puissance. C'est pourquoi nous avons la sauvegarde automatique (depuis les éditeurs de texte Unix avec des noms à deux lettres comme ex et vi). Il serait toujours préférable que votre logiciel plante visiblement, plutôt que de continuer avec un état incohérent.


Les erreurs dont parle la page de manuel sont des erreurs d'exécution, pas des erreurs de programmation. Vous ne pouvez pas simplement passer NULL dans n'importe quelle API attendant un pointeur et attendez-vous à ce que cette API fasse quelque chose de raisonnable. Passer un NULL pointeur vers une fonction documentée pour exiger un pointeur vers des données est un bogue.

Question connexe :en C ou en C++, dois-je vérifier les paramètres de pointeur par rapport à NULL/nullptr ?

Pour citer le commentaire de R. sur l'une des réponses à cette question :

... vous semblez confondre les erreurs résultant de conditions exceptionnelles dans l'environnement d'exploitation (fs plein, mémoire insuffisante, réseau en panne, etc.) avec des erreurs de programmation. Dans le premier cas, bien sûr, un programme robuste doit être capable de les gérer avec élégance. Dans ce dernier cas, un programme robuste ne peut pas les expérimenter en premier lieu.


Linux
  1. Erreur C++ :référence indéfinie à 'clock_gettime' et 'clock_settime'

  2. Pourquoi connect() donnerait EADDRNOTAVAIL ?

  3. Le moyen le plus simple de localiser un défaut de segmentation

  4. Pourquoi certains programmeurs du noyau utilisent-ils goto au lieu de simples boucles while ?

  5. Pourquoi ENOENT signifie-t-il Aucun fichier ou répertoire de ce type ?

Pourquoi j'utilise exa au lieu de ls sous Linux

Pourquoi voyez-vous l'erreur :snap "xyz" introuvable ?

Erreur Gparted - Erreur de segmentation (core vidé) ?

Linux vs Mac OS :15 raisons d'utiliser Linux au lieu de Mac OS

Pourquoi Slack renvoie-t-il une erreur de segmentation après la mise à niveau de Fedora 29 ?

Avertissements d'erreur de segmentation du serveur Web Linux / apache