9
votes

C: ifs ou gotos nichés

Quelle est la meilleure façon de gérer les ressources pour un programme C strong>. Devrais-je utiliser une structure imbriquée si une structure ou dois-je utiliser des déclarations goto?

Je suis conscient qu'il y a beaucoup de tabou fort> sur goto forte> déclarations. Cependant, je pense que cela est justifié pour le nettoyage des ressources locales. J'ai fourni deux échantillons. On compare une structure imbriquée si elle utilise des déclarations goto. Je trouve personnellement que les déclarations GOTO rendent le code plus facile à lire. Pour ceux qui pourraient affirmer que la imbriquée si forte> Invite une meilleure structure, imaginez si le type de données était autre chose qu'un char *, comme une poignée de Windows. Je pense que la structure imbriquée si forte> Strut de la main avec une série de Createfile Strong> Fonctions ou toute autre fonction qui prend de grandes quantités de paramètres. P>

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


5 commentaires

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 est inoffensif (un NOP). Dans ce cas, vous assurant gratuit 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.


11 Réponses :


3
votes

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.

Vous pourriez probablement écrire quelque chose de décent sans goto, bien sûr. Mais cela ne fera aucun mal à ce genre de circonstance.


10 commentaires

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 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 .....



6
votes

Utilisez toujours un goto dans chaque programme pour gêner les puristes

C'est ma philosophie.

Sérieusement, à certaines occasions, un goto est raisonnable, surtout s'il fait quelque chose d'évident comme passer au code de retour commun au bas d'une fonction.


1 commentaires

Nettoyage de ressources est l'exemple canonique d'utilisation de goto dans C.



2
votes

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!


2 commentaires

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.



1
votes

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é.

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.


3 commentaires

"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.



10
votes

Nettoyage en utilisant goto 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.

Cela dit, je citerai "la programmation structurée avec des déclarations goto" de Knuth:

Je soutiens pour l'élimination de Go to Cher dans certains cas et pour leur introduction dans d'autres.

La citation de Knuth de Dijkstra dans ce même article:

"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].


0 commentaires

11
votes

Si, en utilisant goto , vous pouvez éviter d'écrire du code complexe, puis utilisez goto .

Votre exemple pourrait également être écrit comme celui-ci (non goto < / code> s): xxx


0 commentaires

1
votes

in c, goto est souvent le seul moyen d'approximativement du code de nettoyage comme C ++ destructeurs ou java enfin 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 explicitement dans le but d'éviter la duplication de code de nettoyage, il est prudent de dire que vous n'êtes probablement pas abuser.

Vous pouvez trouver de nombreuses utilisations parfaitement raisonnables de goto dans le noyau Linux, par exemple.


0 commentaires

11
votes

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

Lire le papier de Knuth, vous constaterez que votre motif goto est l'une des bonnes utilisations Pour aller.

BTW, Dijkstra est très utile pour un certain nombre d'autres choses aussi. Du:

  • La programmation orientée objet est une idée exceptionnellement mauvaise qui n'aurait pu être originaire de Californie.

    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.

    Utilisez goto uniquement avec raison et structure. Utilisez-les rarement. Mais utilisez-les.


0 commentaires

2
votes

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: xxx


0 commentaires

2
votes

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 _ et leurs étiquettes goto sont nettoyer_ .

Vous utilisez goto line_1324 Ensuite, et le code sera modifié afin que l'étiquette line_1234 est en ligne 47823 ...

Utilisez-le comme l'exemple et veillez à écrire du code à lire.


4 commentaires

Utilisation de CleanUp_ pourrait provoquer une collision. En le préfixant avec le nom de la fonction que je vous ai suivi, chaque étiquette sera unique. Peut-être préféreriez-vous préférer un double préfixe _cleanup_ ?


Pour clarifier, j'utilise _ pas _


@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 dans le futur.



0
votes

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. xxx


0 commentaires