IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Realloc() - Segmentation fault & clean_stdin


Sujet :

C

  1. #1
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut Realloc() - Segmentation fault & clean_stdin
    Bonjour à tous,

    Programme en général :

    Le programme que j'ai écrit est un programme de jeux Pile ou face. L'utilisateur rentre p ou f et grace aux fonctions "aléatoires" du C on génére pile ou face.

    Problème realloc() : [RESOLU]

    On nous a demandé d'optimiser l'espace mémoire, on doit stocker le tirage et les propositions du pile ou face, ce que je fais grâce a un pointeur. J'utilise malloc() au départ :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    /* Allocation de tab_tirage et tab_proposition d'une taille mémoire d'un char.
    */
    tab_tirage =  malloc( sizeof(char));	// Le retour du malloc pourrait être caster (void*) mais c'est inutile.
    	if (tab_tirage == NULL)
    	{
    		printf("MALLOC : Erreur d'allocation\n");
    		exit(1);
    	}
    tab_proposition =  malloc( sizeof(char));
    	if (tab_proposition == NULL)
    	{
    		printf("MALLOC : Erreur d'allocation\n");
    		exit(1);
    	}
    Et a chaque fois que l'utilisateur re-lance une pièce je realloc ces deux vecteurs d'une case :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
     
    /* 
    A chaque fois que l'utilisateur lance une nouvelle piece on allonge notre vecteur de 1 case (de taille char) pour stocker le résultat. 
    */
     
    void alloueMemoire(char*tab_tirage,char *tab_proposition){
     
    /* /!\ realloc() renvoie un nouveau pointeur, problème ?
    */
     
    	tab_tirage = realloc((char*)(tab_tirage),cpt_lancePiece* sizeof(char));	
    	if (tab_tirage == NULL)
    	{	
    		printf("REALLOC : Erreur d'allocation\n");
    		exit(1);
    	}
     
    	tab_proposition = realloc((char*)(tab_proposition),cpt_lancePiece* sizeof(char));
    	if (tab_proposition == NULL)
    	{
    		printf("REALLOC : Erreur d'allocation\n");
    		exit(1);
    	}
     
    }
    J'ai consulté de nombreux forums d'aide et ils donnaient des solutions diverses et variées mais qui n'ont pas résolu mon problème.
    Ha oui... le problème justement, c'est que au bout de 24 lancé (virtuelle) de pièce, par conséquent au bout de 24 realloc() j'ai une erreur de segmentation.
    Il parait que ce n'est pas trop conseillé de reallocé autant mais bon sinon que faire ? Mais ce qui m'interresse c'est surtout de savoir pourquoi ca ne va pas.

    Solution : Quelques améliorations dans la fonction realloc en cas de cr@sh ! Mon problème se situait au niveau de la variable pieceLance qui ne valait, lors du premier passage, 1. Donc la fonction re-alloc ré-allouait seulement 1 case (inutile & inefficace).


    Problème 2 : Vider le buffer clavier.
    [RESOLU]

    Ce n'est pas vraiment un problème plutot quelque chose d'agacant.
    A un moment donné je demande à l'utilisateur d'entré P ou F :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
     
    	char buffer=0;		// Variable locale à la fonction lancePiece.	
     
    // Vérification de l'entrée de l'utilisateur.
     
    	while (buffer != 70 && buffer != 80 && buffer != 102 && buffer != 112 )
    	{
    		printf("Quelle est votre proposition ? <Pour pile taper p ou f pour face> : ");
    		clean_stdin();
    		buffer = getchar();
    	}
    Fonction clean_stdin(), venant de la faq de ce forum.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
     
    void clean_stdin(void)
    {
        int c;
     
        do {
            c = getchar();
        } while (c != '\n' && c != EOF);
    }
    Lorsque l'utilisateur se trompe, n'entre rien et clique directement sur enter, lorsqu'il va essayer de re-entré p ou f il devra l'entré 2 fois de cette facon :

    p <enter> p <enter>

    Comment puis-je arranger ca ?

    Solution : Nouvelle fonction LireClavier();
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    int LireClavier(void)
    {
      int car,c;
      car = c = getchar();
      while (c != '\n' && c != EOF)c = getchar();
      return car;
    }
    Merci d'avoir lu mon post et de vos prochaines aides =)

    Bye bye

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    Lorsque tu appelles la fonction alloueMemoire, elle te crée localement deux pointeurs, qu'elle initialise avec les valeurs respectives des deux pointeurs que tu lui passes.
    Lorsque ces deux pointeurs sont modifiés au sein de la fonction, ils sont détruits lorsqu'elle se termine.
    Par conséquent, non seulement les deux pointeurs, dans la fonction appelante, ont une valeur inchangée qui, à cause de l'appel à realloc, correspondent alors ensuite à une adresse dont l'accès devient interdit (qui provoquera donc un crash lors d'un accès à cette adresse), mais cela provoque aussi à une fuite de mémoire car les nouvelles adresses renvoyées par realloc sont alors perdues.

    Pour modifier ces pointeurs via une autre fonction, il faut passer l'adresse de ces pointeurs et non leur valeur.
    Ta fonction alloueMemoire doit donc ressembler à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void alloueMemoire(char const ** const tab_tirage,char const **const tab_proposition)
    {
    (...) /* code à changer */
    }
     
    (...) /* plus loin dans une autre fonction */
    alloueMemoire(&tab_tirage, &tab_proposition);
    Pour résumer :
    tab_tirage correspond à l'adresse où est stocké en mémoire le pointeur de la fonction appelante (celui qui est passé en argument).
    *tab_tirage correspond à sa valeur (celle qu'on veut changer "à distance").
    **tab_tirage correspond à l'objet pointé (le char).


    Sinon, deux, trois petits trucs aussi.
    1) Il faut faire attention à realloc. Si tu écrases le pointeur, sans faire une copie, et que la fonction échoue, tu as une fuite de mémoire. Si cette fonction échoue, l'allocation existe toujours et rien n'est donc libéré.

    2) A chaque malloc, il doit y avoir un free correspondant. Il faut donc structurer le code de manière à libérer proprement les zones de mémoire allouées si un des malloc échoue, ou lorsque le programme se termine normalement.
    En gros : exit, les exit. ^^

  3. #3
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Pour le realloc() :

    Le realloc peut changer l'adresse où se trouve les données et il faut donc la récupérer. Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void alloueMemoire(char** ptirage,char ** pproposition)
    {
            char * p;
    	p= realloc(*ptirage,cpt_lancePiece);	
    	if (p == NULL)
    	{	
            ....
    	}
            else * ptirage = p;
    .....
    }
    .....
    alloueMemoire( &tab_tirage, &tab_proposition);
    - cpt_lancePiece est une variable globale ? Ce n'est pas bien ! Ce devrait être un paramètre de la fonction.

    - A noter qu'on peut aussi allouer initialement par cette fonction en mettant tab_tirage et tab_proposition à NULL.



    Pour le vidage clavier :

    La fonction clean_stdin() suppose que le buffer n'est pas vide et donc qu'il faut le vider. Si il est vide, on va faire des lectures pour rien ici
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        do {
            c = getchar();
        } while (c != '\n' && c != EOF);

  4. #4
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Je vous remercie de vos réponses et je suis parvenu à régler le problème 2.
    Pour ce qui est du problème 1, celui du realloc, j'en suis au point de départ et je n'ai pas compris certaines choses dans vos messages.
    J'ai tout de même modifier mon code qui ressemble à ça maintenant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
     
    // Prototype : 
     
    void alloueMemoire(char**,char**);
     
    /* 
    A chaque fois que l'utilisateur lance une nouvelle piece on allonge notre vecteur de 1 case (de taille char) pour stocker le résultat. 
    */
     
    void alloueMemoire(char**ptirage,char**pproposition){
     
    	char *p;
    	p = realloc(*ptirage,cpt_lancePiece/** sizeof(char)*/);	
    	if (p == NULL)
    	{	
    		printf("REALLOC : Erreur d'allocation\n");
    		free(ptirage);
    		free(pproposition);
    		exit(1);
    	}
    	else *ptirage = p;
     
    	p = realloc(*pproposition,cpt_lancePiece/** sizeof(char)*/);
    	if (p == NULL)
    	{
    		printf("REALLOC : Erreur d'allocation\n");
    		free(ptirage);
    		free(pproposition);
    		exit(1);
    	}
    	else *pproposition = p;
    }
    Bon voilà je pense avoir suivi vos conseils , seulement c'est toujours au bout de 24 lancées que j'ai une segmentation fault.

    Citation Envoyé par diogene Voir le message
    cpt_lancePiece est une variable globale ? Ce n'est pas bien ! Ce devrait être un paramètre de la fonction.
    Tu me dis ça mais je ne comprends pas pourquoi c'est mal ? Rajouter un paramètre n'alourdis t'il pas la fonction ? Et n'est-ce pas plus court de mettre cette variable en globale qui est utilisée dans d'autres fonctions ?
    Elle est déclarée comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    int static cpt_lancePiece;
    Je sais que tu as raison et qu'une variable globale c'est vraiment moche mais je ne vois pas pourquoi je devrais m'en passer.

    Citation Envoyé par jeroman Voir le message
    void alloueMemoire(char const ** const tab_tirage,char const **const tab_proposition)
    Je ne comprends pas très bien pourquoi tu mets des const :/
    Lorsque je les rajoute j'obtient une erreur de compilation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    PileouFace.c:192: error: conflicting types for ‘alloueMemoire’
    PileouFace.c:49: note: previous declaration of ‘alloueMemoire’ was here
    PileouFace.c: In function ‘alloueMemoire’:
    PileouFace.c:197: warning: passing argument 1 of ‘realloc’ discards qualifiers from pointer target type
    /usr/include/stdlib.h:485: note: expected ‘void *’ but argument is of type ‘const char *’
    PileouFace.c:207: warning: passing argument 1 of ‘realloc’ discards qualifiers from pointer target type
    /usr/include/stdlib.h:485: note: expected ‘void *’ but argument is of type ‘const char *’
    Citation Envoyé par jeroman Voir le message
    Si tu écrases le pointeur, sans faire une copie, et que la fonction échoue, tu as une fuite de mémoire
    Je pense que je corrige ça grâce au nouveau pointeur p, n'est-ce pas ?

    Citation Envoyé par jeroman Voir le message
    A chaque malloc, il doit y avoir un free correspondant. Il faut donc structurer le code de manière à libérer proprement les zones de mémoire allouées si un des malloc échoue, ou lorsque le programme se termine normalement.
    En gros : exit, les exit. ^^
    Ma manière marche t'elle ? C'est-à-dire faire deux free() avant le exit(1) ?

    Citation Envoyé par diogene Voir le message
    p= realloc(*ptirage,cpt_lancePiece);
    Comme j'ai pu constater tu ne mets que le coefficiant multiplicateur de la taille, doit-on obligatoirement ajouter un : * sizeof(char) ? Est-ce que c'est sous-entendu selon le type de l'ancien (ou nouveau?) pointeur ? Ou alors est-ce simplement parce que realloc attends une taille en octet et que le type char ne fait de toutes facons que 1 octet ?


    Je suis désolé, ca fait encore un post à rallonge mais enfin tant que j'y suis...

    Merci à vous,
    Tchao

  5. #5
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    - Erreur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void alloueMemoire(char**ptirage,char**pproposition){
    ....
    		free(*ptirage);
    		free(*pproposition);
    -
    seulement c'est toujours au bout de 24 lancées que j'ai une segmentation fault.
    Il faut voir le reste du code

    -
    Et n'est-ce pas plus court de mettre cette variable en globale qui est utilisée dans d'autres fonctions ?
    ....
    mais je ne vois pas pourquoi je devrais m'en passer
    Parce que tu peux t'en passer. Les variables globales ne doivent pas être utilisées simplement pour s'épargner un passage de paramètre. Elles sont néfastes pour la lecture et la maintenance du code parce que :
    1- les fonctions cessent d'être une entité autonome et dépendent de quelque chose qui leur est étranger.
    2- il est difficile lorsque le code devient un peu copieux de savoir qui les modifie et quand. Si il y a une valeur erronée dedans, qui la mise et quand ? Le debug risque d'être coton.

    -
    Je pense que je corrige ça grâce au nouveau pointeur p, n'est-ce pas ?
    Oui
    -
    Ma manière marche t'elle ? C'est-à-dire faire deux free() avant le exit(1) ?
    Mais tu ne fais pas les free() des bons pointeurs (voir début du post)

    -
    Ou alors est-ce simplement parce que realloc attends une taille en octet et que le type char ne fait de toutes facons que 1 octet ?
    Oui. sizeof(char) vaut toujours 1 (byte)

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    Je ne comprends pas très bien pourquoi tu mets des const :/
    Généralement, il est préférable de mettre des 'const' partout où l'on peut (ce qui veut bien dire qu'il ne faut pas en mettre sur des objets qu'on veut modifier), afin d'éviter de modifier accidentellement des variables (ou des pointeurs) que la fonction ne doit pas modifier. Modifier une valeur qui est 'const' provoque une erreur à la compilation, c'est donc sécuritaire.
    Mais avec le premier 'const', il y a effectivement un binz que je ne comprends pas. Je replongerai dans la norme demain, il y a quelque chose qui n'est pas clair.

    Ma manière marche t'elle ? C'est-à-dire faire deux free() avant le exit(1) ?
    Oui, 'free', c'est bien... mais 'free' sans 'exit', c'est encore mieux. Enfin, c'est mon avis à moi.
    Le problème de 'exit', c'est qu'il termine le programme sauvagement alors qu'en principe un programme doit être suffisamment bien structuré pour qu'il gère lui-même les erreurs qui pourraient se produire (fonctions qui échouent, etc) et que par conséquent il libère lui-même tout ce qu'il a alloué, même si l'O.S. le fait lui-même. C'est juste une question de principe, qui permet de coder proprement.
    C'est notamment la raison pour laquelle je te conseillerais de faire retourner une valeur à ta fonction d'allocation, afin que le code appelant puisse lui-même gérer en cas d'erreur.

    Voilà un code (une ébauche, qui ne sert pas à grand-chose en l'état, mais qu'il faudra compléter en fonction de ce que tu veux faire) que je te propose, il y a peut-être moyen de faire mieux :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    #include<stdio.h>
    #include<stdlib.h>
     
    #define NOMBRE_COUPS_MAXI 50
     
    int alloueMemoire(char ** const , char ** const , size_t const );
     
    int alloueMemoire(char ** const ptirage , char ** const pproposition , size_t const cpt_lancePiece)
    {
    	char *p;
     
    	p = realloc( *ptirage , cpt_lancePiece ); /* Si le 1er parametre de 'realloc' vaut NULL, alors la fonction se comporte comme 'malloc' */
    	if (p == NULL)
    	{	
    		printf("REALLOC : Erreur d'allocation\n");
    		free(*ptirage) , *ptirage = NULL;
    		free(*pproposition) , *pproposition = NULL;
    		return 0;
    	} else
    		*ptirage = p;
     
    	p = realloc( *pproposition , cpt_lancePiece ); /* ... et ici aussi, forcément. */
    	if (p == NULL)
    	{
    		printf("REALLOC : Erreur d'allocation\n");
    		free(*ptirage) , *ptirage = NULL;
    		free(*pproposition) , *pproposition = NULL;
    		return 0;
    	} else
    		*pproposition = p;
     
    	return 1;
    }
     
    int main(void)
    {
    	size_t cpt_lancePiece = 0;
    	char * ptirage = NULL , * pproposition = NULL;
    	int success;
     
    	/* on boucle 'NOMBRE_COUPS_MAXI' fois, sauf si une erreur survient avant */
    	while ( ++cpt_lancePiece <= NOMBRE_COUPS_MAXI &&  (success = alloueMemoire ( &ptirage , &pproposition , cpt_lancePiece )) )
    	{
     
    		/* ... ajouter du code ici (stocker en mémoire, dans la mémoire allouée, le dernier coup joué (pile ou face), etc) ... */
     
    		printf("%d> %p %p\n", cpt_lancePiece , ptirage , pproposition); /* inutile, affiche simplement la valeur des pointeurs à chaque appel de 'alloueMemoire' */
    	}
     
    	free (ptirage) , ptirage = NULL; /* il n'y a aucun risque à utiliser 'free' même avec une valeur NULL en paramètre (au cas où le pointeur aurait été remis à NULL juste avant) */
    	free (pproposition) , pproposition = NULL;
     
    	getchar(); /* évite au programme de refermer sa fenêtre immédiatement, cela permet de lire les résultats */
     
    	return 0;
    }

  7. #7
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Alors là je dois avouer que je suis perdu, à chaque fois que j'ai re-posté j'ai essayer de faire marcher ce programme pendant bien 2 heures.
    J'ai fini par passer 3 variables sur 5 en paramètre et là c'est encore plus la galère pour comprendre (je pense?) et le problème des realloc n'est toujours pas résolu, bref je suis un .

    Bon en désespoir de cause je poste tout mon code qui a toujours le même problème. J'ai néanmoins suivis vos conseils, j'ai passé 3 de mes variables globales en paramètres, j'ai changé ma fonction realloc.

    J'ai également pas mal commenté ce programme, c'est le premier programme que je commente autant donc si vous avez des conseils, ou si il est mal commenté etc. Hésitez pas a me descendre J'espere quand même qu'il le sera assez parce que je pense que ca ne doit pas être facile de relire quelque chose de sale. Vu que c'est la première fois que je poste un code en entier, n'hésitez surtout pas à me dire ce qui ne va pas dans ma manière de coder.

    Merci beaucoup, vos réponses m'ont déjà été d'une grande aide.

    EDIT : si ca peut vous aider :

    OS : Linux
    compilateur : gcc
    éditeur : VIM

  8. #8
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Globalement, je trouve ton code difficile à lire sans pouvoir déterminer ce qui cloche vraiment dans sa présentation (trop de commentaires inutiles pour moi ? indentation mal respectée parfois ? lignes blanches en excès qui brisent la continuité de la lecture ?...)

    Passons au code proprement dit :

    *
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      switch (menu){
        if(firstGame == 0){	// L'utilisateur ne peut commencer une nouvelle partie que si il a déjà lancé une pièce.
           case 'n':
           case 'N':
    Le if est mal placé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     switch (menu){
           case 'n':
           case 'N':
              if(firstGame == 0){	// L'utilisateur ne peut commencer une nouvelle partie que si il a déjà lancé une pièce.
              ....
              }
              break;

    * Ta gestion clavier laisse à désirer : ton principe est le suivant : purge du clavier puis lecture d'un char. C'est difficile à gérer parce la purge dépend de ce qui a été fait AVANT sur le clavier. Il est préférable de lire puis de remettre ensuite le clavier tout propre en le purgeant. Le clavier est alors toujours dans un état connu. Comme tu ne lis que des char, tu peux grouper le tout :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int LireClavier(void)
    {
      int car,c;
      car = c = getchar();
      while (c != '\n' && c != EOF)c = getchar();
      return car;
    }
    * srand() peut être avantageusement placé au début du main ce qui élimine la globale firstRand
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int my_rand (void){
       return rand() / (double)RAND_MAX * (3 - 1);
    }
    * Enfin et surtout, ta gestion du nombre de char à allouer est fausse :
    - Au départ tu as 1 élément d'alloué et pieceLance == 0
    - Après le premier lancé, pieceLance==1 et tu réalloue 1 élément, donc rien de plus.

    Tu dois réallouer pieceLance+1 éléments.

  9. #9
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Juste une émote :

    Voila mon programme est presque finis et presque parfait grâce à vous

    Il me reste encore un petit problème sur les spécificateurs de format, par exemple si on veut afficher 100.00 ou 5.25 mais reserver le même espace pour les deux. Je serais tenté de faire comme ça dans mon printf : %3.2f. Et non ca ne marche pas.

    Je poste mon programme que j'ai re-modifier notamment dans les commentaires et tout ce qui n'allait pas. Dites moi si il est toujours aussi illisible.
    Si vous avez le temps je serais curieux de savoir quelle est la note que vous lui donner

    Bon me reste plus qu'à faire la version pour Windows, allez hop au travail.

    Merci du temps que vous m'avez accordé.

  10. #10
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    C'est beaucoup mieux, à mon avis.

    Quelques détails :

    * au lieu d'utiliser malloc au début, tu peux sans problèmes utiliser ta fonction alloueMemoire() après avoir initialisé les pointeurs à NULL :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int main (void)
    {
    ....
       int pieceLance=0;
    ....
       char *tab_proposition = NULL;
       char *tab_tirage = NULL;
       int quit = 0;	// Variable "booléene" permettant l'arrêt du programme si quit = 1.
       srand(time(NULL));
       alloueMemoire(&tab_tirage, &tab_proposition, pieceLance);
       while (quit == 0)
       {....
    * Dans le case 'n' ou 'N' et 'q' ou'Q' , tu ne réinitialise explicitement pas la mémoire allouée. En fait, elle est réinitialisée dans afficheScore(), donc c'est bon mais ça manque un peu d'évidence. Il n'est pas de bonne pratique qu'une fonction fasse plusieurs choses, comme ici l'affichage comme son nom l'indique et réinitialise le jeu comme son nom ne permet pas du tout de le deviner. Il vaut mieux faire deux fonctions. D'ailleurs, toi-même tu sembles incertain sur ce que fait exactement la fonction puisque tu réinitialises pieceLance et pieceLanceJuste à la fois dans le case et dans la fonction.

    * Dans lancePiece(), au lieu d'utiliser les valeurs magiques 70, 80,... utilise directement 'F', 'P',... C'est plus clair !
    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
       while (buffer != 'F' && buffer != 'P' && buffer != 'f' && buffer != 'p' )
    et de même plus loin.

    * Tu n'as pas besoin de passer par des variables pour écrire ce genre de choses :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      float pL = *pieceLance;
      float pLj = *pieceLanceJuste;
      float taux = (float)(pLj/pL*100);
      printf("\n\nTaux de reussite : %.2f %c !\n\n",taux,37);
    il suffit de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      printf("\n\nTaux de reussite : %.2f %c !\n\n",(float)pLj/pL*100,37);
    le (float) s'applique à pLj et du coup, pour faire les opération pL et 100 seront convertis automatiquement en float

    * Dans afficheScore(), tu aurais peut être avantage à créer une fonction qui trace le séparateur, le même code est utilisé 3 fois et de même pour l'affichage des données. Ce qui devrait donner quelque chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    /*---------------------------------------------------------------*/
    void separateurH( int alloc_extend, int save_cpt_lP)
    {
       int i; 
       printf("\n--------------");  
       for(i=(1+alloc_extend); i <= save_cpt_lP && (i-alloc_extend) <= 10; i++)printf("-----");
    }
    /*---------------------------------------------------------------*/
    void afficheDonnee(int alloc_extend, int save_cpt_lP, char * donnee)
    {
      int i;   
      for(i=(0+alloc_extend); i<save_cpt_lP && (i-alloc_extend) < 10; i++)	printf(" %2c |", donnee[i]);		
    }
    /*---------------------------------------------------------------*/
    // Dans afficheScore()
    ....
       while (save_cpt_lP > alloc_extend)
       {
         printf("\n\n");
         separateurH(alloc_extend, save_cpt_lP);
         printf("\nTentative n° |");
         for(i=(1+alloc_extend); i <= save_cpt_lP && (i-alloc_extend) <= 10; i++) printf(" %2d |",i);
         separateurH( alloc_extend, save_cpt_lP);
         printf(" \nTirage       |");
         afficheDonnee(alloc_extend, save_cpt_lP, tab_tirage);
         printf(" \nProposition  |");
         afficheDonnee(alloc_extend, save_cpt_lP, tab_proposition);
         separateurH( alloc_extend, save_cpt_lP);
         alloc_extend +=10;
       }
       printf("\n\nTaux de reussite : %.2f %c !\n\n",(float)pLj/pL*100,37);
    ....
    Bon me reste plus qu'à faire la version pour Windows, allez hop au travail.
    La version pour Windows devrait être la même, je n'ai pas vu de code non standard.

  11. #11
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Citation Envoyé par diogene Voir le message
    La version pour Windows devrait être la même, je n'ai pas vu de code non standard.
    Bien vu lol.

    Sinon j'ai corrigé tous ce que tu m'as dit, j'ai rajouté des const un peu partout où je pouvais comme le suggérait jeroman. Au fait mon erreur à propos des const était due à mon prototype, en effet mettre un const sur un pointeur doit être précisé dans le prototype (à ce que j'ai pu comprendre). Par contre tu mettais des const dans la fonction alloueMemoire sur les deux vecteurs, mais justement on les modifies

    Enfin bref ce n'est pas grave, il me reste juste l'histoire du spécificateur de format j'arrive pas a reserver une certaine zone.

    %3.2f, devrait reserver une zone comme ça 333.22 ? non ? Enfin c'est vraiment un détail, je chercherais bien. Je poste une dernière fois mon code, tous les commentaires sont encore les bienvenus, mais enfin vous m'en avez déjà donné pas mal.
    Tout ce que vous m'avez dit, je le stocke dans const *char bienProgrammer dans mon cerveau

    Merci beaucoup, je ne sais pas quoi dire de plus, ca fait trop plaisir d'avoir des conseils de gens expérimentés.

    BYe bye.

  12. #12
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    1- Il reste une petite question sur la récupération de la mémoire.
    Les séquences dans 'N', 'n' et 'Q', 'q' détruisent la mémoire allouée. C'est sans problème pour 'Q', mais pour 'N' , tab_tirage et tab_proposition pointent maintenant sur une zone illégale ce qui va poser problème pour le lancement suivent, le jeu n'étant pas terminé ! Il faut remettre tab_tirage et tab_proposition à NULL impérativement

    2- Il reste un coté illogique dans le déroulement des opérations : tu alloues pour faire un tirage , tu fais le tirage et tu réalloues pour le tirage suivant. Tu as toujours une allocation d'avance.

    3- Si tu veux améliorer les choses, il faut maintenant se tourner vers une restructuration des données.
    Tu peux regrouper tout ce qui concerne une partie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    typedef struct
    {
      int cpt_partie ;
      int firstGame;
      int pieceLance;
      int pieceLanceJuste;
      char *tab_proposition;
      char *tab_tirage;
    } Partie ;
    que tu peux associer aux fonctions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    /*---------------------------------*/
    void nouveauJeu(Partie * partie)
    {
      partie->pieceLance = 0;
      partie->pieceLanceJuste = 0;
      free(partie->tab_proposition);
      free(partie->tab_tirage);
      partie->tab_proposition = NULL;
      partie->tab_tirage = NULL;
      partie->cpt_partie ++;
      partie->firstGame = 1; 
    }
    /*---------------------------------*/
    Partie * nouvellePartie(void)
    {
      Partie * partie = malloc( sizeof *partie);
      if(partie != NULL)
      {  
         partie->cpt_partie = 0;
         partie->tab_proposition = NULL;
         partie->tab_tirage = NULL;
         nouveauJeu(partie);
      }
      return partie;
    }
    /*---------------------------------*/
    void detruirePartie(Partie * partie)
    {
      free(partie->tab_proposition);
      free(partie->tab_tirage);
      free(partie);   
    }
    /*---------------------------------*/
    Le programme prend l'allure :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    int main (int argc, char*argv[])
    {
       int quit = 0;	// Variable "booléene" permettant l'arrêt du programme si quit = 1.
       Partie * partie = nouvellePartie(); 
       if(partie != NULL)
       {
          srand(time(NULL));      
          while (quit == 0)
          {
            presentation(partie);
            switch(lireClavier())
            {
               case 'n':
               case 'N':
                        if(!partie->firstGame)
                        {
                          afficheScore(partie);
                          nouveauJeu(partie);
    		      printf("\nVos scores ont été réinitialise a zero !");
                          lireClavier();
                        }
                        else messageErreur();
                        break;
              case 'l':
              case 'L':
                        alloueMemoire(partie);
                        partie->firstGame = 0; // il est mieux de gérer firstGame ici, c'est plus clair                   
                        lancePiece(partie);
                        break;
              case 'q':
              case 'Q':
                        afficheScore(partie);
                        detruirePartie(partie);
                        quit = 1;
                        break;
              default:
                        messageErreur();
            }
          }
          printf("Merci d'avoir joué avec nous ! Au revoir.\n\n");
       }
       else printf("Erreur d'allocation mémoire\n");
       return 0;
    }
    Bien sûr les fonctions afficheScore(), alloueMemoire(), lancePiece() sont à revisiter pour tenir compte du changement des arguments. Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void alloueMemoire(Partie * partie)
    {
      char * p = realloc(partie->tab_tirage ,partie->pieceLance+1);
      char * q = realloc(partie->tab_proposition ,partie->pieceLance+1);
      if(p!= NULL)partie->tab_tirage = p ;
      if(q!= NULL)partie->tab_proposition = q;
      if(p==NULL || q==NULL)
      {
        printf("REALLOC : Erreur d'allocation\n");
        detruirePartie(partie);
        exit(1);
      }
    }

  13. #13
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Je viens de finir de le coder avec les structures et dire que, avant ça, je me demandais à quoi ça servait ^^ Rien de tel qu'une application pour mieux comprendre.
    J'ai passé le sujet en résolu car je n'ai plus rien à vous demander.

    Merci du temps que vous aurez passez à relire mon petit code bourré d'erreurs

    Ciao

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Pb segmentation fault avec glutinit()
    Par pipistrelle dans le forum GLUT
    Réponses: 2
    Dernier message: 17/11/2004, 23h17
  2. [SDL_Image] Img_Load : segmentation fault ....
    Par Mathieu.J dans le forum OpenGL
    Réponses: 6
    Dernier message: 19/10/2004, 23h52
  3. [REDHAT] Segmentation fault systematique
    Par mela dans le forum RedHat / CentOS / Fedora
    Réponses: 2
    Dernier message: 21/09/2004, 06h05
  4. Réponses: 13
    Dernier message: 13/07/2004, 15h41
  5. Comment contrer la "segmentation fault" ?
    Par guillaume_pfr dans le forum C
    Réponses: 15
    Dernier message: 08/08/2003, 13h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo