Partage
  • Partager sur Facebook
  • Partager sur Twitter

Les étiquettes en C

goto et continue

    7 juin 2019 à 17:22:39

    Bonjour

    Si j'ai bien compris, on peut faire des étiquettes en C en donnant un nom suivi de  :  comme label123: par exemple.

    Les étiquettes servent à la commande goto, on n'a pas le choix d'en avoir. Il faut bien aller quelque part.

    Mais qu'en est-il des commandes continue et break? peut-on leur donner une étiquette pour sortir de plus d'un niveau d'imbrication?

    J'ai eu beau chercher comme un damné sur Internet, je n'ai rien trouvé.

    Merci pour toute information.

    • Partager sur Facebook
    • Partager sur Twitter

    Le Tout est souvent plus grand que la somme de ses parties.

      7 juin 2019 à 17:47:36

      Hello,

      Hé non, continue et break n'influencent que le comportement de la boucle dans laquelle ils se trouvent.

      Mais les goto sont la porte ouverte à tous les "codes spaghetti":

      #include <stdio.h>
      #include <stdlib.h>
      
      int main(int argc,char *argv[]) {
      	int i,j;
      	
      	switch(atoi(argv[1])) {
      		case 1:
      			break;
      		case 2:
      			i=3;
      			goto i3;
      		default:
      			j=17;
      			goto j17;
      	}
      	
      i0:
      	puts("");
      	for(i=0;i<25;i++) {
      i3:
      		printf("%d ",i);
      	}
      	puts("");
      	
      	j=10;
      	while(j>0) {
      j17:
      		printf("%d ",j);
      		j--;
      		if(j==12)
      			goto i0;
      	}
      	
      	return(0);
      }

      Ce qui ne veut pas dire qu'il ne faut pas les utiliser, mais bien réfléchir avant de le faire

      -
      Edité par edgarjacobs 7 juin 2019 à 18:35:20

      • Partager sur Facebook
      • Partager sur Twitter

      On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

        7 juin 2019 à 21:36:26

        PierrotLeFou a écrit:

        Mais qu'en est-il des commandes continue et break? peut-on leur donner une étiquette pour sortir de plus d'un niveau d'imbrication?

        Salut,

        Non, à part goto, il n'y a pas de break qui disent de sortir de plusieurs niveaux d'imbrication. 

        Mais l'idée est d'éviter de faire trop de niveaux d'imbrication :) Et pour ça, on peut par exemple faire des fonctions. Si tu as un double for, et que dedans tu veux encore faire des for, et bien tu appelles une fonction. Et ainsi avec un return bien placé, tu pourras sortir de tous les for de la fonction ou tu es, qui dépend de la sémantique.

        • Partager sur Facebook
        • Partager sur Twitter

        Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

          7 juin 2019 à 23:07:55

          Bonjour

          Le code spaguetti? Très peu pour moi, même en assembler.

          Je voulais faire quelque chose du genre:

          lbl1:

          for(...) {

           ...

           for(...) {

           ...

           continue lbl1;

           }

           ...

          }

          Où la boucle intérieure s'exécute assez longtemps et a une variable avec l'attribut register.

          Je suppose qu'on peut faire autrement avec une variable logique (booléenne).

          Tant pis!

          • Partager sur Facebook
          • Partager sur Twitter

          Le Tout est souvent plus grand que la somme de ses parties.

            13 juin 2019 à 9:37:22

            Fvirtman a écrit:

            [break à un seul niveau]

            Mais l'idée est d'éviter de faire trop de niveaux d'imbrication


            Vu l'orientation du langage C au début, l'idée de Ritchie, c'est certainement que si tu veux sortir de plusieurs niveaux d'un coup, tu mets un goto, et où est le problème ?

            for(int i = .....) {
               for(int j = ......) {
                  for(int k = .....) {
                      ....
                      if (some_reason) goto exit_of_ijk_loops;
                      ...
                  }
               }
            } 
            exit_of_ijk_loops:
             


            Avec des noms bien choisis, c'est parfaitement clair, bien plus qu'un bidouillage à coup d'indicateurs (qui ont leur intérêt dans d'autres occasions).

            Forcément, si tu appelles les étiquettes l1, l2, l3, ça va pas faire le même effet en terme de lisibilité.

            Pour des continue, quelque chose du genre

            for (int i = ....) {
               for (int j = ......) {
                   for (int k = .....) {
                       ...
                       if (...) goto continue_with_next_i;
                   }
               }
               continue_with_next_i:
            }
            
                   

            Il ne faut pas confondre le code spaghetti, avec des gotos qui vont n'importe où sans logique apparente, et des tournures idiomatiques, comme l'utilisation du goto pour la libération des ressources en cas d'échec

            bool traitement() {
                bool ok = false;
            
                Ressource *a = reservation(....);
                if (a == NULL) goto echec_reservation_a;
            
                faire_quelque_chose();
            
                Ressource *b = reservation(....);
                if (b == NULL) goto echec_reservation_b;
            
                faire autre_chose();
            
                Ressource *c = reservation(....);
                if (c == NULL) goto echec_reservation_c;
            
                // normalement 
                faire le_boulot_normal(); 
                ok = true;
            
                // quand ça se passe mal on arrive directement ci-dessous
            
            echec_reservation_c: 
                liberer(b); 
            echec_reservation_b: 
                liberer(a); 
            echec_reservation_a: 
                return erreur; 
            }
            

            (un truc qu'on retrouve un peu partout dans les modules du noyau linux)




            -
            Edité par michelbillaud 13 juin 2019 à 10:37:19

            • Partager sur Facebook
            • Partager sur Twitter
              13 juin 2019 à 16:35:39

              L'idée de mon post était de "continuer" à l'élément suivant de la boucle externe si on a un échec dans la boucle interne. Je saute ce qui suit la boucle interne avant la fin de la boucle externe.

              Je suis d'accord que mon étiquette n'était pas explicite, mais c'était un exemple.

              • Partager sur Facebook
              • Partager sur Twitter

              Le Tout est souvent plus grand que la somme de ses parties.

                13 juin 2019 à 17:05:18

                Les exemples pour montrer, il vaut mieux que ça soit des exemples au sens de "modèle à suivre", pas simplement d'occurrence.

                -
                Edité par michelbillaud 13 juin 2019 à 17:06:47

                • Partager sur Facebook
                • Partager sur Twitter
                  13 juin 2019 à 18:23:03

                  Il vaut mieux suivre l'esprit de la loi plutôt que la lettre de la loi.

                  Si des gens font des copier/coller sans rien modifier ou vérifier, c'est eux qui ont un problème, pas moi.

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Le Tout est souvent plus grand que la somme de ses parties.

                    13 juin 2019 à 20:18:00

                    En fait, le C est un langage procédural : la philosophie, c'est l'appel des fonctions, pas le goto. Voila pourquoi le goto est mal vu.

                    Mais le processeur lui, fait massivement des goto (JMP), et même un appel de fonciton, c'est un PUSH et un JMP.

                    Tous les if en code machine sont des goto conditionnels (JZ, JNZ, JE, JNE ....) qui font un goto sur le else quand la condition n'est pas remplie. 

                    En bas niveau, le processeur fait beaucoup de goto. On a conservé le goto en C, mais c'est pas trop la philosophie du langage procédural qu'est le C.

                    Mais le compilateur converti beaucoup de trucs en goto du coup...

                    -
                    Edité par Fvirtman 13 juin 2019 à 20:18:25

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                      13 juin 2019 à 23:06:03

                      Il y a une différence entre les humains et les compilateurs. Ils peuvent générer du code spaguetti sans se tromper et rendre des comptes. Il y a tout de même moyen de coder en assembler de façon propre.
                      • Partager sur Facebook
                      • Partager sur Twitter

                      Le Tout est souvent plus grand que la somme de ses parties.

                        14 juin 2019 à 8:24:58

                        Fvirtman a écrit:

                        En fait, le C est un langage procédural : la philosophie, c'est l'appel des fonctions, pas le goto. Voila pourquoi le goto est mal vu.

                        Le goto est "mal vu" pour des raisons dogmatiques. Il n'y a pas plus de raison de mal le voir qu'aucun autre mécanisme du langage. Ça existe, et si on s'en sert à bon escient, tout va bien.

                        On va pas refaire l'histoire des idées en programmation, mais il faut se rappeler que dans les années 60, la norme était de programmer les boucles et les tests avec des branchements, parce que c'était quasiment la seule façon de faire dans les langages sérieux  comme cool et fortran (qui n'avait que la boucle do avec compteur).

                        Dijkstra, Wirth et toute la bande des algolistes ont lutté pour faire passer l'idée d'une programmation structurée, utilisant des structures de contrôles (boucles, alternatives...) pour que la forme du code en reflète clairement la logique. La question etait de savoir quelles structures implanter dans les langages, parce que si en théorie (le théorème de boëhm et jacopini !) Il suffit d'avoir si le et ifthenelse pour se passer du goto, en pratique ça ne donne pas du code clair,au contraire.

                        Donc il y a un compromis , quand on concoit un langage de programmation, à décider quelles structures de contrôle on integre dans le langage. Pour c, a philosophie c'était service minimum : if' switch for do et dowhile, et pour le reste débrouillez vous avec break, continue et goto. C'est tout. Meme pas de fonctions emboîtees 

                        Maintenant l'aspect dogmatique, ca a ete quand il a fallu lutter violemmment pour convaincre ceux qui connaissaient basic, ou fortran d'arreter de tout faire avec ces putains de goto quand c'est plus simple de mettre un while ou un if then else. Le côté ayatollah, c'est de l'interdire totalement au prétexte que des sagouins peuvent l'utiliser de travers.

                        Enfin, si vous voulez avoir une idée de la vraie  philosophie de c, regardez le code pondu par ceux qui l'ont inventé. Les sources des premières versions d'Unix, par exemple. Et comptez les goto. Prévoyez un sac en papier à portée de main.

                        Help yourself here https://minnie.tuhs.org/cgi-bin/utree.pl

                        Un exemple :

                        flag() {
                        	extern getc, peekc;
                        	auto c, f;
                        
                        	f = 0;
                        l1:
                        	switch(c=getc()) {
                        
                        	case 'w':
                        		f = 1;
                        		goto l1;
                        
                        	case 'i':
                        		f = 2;
                        		goto l1;
                        
                        	case 'b':
                        		f = 3;
                        		goto l1;
                        
                        	case 'f':
                        		f = 4;
                        		goto l1;
                        
                        	case 'd':
                        		f = 5;
                        		goto l1;
                        
                        	case 'p':
                        		f =+ 16;
                        		goto l1;
                        	}
                        	peekc = c;
                        	return(f);
                        }


                        https://minnie.tuhs.org/cgi-bin/utree.pl?file=V2/c/cvopt.c

                        C'était pourtant pas compliqué

                        // C légèrement modernisé
                        int flag() {
                            char c;
                            int f = 0;
                            int looping = 1;
                            do {
                        		switch (c = getc()) { 
                        		case 'w':
                        			f = 1;
                        			break;
                        		case 'i':
                        			f = 2;
                        			break;
                        		case 'b':
                        			f = 3;
                        			break; 
                        		case 'f':
                        			f = 4;
                        			break;
                        		case 'd':
                        			f = 5;
                        			break;
                        		case 'p':
                        			f += 16;
                        			break;
                        		default:
                        		    looping = 0;
                        		}
                            } while (looping);
                            peekc = c;
                            return(f);
                        }



                        -
                        Edité par michelbillaud 14 juin 2019 à 11:02:57

                        • Partager sur Facebook
                        • Partager sur Twitter
                          14 juin 2019 à 16:48:47

                          michelbillaud:

                          Je ne te citerai pas car je fous la merde dans ce cas. Je suis pleinement d'accord avec toi. Il ne faut pas être plus catholique que le pape!

                          En passant, ma synthèse vocale m'a presque dit goto hell!

                          Ton code est suffisamment clair et je ne vois pas ce qu'on aurait à redire sur celui-ci. Si on a gardé des goto, continue et break dans C, et ce, malgré la revision de 1999, c'est peut-être parce qu'on peut s'en servir. Il faut le faire à bon escient.

                          • Partager sur Facebook
                          • Partager sur Twitter

                          Le Tout est souvent plus grand que la somme de ses parties.

                            14 juin 2019 à 17:00:27

                            Et l'histoire de faire des fonctions et des returns ne marche pas à tout les coups, les destinations des "échappements" pouvant  être différentes

                            for (int i = ...) {
                               for (int j = ......) {
                                   for (int k =  ....) {
                                       ...
                                       if (raison_x(i,j,k)) goto try_another_j; 
                                       ...
                                       if (raison_y(i,j,k)) goto give_up;
                                       ...
                                       if (raison_xy(i,j,k) goto try_another_i;
                                       ...
                                    }
                                    try_another_j;
                               }
                               try_another_i:
                            }
                            give_up:
                            

                            (ce qui arrive dans les programmes de recherche d'une solution par backtracking, par exemple).

                            * Si on a gardé tout ça dans C, c'est parce que les gens qui programment en C en ont absolument besoin

                            • parce que le code existant doit continuer à tourner
                            • parce que le reste du langage est insuffisant pour exprimer ce qu'on veut faire

                            En pratique, l'absence de "break" et "continue" à plusieurs niveaux n'est pas dramatique, au lieu d'écrire, comme en java

                            //Java
                            oop_over_array: for(int i = .....) {
                                     ....
                                     for(....) {
                                        if (....) break loop_over_array;
                                     }
                               }
                            
                            

                            on écrira

                            // C
                            for (.....) {
                                ....
                                if (...) goto end_of_loop_over_array;
                            }
                            end_of_loop_over_array:
                            



                            (en java l'étiquette désigne l'instruction qui sera cassée par le break)

                            -
                            Edité par michelbillaud 14 juin 2019 à 17:09:15

                            • Partager sur Facebook
                            • Partager sur Twitter
                              14 juin 2019 à 20:48:12

                              michelbillaud a écrit:

                              c'était quasiment la seule façon de faire dans les langages sérieux  comme cool et fortran

                              Le langage Cool, un langage sérieux ?

                              (J'adore la faute de frappe ! :) )

                              Tiens, il y a un autre dogme dont j'ai entendu parler ici : pas de variables globales. En langage Cool, elles le sont toutes (et c'est sacrément pénible...)

                              • Partager sur Facebook
                              • Partager sur Twitter
                                14 juin 2019 à 21:11:54

                                Si tu parles de cobol, dont ma connaissance s'arrete au standard 85, les sous programmes peuvent être compilés séparément (et chargés dynamiquement !), on peut donc avoir des variables locales.

                                Aux dernières nouvelles, en C++,  std::out et ses copains sont des variables globales :-)

                                Ceci dit, moins on en a mieux on se porte. Mais si c'est pour passer en paramètre à tout le monde une variable déclarée dans le main, c'est de la connerie.

                                -
                                Edité par michelbillaud 14 juin 2019 à 21:15:12

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  15 juin 2019 à 2:14:27

                                  Si vous voulez sortir les vieux langages du placard, que dire des common de Fortran?

                                  Mêm en assembler (les vieux également) on a des espaces externes semblables au common.

                                  Moi aussi, je n'ai rien contre les variables globbales si on peut éviter de les passer en paramètres aux fonctions, surtout si on doit le faire pour presque toutes les fonctions.

                                  • Partager sur Facebook
                                  • Partager sur Twitter

                                  Le Tout est souvent plus grand que la somme de ses parties.

                                    15 juin 2019 à 11:16:03

                                    michelbillaud a écrit:

                                    Si tu parles de cobol, dont ma connaissance s'arrete au standard 85, les sous programmes peuvent être compilés séparément (et chargés dynamiquement !), on peut donc avoir des variables locales.

                                    Disons que les variables sont globales par rapport au fichier. Mais en effet ce n'est pas global tout court.

                                    Quant au débat sur le dogmatisme, je pense qu'il ne faut pas confondre moyen et but. Le but, c'est d'écrire des programmes qui marchent, sont maintenables, dont le code est lisible, etc. Éviter les goto et les variables globales peut faciliter l'atteinte de ce but, mais n'est pas le but en soi.)

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      15 juin 2019 à 11:55:13

                                      Tout à fait d'accord. Ca _peut_, souvent, pas toujours.

                                      Et vouloir à tout pris éviter les goto et les variables globales peut aussi rendre la programmation moins lisible et moins maintenable. C'est une question de circonstances, et le mot d'ordre "jamais de gotos ni de variables globales parce que c'est mal" fait plus de mal que de bien, parce qu'il empêche de se poser des questions sur ce qu'on fait, en terme de libilité et de maintenabilité justement.

                                      Les commons de Fortran ?  En fait il y avait deux usages

                                      • l'usage normal qui était de partager des variables globales
                                      • l'utilisation pour économiser de la place : une zone mémoire temporaire, redéfinie à la convenance de chaque sous-programme pour y mettre ses variables temporaires, du moment que 2 sous programmes n'utilisent pas le common en meme temps.

                                      (à l'époque, on n'utilisait pas forcément une pile pour stocker les variables locales qu'on appelle maintenant "automatiques").

                                      Le premier usage était raisonnable, le second une source d'emmerdements, mais il fallait tasser pour faire tourner les programmes sur des dizaines de Ko, pas des Megas.

                                      -
                                      Edité par michelbillaud 15 juin 2019 à 12:04:49

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        15 juin 2019 à 13:42:28

                                        Michel, tu as raison pour les vieilles machines, les mémoires étaient en méga (et encore) et non en giga. Elles n'avaient pas toujours de piles.

                                        As-tu déjà travaillé sur un Z80 avec 64 Ko? C'était un jouet.

                                        Voir sur Wikipédia les séries 6000 de Control Data

                                        Le _blank_ common facilitait l'extension de la mémoire requise.

                                        • Partager sur Facebook
                                        • Partager sur Twitter

                                        Le Tout est souvent plus grand que la somme de ses parties.

                                        Les étiquettes en C

                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                        • Editeur
                                        • Markdown