Partage
  • Partager sur Facebook
  • Partager sur Twitter

Exercices pour débutants en C (suite)

Venez vous entrainer!

Anonyme
    10 décembre 2010 à 20:57:53

    Je le prends pas mal mais dans les règle je lis ça :

    .. Vous êtes donc tous invités à poster votre code directement sur le topic, avec des balises secret. Toutefois "réponse" reste en fonction pour ceux qui auraient peur de poster en public. Je les y invite cependant car c'est toujours plus constructif d'avoir divers avis. J'en profite pour demander aux participants de faire un effort sur la lisibilité et la clarté de leur code, afin que nous n'ayions pas à décrypter ce que vous avez posté. C'est un effort salutaire autant pour les lecteurs que pour les posteurs.
    • Partager sur Facebook
    • Partager sur Twitter
    Anonyme
      10 décembre 2010 à 21:19:18

      Citation : Lithrein

      Autrement pour les propositions d'exercices je pense que l'on pourrait les faire directement ici puisque chaque exercice à un sujet dédié sur le forum.
      En clair, je vois bien ce sujet regrouper les propositions d'exercices, les exercices retenus ainsi que leurs corrections.



      C'est vrai que ce n'est pas très clair mais bon ...
      C'est surtout que personne ne le fait plus.

      Bref
      • Partager sur Facebook
      • Partager sur Twitter
        10 décembre 2010 à 23:04:55

        MMMh,
        @frdemahieu:
        Tu aurais pu lire l'historique de ce post...

        Tu aurais vu que ce n'est pas si simple de proposer un truc...

        Que dire, tu as vu l'exo de Tosh?

        Je ne vais même pas m'emporter. :-°
        • Partager sur Facebook
        • Partager sur Twitter
        Zeste de Savoir, le site qui en a dans le citron !
          22 décembre 2010 à 9:23:26

          Voici mon programme en mode Joueur contre Joueur :

          #include <stdio.h>
          #include <stdlib.h>	// pour l19/20 (srand)
          #include <time.h>	// pour l19/20 (time)
          
          void Afficher(char c[]);
          int verif_fin(char c[], char car);
          int verif_case(char c[], int pos);
          
          int main(void)
          {
          	int end=1, win1=0, win2=0, j, fin, pos, possible;
          	char c[9], car;
          
          	do {
          		c[0] = '1', c[1] = '2', c[2] = '3', c[3] = '4', c[4] = '5', c[5] = '6', c[6] = '7', c[7] = '8', c[8] = '9';
          		fin=0;
          		possible=1;
          
          		srand(time(NULL));
          		j = (rand() % (2 - 1 + 1)) + 1;
          
          		system("clear");
          		Afficher(c);
          
          		do {
          			if (j == 1)
          			{
          					printf("\n Joueur 1 :\t");
          					scanf("%d", &pos);
          					j = 2;
          					car = 'X';
          			}
          
          			else
          			{
          					printf("\n Joueur 2 :\t");
          					scanf("%d", &pos);
          					j = 1;
          					car = 'O';
          			}
          
          					possible = verif_case(c, pos);
          
          					if (possible == 0 || pos > 9 || pos < 1)
          					{
          						do {
          							printf("\n Case déjà remplie, ou mauvais chiffre entré. Veuillez recommencer :\t");
          							scanf("%d", &pos);
          							possible = verif_case(c, pos);
          						} while (possible == 0);
          					}
          			
          			c[pos-1] = car;
          
          			system("clear");
          			Afficher(c);
          			fin = verif_fin(c, car);
          
          		} while (fin == 0);
          
          		if (fin == 3)
          		{
          			printf("\n\nMatch nul.\n\n");
          		}
          
          		else
          		{
          			printf("\n\nVictoire du joueur %d.\n\n", fin);
          		}
          
          		if (fin == 1)
          		{
          			win1++;
          		}
          
          		if (fin == 2)
          		{
          			win2++;
          		}
          
          		printf("\tRejouer ?\t1. Oui\n\t\t\t2. Non\t\t");
          		scanf("%d", &end);
          
          	} while (end == 1);
          
          	if (win1 > win2)
          	{
          		printf("Victoire %d à %d du joueur 1.\n\n", win1, win2);
          	}
          
          	if (win1 == win2)
          	{
          		printf("Égalité %d partout.\n\n", win1);
          	}
          
          	if (win1 < win2)
          	{
          		printf("\nVictoire %d à %d du joueur 2.\n\n", win2, win1);
          	}
          
          	return 0;
          }
          
          
          
          ////////////////////////////////////////////////////////////////
          void Afficher(char c[])
          {
          	printf("\n\t\t ___________");
          	printf("\n\t\t|   |   |   |");
          	printf("\n\t\t| %c | %c | %c |", c[0], c[1], c[2]);
          	printf("\n\t\t|___|___|___|");
          	printf("\n\t\t|   |   |   |");
          	printf("\n\t\t| %c | %c | %c |", c[3], c[4], c[5]);
          	printf("\n\t\t|___|___|___|");
          	printf("\n\t\t|   |   |   |");
          	printf("\n\t\t| %c | %c | %c |", c[6], c[7], c[8]);
          	printf("\n\t\t|___|___|___|\n");
          }
          
          
          
          int verif_fin(char c[], char car)
          {
          	int fin = 0;
          
          	if ( (c[0] == c[1] && c[1] == c[2]) || (c[3] == c[4] && c[4] == c[5]) || (c[6] == c[7] && c[7] == c[8]) || (c[0] == c[3] && c[3] == c[6]) || (c[1] == c[4] && c[4] == c[7]) || (c[2] == c[5] && c[5] == c[8]) || (c[0] == c[4] && c[4] == c[7]) || (c[2] == c[4] && c[4] == c[6]))
          	{
          		if (car == 'X')
          		{
          			fin = 1;
          		}
          
          		else
          		{
          			fin = 2;
          		}
          	}
          
          	if ( c[0] != '1' && c[1] != '2' && c[2] != '3' && c[3] != '4' && c[4] != '5' && c[5] != '6' && c[6] != '7' && c[7] != '8' && c[8] != '9' && fin !=1 && fin !=2 )
          	{
          		fin = 3;
          	}
          
          	return(fin);
          }
          
          
          
          int verif_case(char c[], int pos)
          {
          	int res;
          
          	if ( (pos==1 && c[0]!='1') || (pos==2 && c[1]!='2') || (pos==3 && c[2]!='3') || (pos==4 && c[3]!='4') || (pos==5 && c[4]!='5') || (pos==6 && c[5]!='6') || (pos==7 && c[6]!='7') || (pos==8 && c[7]!='8') || (pos==9 && c[8]!='9') || pos > 9 || pos < 1 )
          	{
          		res=0;
          	}
          
          	else
          	{
          		res=1;
          	}
          
          	return(res);
          }
          



          Il y a sans doutes des choses à améliorer, mais bon j'en suis assez satisfait.

          Je vais maintenant essayer de me lancer dans une IA.


          Si vous avez des remarques/conseils/corrections à apporter, n'hésitez pas !
          • Partager sur Facebook
          • Partager sur Twitter
            22 décembre 2010 à 13:36:55

            Salut th-p !

            Tu sais, les codes, il faut les poster ici.


            Bonne continuation !
            • Partager sur Facebook
            • Partager sur Twitter
              23 décembre 2010 à 14:06:32

              salut tous le monde ; je suis un nouveau membre je commance a 0.5 pas a zero , se site je le trouve excelent . je demande monsieur 日本語を勉強する。 celui qui a ecrit la correction zword de m'explique enum{in,out} et merci
              • Partager sur Facebook
              • Partager sur Twitter
                23 décembre 2010 à 14:37:42

                @bechir011 : enum {in, out}; permet de déclarer une énumération anonyme qui contient deux constantes : `in' (qui vaut 0) et qui signifie que l'on se trouve dans un mot et `out' (qui vaut 1) et qui signifie que l'on se trouve hors d'un mot.

                Plus de renseignement dans le cours "officiel".

                Autrement, mon pseudonyme c'est Lithrein. 日本語を勉強する est ma citation. L'un est un nom propre l'autre une phrase, nuance.
                • Partager sur Facebook
                • Partager sur Twitter
                  31 décembre 2010 à 12:22:13

                  On a droit de proposer des exercices ?
                  • Partager sur Facebook
                  • Partager sur Twitter
                    2 janvier 2011 à 22:09:04

                    Mois: Janvier
                    Sujet: Boucles, manipulation de tableaux, pointeurs et récursivité

                    zMorp - 2ème partie


                    Le sujet se trouve ici : http://www.siteduzero.com/forum-83-597 [...] e-partie.html
                    • Partager sur Facebook
                    • Partager sur Twitter
                      8 janvier 2011 à 9:20:41

                      Bonjour amie zero je ne sais pas si j'ai le droit de proposer des mini exercice pour debutant ? =)

                      Mes bon je vais can meme vous en faire un :

                      Voila le concept vous connaiser tous la bourse x)
                      et bien vous dever creer un programme pour acheter des actions ...
                      et vous devais pouvoir voir si vos action baisse ou ne baisse pas vous aver bien compris le concept j'imagine ...

                      Bon codage

                      Cordialement dristilil
                      • Partager sur Facebook
                      • Partager sur Twitter
                        1 février 2011 à 19:06:42

                        Février - getline (3)



                        Prérequis



                        • Entrées / Sorties
                        • Manipulations de fichiers.
                        • Gestion des erreurs.
                        • (Ré)allocation de mémoire.
                        • Utilisation de fonctions.


                        Énoncé



                        La fonction 'getline` qui sert à récupérer une ligne - chaine de caractères se terminant par un '\n'. C'est une extension de la bibliothèque standard fournie par la glibc. Elle n'existe donc pas sur Windows, par contre, elle existe sur les Unix-like (pensez donc à nommer votre fonction autrement).

                        L'exercice suivant vous propose de réécrire cette fonction qui est assez intéressante puisqu'elle fait appel à vos connaissances en C plutôt qu'à d'autres choses.

                        Le manuel de la fonction :
                        man (3) getline : (fr), (en).

                        La gestion des entrées sorties et des erreurs n'est pas forcément évidente, n'hésitez donc pas à faire le travail étape par étape et à approfondir les concepts.
                        Toute question est la bienvenue sur le fil destiné à cet usage .

                        L'idée originale est de yoch


                        Bonne Chance.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          1 février 2011 à 20:51:24

                          Oui, cet exo est une révision du cour C, je vais participer...

                          Le nom de cet exo ne devrait pas plutôt être "zGetline (3)" ?
                          • Partager sur Facebook
                          • Partager sur Twitter
                            9 février 2011 à 18:02:45


                            Bonjour,

                            Après avoir utilisé la correction Lithrein pour la conception de la calculatrice (version débutant) je me rend compte qu'il y a un petit hic. En effet, lors de la remise à 0 par c, il faut que l'utilisateur jongle pour rentrer un chiffre qqconque et une fois revenu au début de la boucle utiliser ses nouvelles valeurs.

                            Je ne sais pas si je me suis fais comprendre, pour résumer lorsque que l'on fait c, on ne peut pas de suite utiliser nos nouveaux chiffres.

                            Existe une solution pour parer à cela ? merci.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              9 février 2011 à 18:35:54

                              @Fherreri : Je reconnais que c'est un problème un peu embêtant. Pour le contourner, il suffit de remettre l'opérateur + par défaut comme lors de l'initialisation puisque si cela n'est pas fait, aucune des conditions entre la récupération du nombre et de l'opération (ligne 14 à 21) n'est satisfaite puisque op='c'.

                              #include <stdio.h>
                              
                              int
                              main(void) {
                              
                                  int resultat = 0, nb = 0;
                                  char op = '+';
                              
                                  while (op != 'q') {
                                      printf("Entrez un nombre : ");
                                      scanf("%d",&nb);
                                      while (getchar() != '\n');
                              
                                      if (op == '+')
                                          resultat += nb;
                                      else if (op == '-')
                                          resultat -= nb;
                                      else if (op == '*')
                                          resultat *= nb;
                                      else if (op == '/')
                                          resultat /= nb;
                              
                                      printf("Quelle operation (+, -, *, /, c, q) : %d ", resultat);
                                      op = getchar();
                              
                                      if (op == 'c') {
                                          resultat = 0;
                                          op = '+';
                                      }
                                  }
                              
                                  return 0;
                              }
                              


                              En tout cas, merci de m'avoir rapporté ce problème, je vais modifier la correction en conséquence.
                              • Partager sur Facebook
                              • Partager sur Twitter
                                9 février 2011 à 18:57:12

                                Ah merci pour cette solution qui fonctionne parfaitement !

                                Ça me motive pour la suite de cette exercice !

                                Et merci pour votre travail :)
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  3 mars 2011 à 19:44:14

                                  écrire un programme permettant de résoudre des systèmes d'équations et des équations dans C.
                                  un menu sera présenté pour le choix de l'opération
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    5 mars 2011 à 17:05:41

                                    Citation : lulu45

                                    écrire un programme permettant de résoudre des systèmes d'équations et des équations dans C.
                                    un menu sera présenté pour le choix de l'opération



                                    Ce n'est qu'un au revoir... Bon, relis les messages precedents, juste en haut ;)
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      7 mars 2011 à 21:48:33

                                      Mois : Mars
                                      Sujet : Manipulation de chaînes de caractères

                                      zConjug



                                      L'exercice de ce mois-ci nous permettra de revoir un peu la conjugaison française. :)
                                      Dans un premier temps, serez amenés à conjuguer un verbe du premier ou du deuxième groupe sans prendre en compte les exceptions. Par la suite, nous essayerons de gérer quelques exceptions (oui... il y en a tellement).
                                      Tous les temps dans lesquels seront conjugués nos verbes seront à l'indicatif.

                                      Les verbes du premier groupe



                                      Dans un premier temps, la conjugaison de verbes du premier groupe !
                                      Pour ce programme, l'utilisateur entrera un verbe et le programme devra vérifier s'il se fini bien par -er, si oui, le conjuguer au présent. On supposera que l'utilisateur est intelligent (s'il rentre un verbe du genre aerzasdqdfzer, notre programme le conjuguera).
                                      Voici les terminaisons du présent :

                                      Je         -e
                                      Tu         -es
                                      Il/Elle/on -e
                                      Nous       -ons
                                      Vous       -ez
                                      Ils/Elles  -ent


                                      La langue française ne se limite pas à un seul temps, c'est pourquoi vous améliorerez votre programme pour qu'il conjugue aussi votre verbe à l'imparfait, au futur simple (je ne mets pas le passé simple à cause des accents). Encore une fois, nous ne prendrons pas en compte les exceptions (manger, appeler, etc.).
                                      Voici les terminaisons :

                                      Imparfait    : -ais,  -ais,  -ait, -ions,  -iez,  -aient
                                      Futur simple : -erai, -eras, -era, -erons, -erez, -eront


                                      Les verbes du second groupe



                                      Ici, rien de difficile si vous avez fait la première partie. Vous vérifiez si le verbe appartient au second groupe (se finir par -ir) et vous le conjuguez. Pour les verbes du 2ème groupe, il n'y a pas d'exceptions ; si le participe présent du verbe ne se finit pas par -issant, c'est un verbe du 3ème groupe (on ne fera pas de distinctions, si l'utilisateur entre un verbe du 3ème groupe (pouvoir, fuir) on le conjuguera comme un verbe du 2ème groupe).
                                      Voilà les terminaisons :

                                      Présent      : -is,     -is,     -it,     -issons,  -issez,  -issent
                                      Imparfait    : -issais, -issais, -issait, -issions, -issiez, -issaient
                                      Futur simple : -irai,   -iras,   -ira,    -irons,   -irez,   -iront


                                      Gestion des exceptions des verbes du premier groupe



                                      Comme vous l'aurez constatés, certains verbes ont quelques particularités... c'est pourquoi vous ferez un programme qui gère toutes ces exceptions. Vous pourrez les retrouver sur ce lien.
                                      Prenez bien en compte que le verbe 'aller' est du 3ème groupe. ;)
                                      Vous pourrez modifier le 'je' devant les verbes ; si le verbe commence par une voyelle, on met j' et non je (j'appelle, je chante).
                                      S'il y en a qui se sentent d'attaque, vous pouvez essayer de conjuguer certains verbes du 3ème groupes (-oir, etc.).

                                      Bon courage !


                                      Un topic à été créé pour cet exercice : http://www.siteduzero.com/forum-83-622 [...] ebutants.html


                                      Pouet_forever m'a considérablement aidé et conseillé pour la réalisation cet exo, je le remercie beaucoup.
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        6 avril 2011 à 20:27:47

                                        Correction - getline (3)



                                        Je rappelle l'énoncé en secret.

                                        Citation : Lithrein


                                        Prérequis



                                        • Entrées / Sorties
                                        • Manipulations de fichiers.
                                        • Gestion des erreurs.
                                        • (Ré)allocation de mémoire.
                                        • Utilisation de fonctions.



                                        Énoncé



                                        La fonction 'getline` qui sert à récupérer une ligne - chaine de caractères se terminant par un '\n'. C'est une extension de la bibliothèque standard fournie par la glibc. Elle n'existe donc pas sur Windows, par contre, elle existe sur les Unix-like (pensez donc à nommer votre fonction autrement).

                                        L'exercice suivant vous propose de réécrire cette fonction qui est assez intéressante puisqu'elle fait appel à vos connaissances en C plutôt qu'à d'autres choses.

                                        Le manuel de la fonction :
                                        man (3) getline : (fr), (en).

                                        La gestion des entrées sorties et des erreurs n'est pas forcément évidente, n'hésitez donc pas à faire le travail étape par étape et à approfondir les concepts.
                                        Toute question est la bienvenue sur le fil destiné à cet usage .

                                        L'idée originale est de yoch



                                        Bonne Chance.



                                        Dans l'objectif de faire un travail fini, je vais vous proposer une correction possible à l'exercice qui portait sur la manière de réécrire la fonction getline.
                                        Je tiens tout d'abord, à dire que cette correction a essentiellement un but pédagogique. En effet, la version que je vous propose ne mise pas sur les performances et sur d'autres astuces visant à les optimiser. Par conséquent, je ne préconise pas l'usage du code de cette correction si les performances sont recherchées, mais plutôt d'une version plus légère telle que celle proposée par gouttegd sur le sujet dédié.

                                        Après cette petite introduction, je vous propose d'entamer tout doucement la-dite correction.

                                        "Simplifions" le traitement



                                        Tout d'abord, il est couramment admis, que la gestion du texte (surtout quand sa longueur est inconnue) n'a jamais vraiment été une partie de plaisir en C, c'est pourquoi, je vous propose de commencer par créer une "interface" visant à "simplifier" la gestion d'un texte de longueur variable. Pour cela, nous allons créer une structure qui va servir de buffer et quelques fonctions pour la manipuler.

                                        Commençons donc par se demander qu'est-ce qu'un buffer, c'est tout simplement une chaine de caractères de longueur variable (donc un pointeur sur un char), puisqu'il peut être commode d'avoir accès à sa taille en temps constant, j'ai rajouté un champ contenant la longueur de la chaine mais cela n'est en aucun cas obligatoire.
                                        struct buffer_s {
                                            char * string;
                                            size_t buf_length;
                                        };
                                        


                                        Maintenant, que l'on a notre structure, il faudrait que l'on crée une ou deux fonctions pour la manipuler aisément, autrement, tout l'intérêt est perdu. Les fonctions qui me semblent essentielles pour résoudre cet exercice sont celles qui permettent de créer un buffer, de l'agrandir et d'optimiser sa taille.
                                        D'autres fonctions utiles telles que la réduction, la remise à zéro, etc. peuvent être rajoutées mais elles ne sont pas utiles à la résolution. Leurs implémentations sont donc laissées aux lecteurs intéressés.

                                        En toute logique, il convient de commencer par celle qui permet de créer un buffer.
                                        Que ce passe-t-il ? La fonction reçoit un pointeur vers une chaine de caractère (celle-ci doit avoir été allouée dynamiquement, puisque dans un cas on recopie simplement le pointeur), ainsi que la taille qu'il doit avoir.
                                        On peut distinguer deux cas : soit le pointeur sur la chaine est nul, dans ce cas on initialise intégralement le buffer pointé (allocation, calcul de taille). Soit, le pointeur n'est pas nul, dans ce cas, on a juste à fixer la taille de la chaine grâce au deuxième paramètre et à faire pointé le pointeur de la structure vers la chaine de caractères reçue.

                                        struct buffer_s
                                        create_buffer (char * bufptr, int n) {
                                            struct buffer_s buf = {NULL, 0};
                                        
                                            if (bufptr == NULL) {
                                                errno = 0;
                                                if ((buf.string = calloc(BUF_SIZE, sizeof *(buf.string))) == NULL) {
                                                    puts(strerror(errno));
                                                }
                                            } else {
                                                buf.string = bufptr;
                                            }
                                        
                                            buf.buf_length = (bufptr) ? n : BUF_SIZE;
                                        
                                            return buf;
                                        }
                                        


                                        Deuxièmement, la fonction magique du jour, concaténer des chaines sans avoir à se soucier de la mémoire.
                                        Pour faire de la magie, il nous suffit d'une fonction qui prend en paramètre un pointeur sur une chaine de caractères et un pointeur sur un buffer et qui fait de la magie (ou pas (enfin, nous verrons bien)).

                                        Dans un premier temps on vérifie que les paramètres reçus ne valent pas NULL. Si jamais c'est le cas, on retourne -1 (ça casse un peu la magie mais ce n'est pas bien grave).
                                        Deuxièmement, si la longueur de la chaine à ajouter est égale à 0 alors le travail est vite fait.
                                        On peut maintenant distinguer de cas, soit on a la place de stocker la nouvelle chaine dans la première, dans ce cas on laisse à strncat le boulot. Autrement, il faut agrandir la chaine de départ à l'aide de la fonction realloc. En C, cette fonction est l'une des plus subtile, en effet, il faut bien penser à s'occuper du cas où elle échoue, il faut donc faire soit une sauvegarde du pointeur soit travailler sur un pointeur temporaire avant de commencer la ré-allocation.
                                        Finalement, strncat s'occupe de la fin.


                                        int
                                        buffer_append (struct buffer_s * buf, char * content) {
                                            size_t content_len;
                                            size_t buf_len;
                                        
                                            if (!buf || !content)
                                                return -1;
                                        
                                            content_len = strlen(content);
                                            buf_len     = strlen(buf->string);
                                            errno       = 0;
                                        
                                            if (content_len == 0)
                                                return -1;
                                        
                                            if (buf_len + content_len + 1 > buf->buf_length) {
                                                char * new_lineptr = NULL;
                                                long needed = buf->buf_length + 2 * content_len + 1;
                                        
                                                if (needed < 0) {
                                                    errno = ERANGE;
                                                    return -1;
                                                }
                                        
                                                if ((new_lineptr = realloc(buf->string, needed)) == NULL)
                                                    return -1;
                                        
                                                buf->string     = new_lineptr;
                                                buf->buf_length = needed;
                                            }
                                        
                                            /* /!\ strncat ajoute un '\0' en plus des 'content_len` caracteres
                                              si celui-ci n'est pas present. */
                                        
                                            strncat(buf->string, content, content_len);
                                        
                                            return errno;
                                        }
                                        


                                        Enfin, la fonction qui permet d'optimiser la place en mémoire de la chaine (c'est cette fonction qui en cas d'abus peut nuire aux performances).

                                        La mettre en place est un jeu d'enfant, il suffit de lui passer la structure à optimiser puis on calcule la taille minimale (le nombre de caractères plus un pour le '\0'). On crée une nouvelle chaine avec ces dimensions. On copie l'ancienne chaine dans la nouvelle et enfin on met à jour notre structure et c'est déjà fini.
                                        int
                                        buffer_fix (struct buffer_s * buf) {
                                            char * new_lineptr = NULL;
                                            size_t str_len;
                                        
                                            if (buf == NULL)
                                                return -1;
                                        
                                            errno = 0;
                                            str_len = strlen(buf->string);
                                        
                                            if ((new_lineptr = calloc(str_len + 1, sizeof *(buf->string))) == NULL)
                                                return errno;
                                        
                                            strncpy(new_lineptr, buf->string, str_len);
                                            new_lineptr[str_len] = '\0'; /* Useless */
                                        
                                            free(buf->string), buf->string = NULL;
                                        
                                            buf->string = new_lineptr;
                                            buf->buf_length = str_len+1;
                                        
                                            return errno;
                                        }
                                        


                                        Maintenant, que l'on a une "interface" qui fait un peu de magie en coulisse, on va pouvoir s'attaquer au gros morceau, à savoir l'écriture de la fonction getline.

                                        Réécrivons getline



                                        Pour tout vous dire plus simple vous mourrez, regardez donc par vous même :
                                        ssize_t
                                        _getline(char **lineptr, size_t *n, FILE *stream) {
                                            return _getdelim(lineptr, n, '\n', stream);
                                        }
                                        


                                        Tout ça c'est génial à un petit détail près : comment fonctionne _getdelim ? Allons donc voir.

                                        Le prototype de _getdelim est le suivant :
                                        ssize_t _getdelim(char **lineptr, size_t *n, int delim, FILE *stream).

                                        Vous conviendrez que si l'un des pointeurs passé en paramètre est nul, on ne peut rien faire. En conséquence, dans ce cas là, on ne fait rien.
                                        À partir de la chaine *lineptr, on crée un buffer (après tout, il fallait bien qu'ils servent un peu.)
                                        Puis, on lit les caractères un à un avec fgetc ou par blocs avec fgets, ceux-ci sont stockés dans une variable tampon que l'on vide dans notre buffer dès qu'elle est pleine. On continue d'effectuer ce traitement, tant que le délimiteur de chaine n'est pas trouvé ou que l'on est pas tombé sur EOF.
                                        Finalement, on optimise le buffer et on met à jour ses attributs avant de le renvoyer. Et, ... le tour est joué !

                                        ssize_t
                                        _getdelim(char **lineptr, size_t *n, int delim, FILE *stream) {
                                            struct buffer_s buffer;
                                            long nb_car;
                                            int c, i;
                                            char tmp[BUF_SIZE+1] = {0};
                                        
                                            if (!lineptr || !n || !stream) {
                                                errno = EINVAL;
                                                return -1;
                                            }
                                        
                                            errno = 0;
                                            buffer = create_buffer(*lineptr, *n);
                                            nb_car = 0;
                                        
                                            if (errno == ENOMEM)
                                                return -1;
                                        
                                            for (i = 0 ; (c = getc(stream)) != delim && c != EOF ; ++nb_car, i = (i + 1) % BUF_SIZE) {
                                                int err = 0;
                                        
                                                if (i == 0)
                                                    memset(tmp, 0, BUF_SIZE);
                                                else if (i == BUF_SIZE - 1)
                                                    err = buffer_append(&buffer, tmp);
                                        
                                                    tmp[i] = c;
                                        
                                                if (err)
                                                    return -1;
                                            }
                                        
                                            if (buffer_append(&buffer, tmp))
                                                return -1;
                                        
                                            if (c == EOF)
                                                return -1;
                                        
                                            if (c == delim) {
                                                char tmp[2] = {0};
                                                tmp[0] = delim;
                                                buffer_append(&buffer, tmp);
                                            }
                                        
                                            /* '\0' est present de base a la fin du buffer */
                                            if (buffer_fix(&buffer))
                                                return -1;
                                        
                                            *lineptr = buffer.string;
                                            *n = strlen(*lineptr);
                                        
                                            return nb_car;
                                        }
                                        


                                        Le tout en une fois :
                                        #include <stdio.h>
                                        #include <stdlib.h>
                                        #include <string.h>
                                        #include <stddef.h>
                                        #include <errno.h>
                                        
                                        /********* Documentation ***********************************************************
                                         * getline()  lit  une  ligne  entiere et stocke l'adresse du tampon contenant le  *
                                         * texte  dans *lineptr. Le tampon se termine par un 0 terminal et inclut le cara- *
                                         * ctere saut-de-ligne, si un tel separateur a ete trouve.                         *
                                         * Si  *lineptr  vaut NULL, la routine getline() alloue un tampon pour recevoir la *
                                         * ligne, ce tampon devra etre libere par le programme utilisateur.                *
                                         * Alternativement,  avant d'appeler getline(), *lineptr peut contenir un pointeur *
                                         * vers  un  tampon alloue par malloc() et de taille *n octets. Si le buffer n'est *
                                         * pas  suffisant  pour  recevoir  la  ligne  saisie,  getline()  redimensionne le *
                                         * tampon afin de s'adapter grace a realloc(), mettant a jour *lineptr et *n comme *
                                         * il se doit. Quoi qu'il en soit, en cas de succes, *lineptr et *n seront adaptes *
                                         * afin de rendre compte respectivement de l'adresse et de la taille du tampon.    *
                                         * getdelim()  fonctionne  comme getline(), si ce n'est qu'un separateur different *
                                         * de  saut-de-ligne  peut etre specifie en tant qu'argument delimiter. Tout comme *
                                         * avec  getline(),  aucun  separateur  n'est  ajoute  s'il  n'y en avait pas dans *
                                         * l'entree avant que la fin du fichier ne soit atteinte.                          *
                                         ***********************************************************************************/
                                        /*#ifndef _SSIZE_T_
                                        #define _SSIZE_T_
                                        typedef long ssize_t; * signed size_t *
                                        #endif*/
                                        
                                        #define BUF_SIZE (256) /* 1 << 8  */
                                        #define DEBUG 1
                                        
                                        struct buffer_s {
                                            char * string;
                                            size_t buf_length;
                                        };
                                        
                                        struct buffer_s
                                        create_buffer (char * bufptr, int n) {
                                            struct buffer_s buf = {NULL, 0};
                                        
                                            if (bufptr == NULL) {
                                                errno = 0;
                                                if ((buf.string = calloc(BUF_SIZE, sizeof *(buf.string))) == NULL) {
                                                    puts(strerror(errno));
                                                }
                                            } else {
                                                buf.string = bufptr;
                                            }
                                        
                                            buf.buf_length = (bufptr) ? n : BUF_SIZE;
                                        
                                            return buf;
                                        }
                                        
                                        int
                                        buffer_append (struct buffer_s * buf, char * content) {
                                            size_t content_len;
                                            size_t buf_len;
                                        
                                            if (!buf || !content)
                                                return -1;
                                        
                                            content_len = strlen(content);
                                            buf_len     = strlen(buf->string);
                                            errno       = 0;
                                        
                                            if (content_len == 0)
                                                return -1;
                                        
                                            if (buf_len + content_len + 1 > buf->buf_length) {
                                                char * new_lineptr = NULL;
                                                long needed = buf->buf_length + 2 * content_len + 1;
                                        
                                                if (needed < 0) {
                                                    errno = ERANGE;
                                                    return -1;
                                                }
                                        
                                                if ((new_lineptr = realloc(buf->string, needed)) == NULL)
                                                    return -1;
                                        
                                                buf->string     = new_lineptr;
                                                buf->buf_length = needed;
                                            }
                                        
                                            /* /!\ strncat ajoute un '\0' en plus des 'content_len` caracteres
                                              si celui-ci n'est pas present. */
                                        
                                            strncat(buf->string, content, content_len);
                                        
                                            return errno;
                                        }
                                        
                                        int
                                        buffer_fix (struct buffer_s * buf) {
                                            char * new_lineptr = NULL;
                                            size_t str_len;
                                        
                                            if (buf == NULL)
                                                return -1;
                                        
                                            errno = 0;
                                            str_len = strlen(buf->string);
                                        
                                            if ((new_lineptr = calloc(str_len + 1, sizeof *(buf->string))) == NULL)
                                                return errno;
                                        
                                            strncpy(new_lineptr, buf->string, str_len);
                                            new_lineptr[str_len] = '\0'; /* Useless */
                                        
                                            free(buf->string), buf->string = NULL;
                                        
                                            buf->string = new_lineptr;
                                            buf->buf_length = str_len+1;
                                        
                                            return errno;
                                        }
                                        
                                        ssize_t _getline(char **lineptr, size_t *n, FILE *stream);
                                        ssize_t _getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
                                        
                                        ssize_t
                                        _getdelim(char **lineptr, size_t *n, int delim, FILE *stream) {
                                            struct buffer_s buffer;
                                            long nb_car;
                                            int c, i;
                                            char tmp[BUF_SIZE+1] = {0};
                                        
                                            if (!lineptr || !n || !stream) {
                                                errno = EINVAL;
                                                return -1;
                                            }
                                        
                                            errno = 0;
                                            buffer = create_buffer(*lineptr, *n);
                                            nb_car = 0;
                                        
                                            if (errno == ENOMEM)
                                                return -1;
                                        
                                            for (i = 0 ; (c = getc(stream)) != delim && c != EOF ; ++nb_car, i = (i + 1) % BUF_SIZE) {
                                                int err = 0;
                                        
                                                if (i == 0)
                                                    memset(tmp, 0, BUF_SIZE);
                                                else if (i == BUF_SIZE - 1)
                                                    err = buffer_append(&buffer, tmp);
                                        
                                                    tmp[i] = c;
                                        
                                                if (err)
                                                    return -1;
                                            }
                                        
                                            if (buffer_append(&buffer, tmp))
                                                return -1;
                                        
                                            if (c == EOF)
                                                return -1;
                                            if (c == delim) {
                                                char tmp[2] = {0};
                                                tmp[0] = delim;
                                                buffer_append(&buffer, tmp);
                                            }
                                        
                                            /* '\0' est present de base a la fin du buffer */
                                            if (buffer_fix(&buffer))
                                                return -1;
                                        
                                            *lineptr = buffer.string;
                                            *n = strlen(*lineptr);
                                        
                                            return nb_car;
                                        }
                                        
                                        ssize_t
                                        _getline(char **lineptr, size_t *n, FILE *stream) {
                                            return _getdelim(lineptr, n, '\n', stream);
                                        }
                                        
                                        #if DEBUG
                                        int
                                        main(int argc, char * argv[]) {
                                             FILE * fp;
                                             char * line = NULL;
                                             size_t len = 0;
                                             ssize_t read;
                                            if (argc < 2)
                                                return EXIT_FAILURE;
                                             fp = fopen(argv[1], "r");
                                             if (fp == NULL)
                                                 return EXIT_FAILURE;
                                        
                                             while ((read = _getline(&line, &len, fp)) != -1) {
                                                /* printf("Retrieved line of length %zu :\n", read);*/
                                             }
                                             printf("%s", line);
                                        
                                             if (line)
                                                 free(line);
                                             return EXIT_SUCCESS;
                                        }
                                        #endif
                                        


                                        En cas de désaccord sur un point quelconque, n'hésitez pas à intervenir.

                                        Bonne soirée.
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                        Anonyme
                                          6 avril 2011 à 20:51:56

                                          Merci pour tout Lithrein :)
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            6 avril 2011 à 21:52:25

                                            Merci pour la correction. :)
                                            Quelques remarques :

                                            Dans create_buffer :
                                            Pourquoi set errno à ENOMEM après avoir affiché sa valeur ? surtout que si calloc échoue, elle mettra errno à ENOMEM. :-°
                                            Tu aurais pu les mettre sur 2 lignes plutôt que tout mettre dans le if, ça aurait éclairci le code. ;)

                                            if ((buf.string = calloc(BUF_SIZE, sizeof *(buf.string))) == NULL) {
                                                        puts(strerror(errno));
                                                        errno = ENOMEM;
                                                    }
                                            

                                            Tu pourrais aussi préciser que la fonction ne copie pas la chaîne de caractère passée en argument et que (c'est essentiel à mon goût) il faut que la chaîne passée en argument soit allouée dynamiquement et non statiquement. Je pense que le mieux serait d'allouer la mémoire quoi qu'il arrive et de recopier la chaîne passée en argument. :)

                                            #define BUF_SIZE (1 << 8) /* 256 */ -> Ca ne sert à rien (à part nuire à la lisiblité et éventuellement au temps d'exécution), pourquoi ne pas mettre 256 directement ? :-°
                                            Tu dis 'Soit, le pointeur n'est pas nul, dans ce cas, on ajuste à calculer la taille de la chaine' -> Ce qui vaudrait dire que tu appelles strlen, or là tu utilises 'n'. :)
                                            Tu pourrais retourner un pointeur, ça permettrait d'avoir un code plus flexible (et de gérer des erreurs en renvoyant NULL si l'allocation à fail, pas d'utiliser errno ;) ).
                                            Inutile: Pas besoin d'initialiser ta variable.

                                            Dans buffer_append :
                                            Pourquoi tu t'amuses avec errno ? Ca ne sert à rien, c'est une variable globale. :-° Tu pourrais t'en servir pour afficher une erreur et renvoyer -1 si la fonction échoue.
                                            J'ai pas bien saisi la subtilité de cette partie, pourquoi t'amuser avec errno ?

                                            errno       = 0;
                                            
                                                if (content_len == 0)
                                                    return (int) errno;
                                            
                                            if (needed < 0)
                                                        return (int) (errno = ERANGE);
                                            


                                            Dans ta fonction getdelim :
                                            Ca c'est faux : buffer_append(&buffer, (char *) &delim); delim n'est pas une chaîne de caractères. Que donne strlen((char*)&delim) ?

                                            Les identificateurs commençant par la lettre majuscule E suivis d'un nombre ou d'une lettre majuscule sont des identificateurs réservés.
                                            J'ai un warning à la ligne 144 : main.c: warning: Semantic Issue: Implicit conversion loses integer precision: 'size_t' (aka 'unsigned long') to 'int'

                                            Tu joues trop avec errno, tu ne peux pas affecter de valeurs comme ça arbitrairement. Qui te dit que les valeurs 12, 22, ou 34 ne sont pas utilisées par une autre variable d'erreur ?
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              6 avril 2011 à 22:57:16

                                              Tout d'abord, je vous remercie du temps que vous avez pris à me lire et à relever des "coquilles".

                                              Citation : Pouet_forever

                                              Dans create_buffer :
                                              Pourquoi set errno à ENOMEM après avoir affiché sa valeur ? surtout que si calloc échoue, elle mettra errno à ENOMEM. :-°
                                              Tu aurais pu les mettre sur 2 lignes plutôt que tout mettre dans le if, ça aurait éclairci le code. ;)

                                              if ((buf.string = calloc(BUF_SIZE, sizeof *(buf.string))) == NULL) {
                                                          puts(strerror(errno));
                                                          errno = ENOMEM;
                                                      }
                                              




                                              J'avoue que sur le coup ce n'est pas très futé. Si je me souviens bien la raison pour laquelle j'avais affiché la valeur de errno puis l'avait fixée à ENOMEM était le fait que je voulais afficher la valeur que met calloc dans errno quand elle échoue puis de mettre errno à ENOMEM. (Puisque par flemme je n'étais pas aller voir le manuel et donc je n'avais pas vu que calloc met errno à ENOMEM en cas d'échec.)

                                              Ensuite, pour le tout dans le if, c'est plus une question d'habitude, et je ne trouve pas que cela nuise vraiment à la lisibilité. Enfin, si d'autres personnes s'en indisposent, je mettrai sur deux lignes.

                                              Citation : Pouet_forever


                                              Tu pourrais aussi préciser que la fonction ne copie pas la chaîne de caractère passée en argument et que (c'est essentiel à mon goût) il faut que la chaîne passée en argument soit allouée dynamiquement et non statiquement. Je pense que le mieux serait d'allouer la mémoire quoi qu'il arrive et de recopier la chaîne passée en argument. :)



                                              Sur ce point, je suis entièrement d'accord avec toi. Je vais voir si je précise que la chaine en argument doit être alloué dynamiquement ou si je la recopie (ce qui peut être long pour de grands buffers, mais bon les performances ne sont pas vraiment recherchées de toutes façons).

                                              Citation : Pouet_forever

                                              #define BUF_SIZE (1 << 8) /* 256 */ -> Ca ne sert à rien (à part nuire à la lisiblité et éventuellement au temps d'exécution), pourquoi ne pas mettre 256 directement ? :-°
                                              Tu dis 'Soit, le pointeur n'est pas nul, dans ce cas, on ajuste à calculer la taille de la chaine' -> Ce qui vaudrait dire que tu appelles strlen, or là tu utilises 'n'. :)
                                              Tu pourrais retourner un pointeur, ça permettrait d'avoir un code plus flexible (et de gérer des erreurs en renvoyant NULL si l'allocation à fail, pas d'utiliser errno ;) ).
                                              Inutile: Pas besoin d'initialiser ta variable.



                                              1. Le #define BUF_SIZE (1 << 8), c'est essentiellement car j'aime bien voir directement que le nombre est une puissance de 2. Cela peut peut-être nuire à la lisibilité, certes mais cela n'a aucune influence sur le temps d'exécution.
                                              2. Pour le calcul de 'n', il est vrai que ma phrase décrit plutôt mal ce qui se passe, je la rectifierais donc en conséquence.
                                              3. Il est vrai que renvoyer errno est déplus inutile, je vais voir si je modifie.

                                              Citation : Pouet_forever

                                              Dans buffer_append :
                                              Pourquoi tu t'amuses avec errno ? Ca ne sert à rien, c'est une variable globale. :-° Tu pourrais t'en servir pour afficher une erreur et renvoyer -1 si la fonction échoue.
                                              J'ai pas bien saisi la subtilité de cette partie, pourquoi t'amuser avec errno ?

                                              errno       = 0;
                                              
                                                  if (content_len == 0)
                                                      return (int) errno;
                                              
                                              if (needed < 0)
                                                          return (int) (errno = ERANGE);
                                              


                                              Il me semble qu'il y avait une raison mais depuis décembre, j'ai oublié laquelle. Là sur le coup, je dirais que renvoyer errno permet juste d'être plus explicite sur le traitement des erreurs dans la fonction appelante mais c'est vraiment discutable.

                                              Citation : Pouet_forever


                                              Dans ta fonction getdelim :
                                              Ca c'est faux : buffer_append(&buffer, (char *) &delim); delim n'est pas une chaîne de caractères. Que donne strlen((char*)&delim) ?



                                              Non, cette "astuce" fonctionne très bien (tout du moins avec gcc) :
                                              #include <stdio.h>
                                              #include <stdlib.h>
                                              #include <string.h>
                                              
                                              int
                                              main (void) {
                                                  char delim = '\n';
                                                  printf("%d", (int) strlen((char *) &delim));
                                              
                                                  return 0;
                                              }
                                              


                                              1


                                              Je compile avec gcc -Wall -Wextra -Wmain -pedantic

                                              Citation : Pouet_forever

                                              J'ai un warning à la ligne 144 : main.c: warning: Semantic Issue: Implicit conversion loses integer precision: 'size_t' (aka 'unsigned long') to 'int'



                                              Je corrigerai.

                                              Citation : Pouet_forever

                                              Les identificateurs commençant par la lettre majuscule E suivis d'un nombre ou d'une lettre majuscule sont des identificateurs réservés.

                                              Tu joues trop avec errno, tu ne peux pas affecter de valeurs comme ça arbitrairement. Qui te dit que les valeurs 12, 22, ou 34 ne sont pas utilisées par une autre variable d'erreur ?



                                              Les identificateurs déclarés avec #define sont en effet réservés et je les redéfini au cas où, il ne le soient pas.

                                              Pour les valeurs, donnée aux constantes E*, je n'ai pas inventé les valeurs, je les ai trouvé conjointement dans un livre sérieux sur le C et dans les fichiers headers de la lib standard.

                                              Je corrigerai ce qui me semble nécessaire demain.

                                              Sur ce,
                                              Bonne soirée.
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                6 avril 2011 à 23:42:38

                                                Citation : Lithrein

                                                Ensuite, pour le tout dans le if, c'est plus une question d'habitude, et je ne trouve pas que cela nuise vraiment à la lisibilité.


                                                Ce n'est qu'un avis personnel. ;)

                                                Citation : Lithrein

                                                1. Le #define BUF_SIZE (1 << 8), c'est essentiellement car j'aime bien voir directement que le nombre est une puissance de 2. Cela peut peut-être nuire à la lisibilité, certes mais cela n'a aucune influence sur le temps d'exécution.


                                                Ca dépend, si ton compilateur remplace 1<<8 par 256 ça n'aura pas d'influences, par contre s'il laisse 1<<8, ça fait des calculs inutiles (infimes, certes, mais inutiles). :)

                                                Citation : Lithrein

                                                Non, cette "astuce" fonctionne très bien (tout du moins avec gcc) :

                                                #include <stdio.h>
                                                #include <stdlib.h>
                                                #include <string.h>
                                                
                                                int
                                                main (void) {
                                                    char delim = '\n';
                                                    printf("%d", (int) strlen((char *) &delim));
                                                
                                                    return 0;
                                                }
                                                

                                                Mouais, pas vraiment convaincu. Tu peux me trouver des endroits où ils appuient ta méthode ?

                                                Citation : Lithrein

                                                Je compile avec gcc -Wall -Wextra -Wmain -pedantic


                                                Je compile en 64 bits. :-°

                                                Citation : Lithrein

                                                Pour les valeurs, donnée aux constantes E*, je n'ai pas inventé les valeurs, je les ai trouvé conjointement dans un livre sérieux sur le C et dans les fichiers headers de la lib standard.


                                                Rien de standard, c'est peut-être souvent (toujours ?) la même chose sur unix, mais connaissant windows, je n'en suis pas persuadé. :-°

                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  7 avril 2011 à 8:41:57

                                                  Citation : Pouet_forever


                                                  Ca dépend, si ton compilateur remplace 1<<8 par 256 ça n'aura pas d'influences, par contre s'il laisse 1<<8, ça fait des calculs inutiles (infimes, certes, mais inutiles). :)



                                                  Je vois souvent cette notation (notamment pour les flags), et elle ne me dérange pas plus que ça.
                                                  Et j'aimerais bien savoir, quel compilateur ne calcul pas lui même toutes les valeurs constantes...


                                                  #include <stdio.h>
                                                  #include <stdlib.h>
                                                  #include <string.h>
                                                  
                                                  int
                                                  main (void) {
                                                      char delim = '\n';
                                                      printf("%d", (int) strlen((char *) &delim));
                                                  
                                                      return 0;
                                                  }
                                                  


                                                  Qu'est-ce qui te garanti qu'il y a nécessairement un caractère nul, après le '\n' ?
                                                  (Bien souvent, le compilateur aligne les char sur 4 octets pour des raisons de performances, mais je suis presque sûr qu'on peut lui demander de ne pas le faire, et rien ne dit qu'il met des nul bytes...)

                                                  D'ailleurs, chez moi, le résultat est 13. (Autant dire que je déborde bien :) )


                                                  • Partager sur Facebook
                                                  • Partager sur Twitter

                                                  Exercices pour débutants en C (suite)

                                                  × 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