Quelle est la meilleure façon de gérer les ressources pour un programme Je suis conscient qu'il y a beaucoup de tabou fort> sur Ceci Article démontre que les déclarations locales goto créent RAII pour C code C. Le code est soigné facile à suivre. Imaginez-le comme une série de Nettères si forts> déclarations. p> Je comprends que goto strong> est tabou dans de nombreuses autres langues car leur existence d'autres mécanismes de contrôle tels que Essayer / Catch, etc., cependant, il semble approprié. P> #include <stdlib.h>
#define STRING_MAX 10
void gotoExample()
{
char *string1, *string2, *string3, *string4, *string5;
if ( !(string1 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string1;
if ( !(string2 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string2;
if ( !(string3 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string3;
if ( !(string4 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string4;
if ( !(string5 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string5;
//important code goes here
gotoExample_string5:
free(string4);
gotoExample_string4:
free(string3);
gotoExample_string3:
free(string2);
gotoExample_string2:
free(string1);
gotoExample_string1:
}
void nestedIfExample()
{
char *string1, *string2, *string3, *string4, *string5;
if (string1 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string2 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string3 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string4 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string5 = (char*) calloc(STRING_MAX, sizeof(char)))
{
//important code here
free(string5);
}
free(string4);
}
free(string3);
}
free(string2);
}
free(string1);
}
}
int main(int argc, char* argv[])
{
nestedIfExample();
gotoExample();
return 0;
}
11 Réponses :
Personnellement, j'ai utilisé goto de cette manière dans le passé. Les gens détestent parce que cela leur rappelle le code des spaghetti qu'ils écrivaient / entretiennent, ou parce que quelqu'un qui a écrit / maintenu ce code a battu le concept que les gotos sont diaboliques. P>
Vous pourriez probablement écrire quelque chose de décent sans goto, bien sûr. Mais cela ne fera aucun mal à ce genre de circonstance. P>
D'accord. Je l'utilise, mais détestez toujours parce que cela me rappelle à quel point le codage gênant C est (par rapport à C ++).
Hah, désolé, vous parlez au mauvais gars si vous préférez C ++ sur C.;)
Oh, eh bien, tout le monde ne peut pas le faire au niveau suivant :-p
Le niveau suivant, ce serait Java: o
Oui, une langue sans quelque sorte de concept de pointeur de fonction, où chaque méthode est virtuelle et que vous ne pouvez pas avoir différentes implémentations pour deux membres d'interface avec la même signature. Niveau suivant. la toux i> Ouais.
Allez - Quelqu'un a dû le dire (et oui, toutes ces choses me manquent, mais il y a des gains)
@Kevin Eh bien, c'est pourquoi je n'aime pas le Java du tout. Cela vous donne quelques améliorations, mais bien moins encore que la plupart des autres langues de niveau supérieur, mais vous payez toujours avec une assez grande performance et d'énormes restrictions sémantiques.
@Let_me_be - mais, tout le monde le fait, ne devrions-nous pas aussi? ;)
@Kevin Eh bien, les entreprises qui ne veulent pas payer pour de bonnes programmeurs ou leur formation supplémentaire l'utilisent. C'est une bonne langue de singe codant (meilleure langue pour cet usage IMHO).
@LETMB - semble dure, j'utilise de nombreuses langues (OK, Java / C / Python / Objc) et trouvez chacun d'eux pour avoir leur place. 10 ans de Java / 25 ans de C et Just Année de Python et de l'OBJC). Je ne suis pas exactement un codeur de singe - bien que je ne sois pas k ou r, ni bjarne, ni gosling, ou .....
C'est ma philosophie. P>
Sérieusement, à certaines occasions, un goto code> est raisonnable, surtout s'il fait quelque chose d'évident comme passer au code de retour commun au bas d'une fonction. P>
Nettoyage de ressources est i> l'exemple canonique d'utilisation de goto dans C.
De deux de vos alternatives, Goto est naturellement meilleur et plus agréable. Mais il y a une troisième et meilleure alternative: utilisez la récursion! P>
La récursion est élégante mais je ne vois pas comment il est possible quand il y a une dépendance des ressources.
@Shiftbit: La ressource peut être globale ou peut être transmise en tant que paramètre de pointeur.
Si vous savez ce que vous faites, et que le code résultant a l'air plus propre et plus lisible (je parie qu'il fait), alors il n'y a absolument aucun problème avec l'utilisation de Gooto. Surtout l'exemple «récupération d'échec de l'initialisation gracieuse» que vous avez montré est largement utilisé. P>
BTW, lors de la rédaction de code structuré qui initialise 100 choses, vous auriez besoin de 100 niveaux d'indentation ... qui est laid laid. P>
"100 choses, vous auriez besoin de 100 niveaux d'indentation ... qui est simple laide." - Exactement
Si vous écrivez un code structuré qui initialise 100 choses, alors vous utilisez une boucle. Quelque chose a déjà mal tourné dans votre conception si vous devez initialiser 100 choses différentes dans la même fonction.
Je suis d'accord 100 choses est un peu absurde. Cependant, il n'est pas toujours possible de créer une boucle pour des choses telles que des poignées de fichier nécessitant des attributs spécifiques. Même s'il était imbriqué 7 ou 8 fois, il serait toujours difficile de lire.
Nettoyage en utilisant Cela dit, je citerai "la programmation structurée avec des déclarations goto" de Knuth: p>
Je soutiens pour l'élimination de Go to Cher dans certains cas et pour leur introduction dans d'autres. P>
blockQuote>
La citation de Knuth de Dijkstra dans ce même article: P>
"S'il vous plaît ne tombez pas dans le piège de croire que je suis terriblement dogmatique à propos de [la déclaration d'aller à la déclaration]. J'ai le sentiment inconfortable que d'autres font une religion, comme si les problèmes conceptuels de la programmation pouvaient être résolu par un seul tour, par une simple forme de discipline de codage! " [29]. P>
blockQuote> goto code> a l'avantage qu'il est moins sujet d'erreur. Devoir libérer chacune de chaque ressource allouée sur chaque point de retour peut conduire à une personne un jour manquant du nettoyage lorsque vous effectuez des travaux de maintenance. P>
Si, en utilisant Votre exemple pourrait également être écrit comme celui-ci (non goto code>, vous pouvez éviter d'écrire du code complexe, puis utilisez
goto code>.
goto < / code> s): p>
in c, Vous pouvez trouver de nombreuses utilisations parfaitement raisonnables de goto code> est souvent le seul moyen d'approximativement du code de nettoyage comme C ++ destructeurs ou java
enfin code> clauses. Comme c'est vraiment le meilleur outil que vous avez à cette fin, je le dis utiliser. Oui, c'est facile à abuser, mais il y a beaucoup de constructions de programmation. Par exemple, la plupart des programmeurs Java n'hésiteraient pas à lancer des exceptions, mais des exceptions sont également faciles à abuser s'ils sont utilisés pour autre chose que des rapports d'erreur, tels que le contrôle des flux. Mais si vous utilisez
goto code> explicitement dans le but d'éviter la duplication de code de nettoyage, il est prudent de dire que vous n'êtes probablement pas abuser. P>
goto code> dans le noyau Linux, par exemple. p>
Nul doute à ce sujet Dijkstra était une formidable personnalité dans le monde de la programmation. Le sien Goto considéré comme nocif papier était bien survolée. Oui goto peut être utilisé sans discernement et peut être nuisible mais beaucoup pensent qu'une interdiction de goto est non garantis. Knuth a fourni une réfutation très bien raisonnée à Dijkstra dans: Programmation structurée avec Go Tos P>
Lire le papier de Knuth, vous constaterez que votre motif goto est l'une des bonnes utilisations Pour aller. p>
BTW, Dijkstra est très utile pour un certain nombre d'autres choses aussi. Du: p>
Dijkstra était un grand mathématicien et a apporté d'énormes contributions à la science informatique. Cependant, je ne pense qu'il devait traiter avec ou s'intéresser, les trucs de type quotidien que 99,99% de Nos programmes font. P>
Utilisez goto uniquement avec raison et structure. Utilisez-les rarement. Mais utilisez-les. P>
Je structurerais le code différemment de celui des deux. Sauf si j'ai eu une raison remarquable de faire autrement, j'écrirais probablement le code quelque chose comme ceci:
Une très grande différence entre l'exemple de l'article dans l'article que vous portez et le code que vous affichez est que vos étiquettes gotos sont Vous utilisez Utilisez-le comme l'exemple et veillez à écrire du code à lire. P>
nettoyer_
goto line_1324 code> Ensuite, et le code sera modifié afin que l'étiquette
line_1234 code> est en ligne 47823 ... p>
Utilisation de CleanUp_
Pour clarifier, j'utilise
@Shiftbit ne sais pas ce que vous programmez dans, mais en C99 § 6.2.1 ", un nom d'étiquette est le seul type d'identifiant ayant une portée de fonctionnement."
Intéressant. Je vais observer Clean_Up
Pour moi, je préfère ce style de manutention d'erreur goto. Prenant plus loin une étape de Nick D, il utilise une seule étiquette Goto pour la manipulation des erreurs.
Notez que votre goto est allé au mauvais endroit - si votre malloc pour String 1 échoue, vous ne voulez pas libérer la mémoire que vous ne pouviez pas allouer :)
@Kevindtimm: une allocation échouée est signalée par un pointeur NULL - et en passant un pointeur NULL sur
GRATUIT code> est inoffensif (un NOP). Dans ce cas, vous assurant
gratuit code> Seules les allocations réussies conduit à des travaux supplémentaires (considérables) supplémentaires.
@ Jerry - Bien que ce soit vrai, c'est un paradigme dur pour beaucoup de grok. La libération d'un pointeur NULL peut être un commutateur de tâche et j'essaie donc de ne pas le faire (donc, ne libérez que des allocations réussies)
@Kevindtimm: Où au monde obtenez-vous l'idée que libérer un pointeur NULL aurait quelque chose à voir avec les commutateurs de tâches?
@ Jerry - Commutateur de tâche - La chose que votre cerveau fait quand il ne grok ne grok rien.