Se connecter / S'enregistrer
Votre question

Pendu en C

Tags :
  • Programmation
Dernière réponse : dans Programmation
5 Octobre 2009 07:14:51

Hello,

J'ai un petit souci.
Alors voilà, j'ai essayé de faire un pendu (en me basant sur l'idée de base du SdZ mais sans regarder le code qu'il employait).
J'ai réussi sans trop de soucis le pendu pour un mot que je définissais à la base.

Mais c'est quand j'essaie de mettre un mot au hasard, par rapport à une liste de mots prédéfinie que je galère un peu.
Contrairement au SDZ (j'avais pas trop d'idée pour commencer ça), j'ai préféré utiliser une structure qu'un fichier pour stocker les mots.
Voici le code :

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int nombreAleatoire(int nombreMax);
  5. int COUNT_L (char chaine[]);
  6. int COMPARE (char chaine1[], char chaine2[]);
  7. int MAJ_L (char *caractere);
  8. int SEARCH_L (char chaine[], char caractere, char chaine2[], int *compteur_coups);
  9.  
  10. const char lettre1='\x82', lettre2='\x8A', lettre3='\x83', lettre4='\x88', lettre5='\x97', lettre6='\x85'; // é, è, â, ê, ù
  11.  
  12. struct Dico
  13. {
  14. char mot[20];
  15. };
  16. typedef struct Dico Dico;
  17.  
  18. int main()
  19. {
  20. system("TITLE YOUPI PENDU");
  21. char caractere_tape = 0;
  22. int compteur_coups = 10, erreur = 0;
  23. Dico mots_pendu[] = {"ALICE", "ANTOINE", "ROMEO", "JULIETTE", "BERNARD", "ANDRE", "BAPTISTE"};
  24. int nombreal = 0;
  25. nombreal = nombreAleatoire (6);
  26. char *mot_trouve = NULL;
  27. mot_trouve = malloc (COUNT_L (&mots_pendu[nombreal].mot[0]) * sizeof(char));
  28. if (mot_trouve == NULL) {exit (0);}
  29.  
  30.  
  31. printf ("Bienvenue dans le super jeu du PENDU !\n\n\n");
  32. printf ("Durant chaque partie, vous aurez le droit %c 10 tentatives\npour trouver le mot concern%c, c'est parti !\n\n\n", lettre6, lettre1);
  33.  
  34.  
  35.  
  36. do {
  37. printf ("Il vous reste %i tentatives !\n", compteur_coups);
  38. printf ("Quel est le mot secret ? %s\n", mot_trouve);
  39. printf ("Tape un caract%cre non accentu%c (a-z) : ", lettre2, lettre1);
  40. caractere_tape = getch();
  41. printf ("%c\n\n\n", caractere_tape);
  42. if (MAJ_L (&caractere_tape) == -1)
  43. {
  44. erreur = 1;
  45. break;
  46. }
  47. SEARCH_L (&mots_pendu[nombreal].mot[0], caractere_tape, &mot_trouve[0], &compteur_coups);
  48. if (compteur_coups == 0) { break; }
  49. } while (COMPARE (&mots_pendu[nombreal].mot[0], &mot_trouve[0]) != 0 );
  50.  
  51. if (COMPARE (&mots_pendu[nombreal].mot[0], &mot_trouve[0]) == 0)
  52. {
  53. printf ("Bravo ! Vous avez trouv%c le mot secret : %s avec un total de %i erreur(s)\n\n", lettre1, mots_pendu[nombreal].mot, 10 - compteur_coups);
  54. }
  55. else if (compteur_coups == 0)
  56. {
  57. printf ("Vous avez perdu la partie ! Le mot secret %ctait %s\n\n", lettre1, mots_pendu[nombreal].mot);
  58. }
  59. else if (erreur == 1)
  60. {
  61. printf ("Erreur, vous avez tap%c un mauvais caractere ! Le programme va se terminer !\n\n", lettre1);
  62. }
  63.  
  64. printf ("Appuyez sur une touche pour mettre fin au programme !\n\n");
  65. getch();
  66.  
  67. return 0;
  68. }
  69.  
  70. int nombreAleatoire (int nombreMax)
  71. {
  72. srand(time(NULL));
  73. return (rand() % nombreMax);
  74. }
  75.  
  76. int COUNT_L (char chaine[])
  77. {
  78. int i = 0, compteur = 1;
  79. while (chaine[i] != '\0')
  80. {
  81. compteur++;
  82. }
  83. return compteur;
  84. }
  85.  
  86. int COMPARE (char chaine1[], char chaine2[])
  87. {
  88. int i = 0, compteur = 0;
  89. while (chaine1[i] != '\0' || chaine2[i] != '\0')
  90. {
  91. if (chaine1[i] != chaine2[i])
  92. {
  93. compteur++;
  94. }
  95. i++;
  96. }
  97. if (compteur != 0 || chaine1[i] != chaine2[i])
  98. {
  99. return 1;
  100. }
  101. else
  102. {
  103. return 0;
  104. }
  105. }
  106.  
  107. int MAJ_L (char *caractere)
  108. {
  109. if (*caractere >= 97 && *caractere <= 122)
  110. {
  111. *caractere-=32;
  112. }
  113. else if (*caractere >= 65 && *caractere <= 90)
  114. {}
  115. else
  116. {
  117. return -1;
  118. }
  119. }
  120.  
  121.  
  122. int SEARCH_L (char chaine[], char caractere, char chaine2[], int *compteur_coups)
  123. {
  124. int i = 0, compteur = 0;
  125.  
  126. while (chaine[i] != '\0')
  127. {
  128. if (chaine[i] == caractere)
  129. {
  130. chaine2[i] = caractere;
  131. compteur++;
  132. }
  133. i++;
  134. }
  135. if (compteur == 0) { *compteur_coups-=1; }
  136. }


Je pense (sûr même) que la partie qui pose problème est celle-ci :
Voici la structure qui ne semble pas poser de problèmes (qui contient 7 mots pour le moment) :

struct Dico
{
char mot[20];
};
typedef struct Dico Dico;
Dico mots_pendu[] = {"ALICE", "ANTOINE", "ROMEO", "JULIETTE", "BERNARD", "ANDRE", "BAPTISTE"};

Ensuite, ce que je n'arrive pas (dernière étape avant la fin du jeu pourtant) c'est d'allouer le même nombre de lettre au "mot_trouve" que celui que je prends au hasard dans la structure, en lui assignant ce nombre fois le caractère étoiles.

Pour qu'en console, ça mette ******* (et que ça fasse apparaître les lettres quand l'utilisateur en trouve -> déjà fait pour ça);

=> mot_trouve = malloc (COUNT_L (&mots_pendu[nombreal].mot[0]) * sizeof(char));
c'est apparemment une des lignes qui fait que quand je compile, j'ai un écran vide =D
J'aimerais bien mettre dans mot autant d'étoiles qu'il y a de lettres à trouver, mais je ne sais pas comment faire.... merci

edit : j'aimerais bien ne pas avoir à utiliser de fonctions comme strcpy pour apprendre vraiment.

Merci beaucoup !

Autres pages sur : pendu

a b L Programmation
5 Octobre 2009 21:24:58

Juste pour info, tu peux définir le typedef à la définition de la structure:
  1. typedef struct
  2. {
  3. char mot[20];
  4. } Dico;


Bref,
Effectivement lorsque tu fais un tableau statique: type var[taille], alors taille doit être une constante: la variable est alloué statiquement. Quand le compilateur vois ça, il sait très bien qu'il aura besoin de (taille*sizeof(type)]) octets à allouer sur la pile, alors il peux le préparer à l'avance. Si tu veux mettre un tableau de taille non défini à la compilation, il va falloir que le programme alloue lui-même la mémoire dont il a besoin. C'est donc effectivement une allocation dynamique sur le tas qu'il te faut faire.

Par contre, je pense que tu galère parce que ta fonction COUNT_L() ne fait pas ce que tu en attends d'elle. :) 
  1. int COUNT_L (char chaine[])
  2. {
  3. int i = 0, compteur = 1;
  4. while (chaine[i] != '\0')
  5. {
  6. compteur++;
  7. }
  8. return compteur;
  9. }

Tu n'incrémentes jamais i => boucle infinie si la chaine n'est pas vide. :) 

Je te corrige:
  1. int COUNT_L (char chaine[])
  2. {
  3. int i = 0, compteur = 1;
  4. while (chaine[i] != '\0')
  5. {
  6. compteur++;
  7. i++;
  8. }
  9. return compteur;
  10. }


mais tu peux faire plus simple (un for qui ne fait rien):
  1. int COUNT_L (char chaine[])
  2. {
  3. int i;
  4. for( i = 0 ; chaine[i] != '\0' ; ++i);
  5.  
  6. return i + 1;
  7. }


Bon, je ne teste pas ton programme, je lis juste rapidement, et je te dis mes remarques:
dans nombreAleatoire(), ne fait pas le srand() à chaque fois que tu souhaites un nombre aléatoire. Tu le fais une fois au début, et après tu n'utilises que rand(). En faisant comme tu fais, sans parler de la loi du khi², tu n'es pas sûr d'avoir un bon générateur aléatoire. ;) 

Dans ta fonction compare(), tu peux optimiser en arrêtant la boucle lorsque "chaine1 == chaine2". Tu peux aussi faire un for qui ne sert à rien. :D 
Sinon le strcmp retourne -1 (si chaine1 < chaine2), 0 ou 1.

Sinon des petites règles pour bien utiliser malloc:
- quand tu écris le code malloc, la première chose à faire ensuite, c'est d'écrire la ligne du free()
- tester tous les pointeur lorsque avant l'utilisation de tout *var ou var->...
m
0
l
5 Octobre 2009 23:17:36

Hey Cricky,

Merci pour ta réponse :) 

Merci pour le tuyau pour le typedef, je ne savais pas.
En effet, j'avais oublié le i :D 
ça marche mieux maintenant :) 

En fait finalement, ça a été simple d'initialiser la chaîne avec des *. Je m'y suis pris comme ça :

void INITIALISE_C (char chaine[], int n_c)
{
int i = 0;
for (i = 0; i < n_c - 1; i++)
{
chaine = '*';
}
}

Au départ, je pensais faire un while (.. != '\0'), mais ça ne marche pas. Je pensais que vu que j'avais spécifié un char, seul le dernier indice du tableau allait se mettre à \0, mais apparemment ça n'était pas le cas. Mais en même temps, je ne sais pas comment se passe l'allocatio navec malloc (ce qui est initialement mis en mémoire à cet endroit lors de l'allocation).

en ayant fait : int count = COUNT_L (&mots_pendu[nombreal].mot[0]);

:) 

Merci beaucoup pour ton aide (comme d'habitude :p  )

-------

Pour le nombreAleatoire, en fait, je ne connais pas du tout la fonction srand, ça, c'est vraiment un code que j'ai pris tout cuit du sdz, d'ailleurs, ça ne me dérange pas de bien comprendre ! Mais vu que c'est dans une fonction, comment puis-je faire la première fois avec le s, et les autres fois sans ?

Pour la fonction compare(), je ne vois pas trop ce que tu veux dire en fait =D (tu parles toujours de mettre un compteur dans la fonction ?)
Merci (je vais peaufiner encore un peu demain :)  )
m
0
l
Contenus similaires
Pas de réponse à votre question ? Demandez !
a b L Programmation
6 Octobre 2009 20:17:03

Citation :
Au départ, je pensais faire un while (.. != '\0'), mais ça ne marche pas. Je pensais que vu que j'avais spécifié un char, seul le dernier indice du tableau allait se mettre à \0, mais apparemment ça n'était pas le cas. Mais en même temps, je ne sais pas comment se passe l'allocatio navec malloc (ce qui est initialement mis en mémoire à cet endroit lors de l'allocation).

Quand tu fais un malloc, le contenu n'est pas mis à 00. Donc, soit tu fais un memset() avec '\0' pour mettre '\0' partout, soit (et c'est mieux), tu mets juste '\0' au premier caractère pour indiquer une chaine vide.
Lorsque tu fais une copie, il faut toujours penser à copier un caractère de plus pour copier le '\0', et quand tu initialise une chaine avec des '*', pense à ajouter un '\0' à la fin.
Bref, si tu penses toujours au '\0', tu n'aura jamais besoin d'utiliser un nombre de caractères (c'est l'avantage des fonction str...() sur les mem...() pour les chaines de caractères).

Citation :
Pour le nombreAleatoire, en fait, je ne connais pas du tout la fonction srand, ça, c'est vraiment un code que j'ai pris tout cuit du sdz, d'ailleurs, ça ne me dérange pas de bien comprendre ! Mais vu que c'est dans une fonction, comment puis-je faire la première fois avec le s, et les autres fois sans ?

En fait le rand() est une fonction mathématique simple qui se base sur au moins une valeur précédente pour calculer la suivante. Par exemple: RAND<n> = (a * RAND<n-1> + c) % m où a,c et m sont des constantes.
Donc pour que rand() fonctionne bien, il faut l'avoir utilisé, mais au début on ne l'utilise pas, il faut donc définir une première valeur pour commencer. La première valeur c'est la graine (=> seed => seed random => srand) srand permet d'initialiser le générateur de nombres pseudo-aléatoire.
Afin d'avoir une séquence différente à chaque démarrage du programme, on utilise la valeur du temps actuel (time()), donc on a une valeur différente à chaque démarrage, ce qui donne une séquence différente.
Donc, le srand(), tu ne le lances qu'une seule fois pour initialiser le générateur de nombres pseudo-aléatoires, et pour obtenir des nombres aléatoires, tu n'utilises plus que rand().

Citation :
Pour la fonction compare(), je ne vois pas trop ce que tu veux dire en fait =D (tu parles toujours de mettre un compteur dans la fonction ?)

Ce que je veux dire, c'est que toi tu recherches tous les caractères différents, mais si tu trouve un caractère différent, tu sais que les chaines sont différentes, alors il faut arrêter la boucle.
Exemple, si tu compares les 2 chaines suivantes:
abzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
abyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
1. Tu commences à lire 'a' == 'a', alors on continue à chercher une différence
2. 'b' == 'b' alors on continue à chercher une différence
3. 'z' > 'y' donc on sait que les chaines sont différentes, alors on s'arrête là et on gagne du temps à ne pas lire les autres caractères. :) 

  1. int i;
  2. for( i = 0 ; chaine1[i] != '\0' && chaine2[i] != '\0' && chaine1[i] == chaine2[i] ; ++i );
  3.  
  4. return chaine1[i] - chaine2[i];

Le for ne fait rien mais boucle tant que l'on n'arrive pas à l'a fin d'un chaine et tant que les caractères sont identiques. Donc, après le for, i indique soit une fin de chaine, soit un caractère différent.

m
0
l
6 Octobre 2009 20:33:25

Hello !

Citation :
tu mets juste '\0' au premier caractère pour indiquer une chaine vide.
Lorsque tu fais une copie, il faut toujours penser à copier un caractère de plus pour copier le '\0', et quand tu initialise une chaine avec des '*', pense à ajouter un '\0' à la fin.

Oki, je ne savais pas que mettre un \0 au premier caractère allait initialiser toute la chaîne ? (ou ce n'est pas le cas ?)
Pour les **, effectivement, après 2-3 mots, j'ai remarqué qu'un caractère s'était ajouté au mot_trouve (au lieu du \0) et donc ça ne marchait pas pour ce mot. Donc je m'en suis occupé :) 

Citation :
Donc, le srand(), tu ne le lances qu'une seule fois pour initialiser le générateur de nombres pseudo-aléatoires, et pour obtenir des nombres aléatoires, tu n'utilises plus que rand().

Ok, donc si j'ai bien compris, ma fonction devrait plutôt ressembler à ça ?

  1. int nombreAleatoire (int nombreMax)
  2. {
  3. static int i = 0;
  4. if (i == 0) { srand(time(NULL));}
  5. i++;
  6. return (rand() % nombreMax);
  7. }


J'écris ça en pensant rejouer plusieurs fois (pas encore fait) dans la même exécution du programme.

Pour ta fonction compare, en fait tout se joue là-dessus ?
Citation :
return chaine1 - chaine2;


Vu que la fonction renvoie un int, si les deux chaînes sont identiques, alors ça retournera -1 ? (\ 0 - \ 0 = 0 - 0 (ou NULL - NULL ? :p  )
Fallait y penser quand même si c'est bien ça :) 

Sinon pour le i++ et le ++i, cela cause une réelle différence dans une boucle for ?

Bien, maintenant, je vais essayer de stocker les caractères tapés dans un tableau, et faire en sorte, que si l'user tape le même caractère, il ne soit pas pénalisé :) 

Merci!
m
0
l
6 Octobre 2009 21:30:47

Bonjour à tous,

-Pour le '\0', tu veux dire quoi par initialiser toute la chaîne ?
Peu importe la valeur que tu affectes à ta variable au départ, celle-ci sera initialisé même lorsque tu fais:
  1. char my_string[]='a'


-En ce qui concerne la fonction nombreAleatoire, le code est correct.
J'aimerais bien en savoir plus sur la loi de Khi² CRicky^^
Sinon ton code est correct.

-Pour compare(), je n'avais jamais vu un truc pareil concernant le code de CRicky :)  car je n'ai pas l'habitude de faire des traitements dans les parenthèses du for, comme quoi on en apprend tous les jours :) 
Si j'ai bien compris dès qu'une des conditions (cond_1 && cond_2 && cond_3) n'est plus vérifiée, il suffit de retourné la différence des 2 cases comparées qui sera différente de 0, donc "chaines non identiques".
Si ça retourne 0, elles sont identiques, c'est bien ça ?

Pour i++: on retourne la valeur et après on l'incrémente.
Pour ++i: on incrémente la valeur et on la retourne.

Après je ne vois pas pourquoi il a choisi "++i" ici :( 

A+
m
0
l
6 Octobre 2009 22:00:53

Salut,

Par initialiser, pour un chaîne, c'est vrai que c'est pas évident. J'entendais un peu ça : char chaîne[nombre] = "";

Bon, j'ai retapé mon programme, il marche nickel :

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int nombreAleatoire(int nombreMax);
  5. int COUNT_L (char chaine[]);
  6. void INITIALISE_C (char chaine[], int n_c);
  7. int REPEAT_C (char table[], char caractere, char choix);
  8. int COMPARE (char chaine1[], char chaine2[]);
  9. int MAJ_L (char *caractere);
  10. int SEARCH_L (char chaine[], char caractere, char chaine2[], int *compteur_coups);
  11.  
  12. const char lettre1='\x82', lettre2='\x8A', lettre3='\x83', lettre4='\x88', lettre5='\x97', lettre6='\x85'; // é, è, â, ê, ù
  13.  
  14. typedef struct
  15. {
  16. char mot[20];
  17. } Dico;
  18.  
  19.  
  20. int main()
  21. {
  22. system("TITLE YOUPI PENDU");
  23. Dico mots_pendu[] = {"BOUCLIER", "INFORMATIQUE", "AMOUR", "PASSION", "PENDU", "CORNICHE", "GLAUCOPHANE", "PARENCHYME", "PALISSADE", "GOURDINETTE", "SARBACANE", "CHANDELIER", "SULFATE", "TROMBINOSCOPE", "ECOUTILLE", "CHAUVIN"};
  24. int nombreal = 0, nombremots = sizeof (mots_pendu) / 20;
  25. char choix = 0;
  26.  
  27. do
  28. {
  29. char caractere_tape = 0;
  30. int compteur_coups = 10, erreur = 0;
  31. nombreal = nombreAleatoire (nombremots - 1);
  32. char *mot_trouve = NULL;
  33. int count = COUNT_L (&mots_pendu[nombreal].mot[0]);
  34. mot_trouve = malloc (count * sizeof(char));
  35. if (mot_trouve == NULL) {exit (0);}
  36. INITIALISE_C (&mot_trouve[0], count);
  37. char t_w_s_c[30] = "";
  38.  
  39. system ("CLS");
  40. printf ("Bienvenue dans le super jeu du PENDU !\n\n\n");
  41. printf ("Durant chaque partie, vous aurez le droit %c 10 tentatives\npour trouver le mot concern%c, c'est parti !\n\n\n", lettre6, lettre1);
  42.  
  43.  
  44.  
  45. do {
  46. printf ("Il vous reste %i tentatives !\n", compteur_coups);
  47. printf ("Quel est le mot secret ? %s\n", mot_trouve);
  48. printf ("Tape un caract%cre non accentu%c (a-z) : ", lettre2, lettre1);
  49. caractere_tape = getch();
  50. printf ("%c\n\n\n", caractere_tape);
  51. if (MAJ_L (&caractere_tape) == -1)
  52. {
  53. erreur = 1;
  54. break;
  55. }
  56. if (REPEAT_C (&t_w_s_c[0], caractere_tape, choix) == 0)
  57. {
  58. SEARCH_L (&mots_pendu[nombreal].mot[0], caractere_tape, &mot_trouve[0], &compteur_coups);
  59. }
  60. if (choix == 'y') { choix = 0;}
  61. if (compteur_coups == 0) { break; }
  62. } while (COMPARE (&mots_pendu[nombreal].mot[0], &mot_trouve[0]) != 0 );
  63.  
  64. if (COMPARE (&mots_pendu[nombreal].mot[0], &mot_trouve[0]) == 0)
  65. {
  66. printf ("Bravo ! Vous avez trouv%c le mot secret : %s avec un total de %i erreur(s)\n\n", lettre1, mots_pendu[nombreal].mot, 10 - compteur_coups);
  67. }
  68. else if (compteur_coups == 0)
  69. {
  70. printf ("Vous avez perdu la partie ! Le mot secret %ctait %s\n\n", lettre1, mots_pendu[nombreal].mot);
  71. }
  72. else if (erreur == 1)
  73. {
  74. printf ("Erreur, vous avez tap%c un mauvais caractere ! Cette partie s'arrête !\n\n", lettre1);
  75. }
  76. free(mot_trouve);
  77.  
  78. printf ("Voulez-vous rejouer ? (y/n) : ");
  79. choix = getch();
  80.  
  81. } while (choix == 'y');
  82. system ("CLS");
  83. printf ("Appuyez sur une touche pour mettre fin au programme !\n\n");
  84. getch();
  85.  
  86. return 0;
  87. }
  88.  
  89. int nombreAleatoire (int nombreMax)
  90. {
  91. static int i = 0;
  92. if (i == 0) { srand(time(NULL));}
  93. i++;
  94. return (rand() % nombreMax);
  95. }
  96.  
  97. int COUNT_L (char chaine[])
  98. {
  99. int i = 0;
  100. while (chaine[i] != '\0')
  101. {
  102. i++;
  103. }
  104. return i + 1;
  105. }
  106.  
  107. void INITIALISE_C (char chaine[], int n_c)
  108. {
  109. int i = 0;
  110. for (i = 0; i < n_c - 1; i++)
  111. {
  112. chaine[i] = '*';
  113. }
  114. chaine[i] = '\0';
  115. }
  116.  
  117. int COMPARE (char chaine1[], char chaine2[])
  118. {
  119. int i = 0, compteur = 0;
  120. while (chaine1[i] != '\0' || chaine2[i] != '\0')
  121. {
  122. if (chaine1[i] != chaine2[i])
  123. {
  124. compteur++;
  125. }
  126. i++;
  127. }
  128. if (compteur != 0 || chaine1[i] != chaine2[i])
  129. {
  130. return 1;
  131. }
  132. else
  133. {
  134. return 0;
  135. }
  136. }
  137.  
  138. int MAJ_L (char *caractere)
  139. {
  140. if (*caractere >= 97 && *caractere <= 122)
  141. {
  142. *caractere-=32;
  143. }
  144. else if (*caractere >= 65 && *caractere <= 90)
  145. {}
  146. else
  147. {
  148. return -1;
  149. }
  150. }
  151.  
  152. int REPEAT_C (char table[], char caractere, char choix)
  153. {
  154. static int z = 0;
  155. int x = 0, booleen = 0;
  156. if (choix == 'y') { z = 0;}
  157. for (x = 0; x <= 29; x++)
  158. {
  159. if (table[x] == caractere)
  160. {
  161. booleen = 1;
  162. break;
  163. }
  164. }
  165. if (booleen == 0)
  166. {
  167. table[z] = caractere;
  168. z++;
  169. return 0;
  170. } else {return 1;}
  171. }
  172.  
  173. int SEARCH_L (char chaine[], char caractere, char chaine2[], int *compteur_coups)
  174. {
  175. int i = 0, compteur = 0;
  176.  
  177. while (chaine[i] != '\0')
  178. {
  179. if (chaine[i] == caractere)
  180. {
  181. chaine2[i] = caractere;
  182. compteur++;
  183. }
  184. i++;
  185. }
  186. if (compteur == 0) { *compteur_coups-=1; }
  187. }


Je vais mettre tes optimisations en commentaires CRicky, encore merci :) 

edit : je viens d'avoir une mauvaise surprise. ça ne marche pas sur XP. Quand je tape des caractères, il me dit qu'ils sont mauvais. Pourtant, je vois bien le bon caractère s'afficher .... et la table de code est logiquement la même surtout que c'est du ASCII de base (pas étendu)..

Merci =)
m
0
l
a b L Programmation
7 Octobre 2009 20:40:43

Quand je parlais du '\0', ce que je voulais dire c'est que comme la chaine se termine toujours par '\0' en C, peu importe ce qui suit en mémoire puisqu'on ne va jamais le lire. ;) 
En C quand on écrit "toto", en fait, une fois compilé, c'est un tableau des caractères 't', 'o', 't', 'o' et '\0'. Du coup, quand on fait char var[] = "", et bien en fait on met un '\0' à l'adresse indiqué par var.

Pour ta fonction aléatoire, ça va marcher, mais moi, je pensais à quelque chose de beaucoup plus simple.
Dans le main:
  1. int main()
  2. {
  3. srand(time(NULL));
  4. ...

Comme ça au tout début de ton programme, tu initialises le générateur, et après tu n'utilise que rand:
  1. int nombreAleatoire (int nombreMax)
  2. {
  3. return (rand() % nombreMax);
  4. }


Pour le "return chaine1 - chaine2;", un caractère char est en fait un nombre codé (ASCII étendu ou latin-1) sur 1 octet, donc c'est juste un nombre de 0 à 255 inclus, avec par exemple '\0' = 0x00, '0' = 0x30.
Du coup, on compare les caractère par rapport à la position dans la table ASCII.
Donc si chaine1 == '\0' et chaine2 == '\0', ça retourne 0x00 - 0x00 = 0x00 => égalité.
si retourne < 0, alors la première chaine est soit plus courte, soit alphabétiquement (enfin suivant l'ordre ASCII...) avant la seconde.

pour le ++i et i++, akred3 a expliqué, alors je vais juste montrer la différence d'implémentation et vous expliquer pourquoi je n'utilise que des pre-incrémentation (++i). Si je code ++i par une fonction "int PreIncrement(int*)" et i++ par une fonction "int PostIncrement(int*)", alors l'implémentation sera:
  1. int PreIncrement (int * pValeur)
  2. {
  3. *pValeur = *pValeur + 1;
  4.  
  5. return *pValeur;
  6. }
  7.  
  8. int PostIncrement (int * pValeur)
  9. {
  10. int valeurAvant = *pValeur;
  11.  
  12. *pValeur = *pValeur + 1;
  13.  
  14. return valeurAvant;
  15. }

Pour le post-increment, on doit passer par une valeur temporaire. Un "int" faisant généralement 4 octets, ça occupe 4 octets supplémentaire sur la pile à l'exécution. 4 octets ce n'est pas beaucoup donc, ça ne sert à rien de se limiter à ++i, on peut faire i++, ça ne changera rien.
En fait, c'est surtout en C++ que ça peut impacter, car si l'on fait la même chose avec des objets, ça crée un objet temporaire avec allocation d'une certaine mémoire, et appel du contructeur par recopie (en espérant qu'il n'ait pas de bug). Bref, c'est surtout en C++ en manipulant les objets que ça a un impact, c'est pourquoi, je prend toujours l'habitude d'utiliser le pre-incrément ++i. Je n'ai pas fait ça pour faire cette explication, c'est juste une habitude.
Même si i++ semble plus naturel que ++i, en fait, d'un point de vue logique, il est plus naturel de faire ++i, mais bon, tant que ce ne sont que des types de base, ça ne changera pas grand chose. ;) 

Pour le khi² en fait c'est le test du khi². C'est du calcul statistique (j'aime pas les stats :D  ) qui permet de savoir si les valeurs correspondent bien à une distribution probabilistique (enfin il y a des dérivés du test).
http://fr.wikipedia.org/wiki/Test_du_khi_carr%C3%A9

Citation :
Si j'ai bien compris dès qu'une des conditions (cond_1 && cond_2 && cond_3) n'est plus vérifiée, il suffit de retourné la différence des 2 cases comparées qui sera différente de 0, donc "chaines non identiques".
Si ça retourne 0, elles sont identiques, c'est bien ça ?

Oui c'est ça.

Citation :
edit : je viens d'avoir une mauvaise surprise. ça ne marche pas sur XP. Quand je tape des caractères, il me dit qu'ils sont mauvais. Pourtant, je vois bien le bon caractère s'afficher .... et la table de code est logiquement la même surtout que c'est du ASCII de base (pas étendu)..

Juste avant le "return -1;", affiche le code ASCII du caractère pour voir ce qui se passe:
  1. int MAJ_L (char *caractere)
  2. {
  3. if (*caractere >= 97 && *caractere <= 122)
  4. {
  5. *caractere-=32;
  6. }
  7. else if (*caractere >= 65 && *caractere <= 90)
  8. {}
  9. else
  10. {
  11. printf( "Code ASCII invalide: 0x%02x", *caractere );
  12. return -1;
  13. }
  14. }

C'est peut-être dû à un retour chariot qui traine, il faudrait peut-être vider le buffer du clavier après avoir entré la lettre.
m
0
l
8 Octobre 2009 10:21:55

Hello,

Citation :
En C quand on écrit "toto", en fait, une fois compilé, c'est un tableau des caractères 't', 'o', 't', 'o' et '\0'. Du coup, quand on fait char var[] = "", et bien en fait on met un '\0' à l'adresse indiqué par var.

Seulement ? Lorsque j'ai créé ce tableau :

char t_w_s_c[30] = "";

J'avais d'abord essayé sans rien définir (sans le ""). Puis j'avais bouclé pour afficher tous les indices du tableau, la plupart contenaient un peu n'importe quoi.
Une fois mis le = "", toutes les cases étaient vides. Je pensais donc que faire char blabla[30] = "", vidait toute la chaine définie.

Okir pour l'incrémentation.

En fait, dans ton exemple :
int i;
for( i = 0 ; chaine1 != '\0' && chaine2 != '\0' && chaine1 == chaine2 ; ++i );
return chaine1 - chaine2;

Si une chaîne est plus courte que l'autre, la boucle va s'arrêter, mais alors forcément une des autres conditions (pourtant liées par des &&) ne sera pas respectée. C'est ça que je trouve un peu bizarre. De même si les deux premiers caractères des chaînes sont égaux, logiquement la boucle s'arrête non? (ou pas puisque les autres conditions ne sont pas vérifiées). C'est bizarre en fait à comprendre je trouve.

Pour les caractères, j'ai d'abord essayé sous Vista :

- Petite surprise lorsque je tape des caractères (au dessus de 7F), quand je tape un é dans la console (je ne sais plus si c'est considéré comme du ANSI ou du OEM quand on tape directement dans la console ^^), il m'affiche par exemple que j'ai tapé : 0XFFFFFF8A
:o 
pour le ç : 0xFFFFFF87

Bon, je suis pas bête :p  Finalement, j'ai remarqué que le dernier octet représentait le codage ASCII étendu OEM du caractère que je tapais dans la console (donc j'ai ma réponse pour la "question" juste avant). La question est : pourquoi tous ces FF avant ? Surtout que c'est censé être stocké dans un char !

- Maintenant, j'essaie sous XP :

Bizarre, j'ai pourtant recompilé avec le code printf ajouté sous Vista pour C/C sur mon xp l'exécutable. Il ne me sort pas la phrase sur XP pourtant la date de modif coïncide bien avec la dernière recompilation ... (j'ai pris dans Debug).

edit : Alors, là, j'ai une grosse surprise :p  !
(j'ai installé le compilateur sous xp et compilé le .c)..
Lorsque je tape un caractère normal, il me fait comme avant, juste le message d'erreur ! (sans le printf ajouté).. et lorsque je tape un caractère > 0x7F, il me fait exactement la même chose que sous Vista (cette fois avec le printf).

o_O

Merci, bizarre quand même !
m
0
l
a b L Programmation
8 Octobre 2009 20:17:29

Citation :
Je pensais donc que faire char blabla[30] = "", vidait toute la chaine définie

Je dirais que ça dépend de ton compilateur et de son optimisation. Quoi qu'il en soit, du point de vue du programmeur, tu ne dois pas considérer que le reste est à 0. Si tu veux des 0, il faut faire un memset().

Citation :
Si une chaîne est plus courte que l'autre, la boucle va s'arrêter, mais alors forcément une des autres conditions (pourtant liées par des && ) ne sera pas respectée. C'est ça que je trouve un peu bizarre. De même si les deux premiers caractères des chaînes sont égaux, logiquement la boucle s'arrête non? (ou pas puisque les autres conditions ne sont pas vérifiées). C'est bizarre en fait à comprendre je trouve.

C'est un problème de logique: si A et B sont des conditions, alors NON(A ET B) <=> (NON A) OU (NON B) (il faut inverser ;)  ).
Concrètement, while( chaine1 != '\0' && chaine2 != '\0' && chaine1 == chaine2 ) relance une boucle si chaine1 != '\0' et chaine2 != 0 et chaine1 == chaine2. Tu veux continuer la boucle si toutes les conditions sont vraie (donc utilisation de &&).
Du coup, comme il faut que toutes les conditions soient vérifiées pour continuer la boucle, il suffit qu'une seule des conditions ne soit pas vérifiée pour que la boucle ne continue pas, et donc s'arrête.
Donc, la boucle s'arrêtera lorsque qu'une des conditions n'est pas vérifié: ça boucle jusqu'à ce que chaine1 == '\0' ou que chaine2 == '\0' ou que chaine1 == chaine2.
En C, le "tant que" n'existe pas, donc il faut penser aux conditions pour que la boucle continue (et pas celles qui la font s'arrêter).

Citation :
(je ne sais plus si c'est considéré comme du ANSI ou du OEM quand on tape directement dans la console ^^)

Dans une console de type DOS, de 128 à 255, c'est de l'ASCII, et sous windows (en fenêtre).

Citation :
La question est : pourquoi tous ces FF avant ? Surtout que c'est censé être stocké dans un char !

En fait, un char (comme un short et un int) est nombre signé. Donc, comme le char est codé sur 1 octet, il va en fait de -127 à 128 inclus. Du coup, si tu prends 0xEE ça code en fait -1, et dans le printf, il y a un cast implicite du char vers l'int, et ça se conde dans l'int: 0xFFFFFFFF.
Pour éviter le problème, il faut le convertir (cast explicite) en "unsigned char" qui est la version non signé (de 0 à 255):
Essaie ceci pour avoir le vrai nombre:
  1. printf( "Code ASCII invalide: 0x%02x", (unsigned char)(*caractere) );


Pour ton exemple, tu as 0x8A qui est en fait 0x8A = 0xFF - 0x76 + 1. Donc, en signé 0x8A = -0x76 = -118 et ça se code en int: -118 = 0xFFFFFFFF - 0x76 + 1 = 0xFFFFFF8A

Je pense avoir compris ton problème:
  1. int MAJ_L (char *caractere)
  2. {
  3. if (*caractere >= 97 && *caractere <= 122)
  4. {
  5. *caractere-=32;
  6. }
  7. else if (*caractere >= 65 && *caractere <= 90)
  8. {}
  9. else
  10. {
  11. return -1;
  12. }
  13. }

Que retourne cette fonction sous XP et sous Vista ? ;) 
m
0
l
8 Octobre 2009 21:22:52

CRicky a dit :

...
Pour le khi² en fait c'est le test du khi². C'est du calcul statistique (j'aime pas les stats :D  ) qui permet de savoir si les valeurs correspondent bien à une distribution probabilistique (enfin il y a des dérivés du test).
http://fr.wikipedia.org/wiki/Test_du_khi_carr%C3%A9
...


Merci CRicky :) 
m
0
l
9 Octobre 2009 20:12:08

Hello =)

Citation :
C'est un problème de logique: si A et B sont des conditions, alors NON(A ET B) <=> (NON A) OU (NON B) (il faut inverser ;)  ).
Concrètement, while( chaine1 != '\0' && chaine2 != '\0' && chaine1 == chaine2 ) relance une boucle si chaine1 != '\0' et chaine2 != 0 et chaine1 == chaine2. Tu veux continuer la boucle si toutes les conditions sont vraie (donc utilisation de && ).
Du coup, comme il faut que toutes les conditions soient vérifiées pour continuer la boucle, il suffit qu'une seule des conditions ne soit pas vérifiée pour que la boucle ne continue pas, et donc s'arrête.
Donc, la boucle s'arrêtera lorsque qu'une des conditions n'est pas vérifié: ça boucle jusqu'à ce que chaine1 == '\0' ou que chaine2 == '\0' ou que chaine1 == chaine2.
En C, le "tant que" n'existe pas, donc il faut penser aux conditions pour que la boucle continue (et pas celles qui la font s'arrêter).

Oki, compris. Je supposte que tu voulais dire chaine1 != chaine2 au lieu de == à la fin de ton avant dernière phrase :) 

Citation :
En fait, un char (comme un short et un int) est nombre signé. Donc, comme le char est codé sur 1 octet, il va en fait de -127 à 128 inclus. Du coup, si tu prends 0xEE ça code en fait -1, et dans le printf, il y a un cast implicite du char vers l'int, et ça se conde dans l'int: 0xFFFFFFFF.

Tu veux dire 0xFF plutôt pour -1 ? C'est donc le printf qui fait ce cast ? Il suffit donc que je définisse un unsigned char dès le début ?

Citation :
Que retourne cette fonction sous XP et sous Vista ? ;) 

Bah sous Vivi, elle marche très bien.

Et sous XP, je viens de rajouter des return 0 dans le if et else if et là ça semble marcher, bizarre non ???

La fonction retournait automatiquement -1 ou quoi ? et que sous xp en plus ? :p 

Pas très compréhensible tout ça ... :) 
m
0
l
a b L Programmation
9 Octobre 2009 23:02:44

Citation :
Oki, compris. Je supposte que tu voulais dire chaine1 != chaine2 au lieu de == à la fin de ton avant dernière phrase :) 

Exact ;) 

Citation :
Tu veux dire 0xFF plutôt pour -1 ?

Exact, j'ai voulu faire différent de -1, et j'ai du oublié de changer le reste. :) 

Citation :
C'est donc le printf qui fait ce cast ? Il suffit donc que je définisse un unsigned char dès le début ?

oui dans le printf, quand tu utilise %d, il attend un entier, donc le compilateur fait un cast implicite.

Citation :
La fonction retournait automatiquement -1 ou quoi ? et que sous xp en plus ?

Est-ce que tu as utilisé le même exe ou as-tu recompilé sous chacun des OS?
Je pense que c'est la compilation qui s'est faite différemment, avec dans un cas un return 0 par défaut (comme le retour du main dans la norme C99), et dans l'autre un return -1 (pour forcer les erreurs).

Mais normalement, tu as du avoir un warning là-dessus (même si les warning ne font pas partie des normes ANSI).
m
0
l
9 Octobre 2009 23:10:22

Hey ,

Ceci dit, pour le %d du printf, d'accord, il attend un entier. Mais à ce moment-là, pourquoi n'affiche-t-il pas un nombre négatif tout simplement au lieu de faire un cast ?
(si c'était un entier, c'est bien ce qu'il ferait pourtant)

Pour l'exe, bah en fait non. J'avais copié/collé mon exe de mon visa à mon xp et ça ne marchait pas.
FInalement, j'ai recompilé sur les deux avec les return 0, et ça marche bien :)  :) 

Tant que j'y suis, j'aurais une petite question par rapport au buffer !

L'autre jour, j'avais manipulé fgets, etc..

J'avais fait cette fonction :

Citation :
void READ (char text[], int size)
{
int i;
if (fgets (text, size, stdin) != NULL)
{
for (i = 0; i <= size; i++)
{
if (text == '\n')
{
text = '\0';
}
}
scanf ("%*[^\n]");
getch();
}
}

qui marchait bien au début et qui ne marchait plus ensuite ^^

Le truc pourri là-dedans, c'est que ça faisait un scanf et getch() même si la chaîne était de bonne taille, ce qui est gênant.

Alors j'ai voulu faire un test sur le buffer, pour faire cette étape uniquement si besoin !

Alors j'ai essayé de voir comment tester le buffer :

Citation :
printf ("Stdin vaut %i, %d, %p", *stdin, *stdin, *stdin);


Le seul truc qui me donnait quelque chose de très cohérent, c'est avec le %d (je comprends pas pourquoi ça donne pas la même chose avec le %d et le %i enfin bref). Par exemple, la chaîne que j'introduis au départ est chaine[10], donc accepte 9 caractères.

Si je tapais lors de ma saisie : baptiste (8 caractères), donc + \n et \0 (\0 rajouté par fgets si j'ai compris ?), le %d m'affichait 0. Si je faisais plus, ça m'affiche toujours quelque chose de très cohérent, par ex :



Là, il m'affiche le 5 et c'est cohérent puisque il reste iste\n (je pensais qu'il y avait encore le \0 après, mais apparemment ou bien ça compte pas comme caractère (puisque c'est null) ou bien c'est rajouté par fgets uniquement pour la chaîne qu'il maitriste). Finalement je me demande si un scanf ("%*[^\0]"); ne suffirait pas pour vider le buffer !)

Donc je me suis dit qu'à partir de là, je pouvais faire un test sur le buffer en faisant un truc du genre :

if (*stdin != 0)
{
scanf ("%*[^\n]");
getch();
}

Le problème c'est que ça ne compile pas =D

Mais j'ai cru comprendre sur ce lien : http://www.linux-kheops.com/doc/man/manfr/man-html-0.9/... que stdin (ou stdout, stderr) était un pointeur de type FILE.. Bref, je sais pas tropcomment faire. =D

Thanks ! GTB
m
0
l
a b L Programmation
10 Octobre 2009 16:21:21

Dabord, sur nos machines, pour le printf avec %d ou %x, quand cette fonction lit %d dans la chaine, il s'attend à un entier int en paramètre. Un int étant sur 4 octets, la fonction va bêtement récupérer 4 octets sur la pile. Il faut donc mettre 4 octets sur la pile et pas 1, d'où la conversion.
Ensuite, j'ai mis dans printf(): %02x ce qui indique de mettre 2 caractères (ça ajoute les 0 devant si besoin). Le %x n'est pas signé car il affiche uniquement le code hexadécimal qui lit directement en mémoire. Le signé/non signé, c'est juste de l'interprétation du code hexadécimal.
comme il y a "02" dans le %x, ça affiche 2 caractères, mais si la valeur hexadécimale dépasse les 02, alors ça affiche la valeur complète. Dans ton cas, comme l'entier est négatif, la valeur est 0xFFFFFFxx, ça dépasse les 2 caractères alors il affiche tout.

Citation :
Pour l'exe, bah en fait non. J'avais copié/collé mon exe de mon visa à mon xp et ça ne marchait pas.
FInalement, j'ai recompilé sur les deux avec les return 0, et ça marche bien

Intéressant, il faudrait décompiler en assembleur pour voir ce qui se passe. :) 

Citation :
Le seul truc qui me donnait quelque chose de très cohérent, c'est avec le %d (je comprends pas pourquoi ça donne pas la même chose avec le %d et le %i enfin bref). Par exemple, la chaîne que j'introduis au départ est chaine[10], donc accepte 9 caractères.

Le type FILE est une structure qui contient entre autres le descripteur du fichier, buffer, etc (peut-être dépendant de l'OS).
Bref, de ton côté, tu ne vas jamais lire le contenu d'un FILE.

Citation :
scanf ("%*[^\n]" );

C'est quoi ça? un expression régulière? Même si ça fait très longtemps que je n'ai pas utilisé la fonction scanf(), je ne pense pas qu'elle prenne les expression régulières. :) 

stdin, stdout et stderr sont bien des FILE* car ils se manipulent avec des descripteurs de fichier.
Je ne comprends pas très bien ce que tu veux faire dans ta fonction READ.
m
0
l
11 Octobre 2009 21:19:15

Hello,

D'accord, maintenant, je crois avoir compris pour ce fameux cast ! Je n'avais jamais entendu parler de cette astuce pour voir combien fait un nombre (négatif ) = 0xFF (nombre de bits ...) - 0xle nombre qu'on cherche si bit de poids fort à 1 = le nombre qu'on cherche (en mettant - devant). Ou sinon complément à 2 :p 

Pour décompiler, je veux bien, mais je sais pas trop comment faire.. (comment utiliser surtout)

Ok pour le type file. Mais tu vois bien que quand je fais un %d, *stdin, cela m'affiche le nombre de caractères dans le buffer ? (\0 exclu bien-sûr !)

En fait dans ma fonction READ, j'espérais m'en servir pour faire le scanf ("%*[^\n]") (supprime les caractères compris dans le buffer sauf les \n suivi d'un getchar(), je me demandais d'ailleurs si on pouvait pas plus simplement faire scanf ("%*[^\n]'). (vu ça sur un tuto de developpez.com). Tu veux le lien ?

Donc en fait j'aurais bien aimé me servir de ce *stdin, mais si c'est pas vraiment possible, tant pis :þ

m
0
l
a b L Programmation
12 Octobre 2009 19:18:31

Pour le nombre négatif, tu peux faire un test avec un masque binaire AND: si (val & 0x80 == 0x80), alors négatif.

Pour décompiler, quelle interface de dev utilises-tu? Visual, autre?

pour le stdin, ça dépend de l'implémentation de la structure du FILE que tu utilise. Si dans cette structure, le premier champ (mis à l'adresse de la structure en mémoire) est le nombre de caractère, alors c'est normal que ça t'affiche ça. Le définition de la structure FILE est dans stdio.h, tu peux voir la structure, mais ce n'est pas quelque chose de standard.

Envoie le lien pour scanf. :) 
m
0
l
12 Octobre 2009 20:28:08

Hey !

Citation :
Dabord, sur nos machines, pour le printf avec %d ou %x, quand cette fonction lit %d dans la chaine, il s'attend à un entier int en paramètre. Un int étant sur 4 octets, la fonction va bêtement récupérer 4 octets sur la pile. Il faut donc mettre 4 octets sur la pile et pas 1, d'où la conversion.


Juste pour revenir là-dessus ! La conversion en int ne semble que se faire que lorsque le nombre est négatif, c'est bizarre tout de même :p 

J'utilise CodeBlocks comme interface de développement :)  Je veux bien essayer, ça me ferait une expérience =)

Pour le stdin, tu as une idée pourquoi le résultat est différent selon que je fasse %i et %d (ce qui était la même chose je pensais ! ). Sinon, bien-sûr, je ne connais pas cette structure, donc pas moyen de me servir de mon résultat avec %D pour tester si le buffer est vide ou pas ? Ce serait intéressant tout de même =) Sinon je ne vois pas trop comment tester le buffer, car j'aimerais faire des scanf ou getchar seulement si le buffer est plein (en gros pour pas que ça demande à l'utilisateur de taper quelque chose s'il a bien respecté les règles pour la saisie d'avant).

Pour moi, le problème avec cette fonction (du sdz) :

void viderBuffer()
{
int c = 0;
while (c != '\n' && c != EOF)
{
c = getchar();
}
}

C'est qu'on a au moins un getchar(), ce que je veux éviter. Sinon pour le EOF, je suppose que ça marque la fin : '\0' ? ..

Voici le link pour scanf : http://xrenault.developpez.com/tutoriels/c/scanf/

;) 
m
0
l
a b L Programmation
13 Octobre 2009 21:04:16

Citation :
Juste pour revenir là-dessus ! La conversion en int ne semble que se faire que lorsque le nombre est négatif, c'est bizarre tout de même :p 

Non, elle se fait aussi si c'est positif, mais quand c'est positif, en hexa ça s'écrit avec 2 caractères au maximum. Du coup le %02x n'affiche que les 2 caractères hexadécimaux (par exemple 0xFF), mais printf a reçu 0x000000FF. ;) 

Citation :
J'utilise CodeBlocks comme interface de développement

Tu mets un point d'arrêt sur l'accolade de début de ta fonction à problème, et dans le menu debug, tu affiches la fenêtre disassembly, et ça t'affiche le code de la fonction.

Citation :
Sinon, bien-sûr, je ne connais pas cette structure, donc pas moyen de me servir de mon résultat avec %D pour tester si le buffer est vide ou pas ?

Non, on ne lit pas cette structure.

Tu peux faire fflush(stdin); pour commencer. Tu peux remplacer le getchar() par un fgetc(stdin)

EOF=-1, c'est getchar (et autres get) qui retourne -1 quand la fin est atteinte.
m
0
l
17 Octobre 2009 11:01:16

Hello !

Voici pour le Debug, je crois que c'est bon (j'ai enlevé les return0 ajoutés évidemment).

  1. Project title : pendu
  2. Project path : C:\Documents and Settings\Toinou\Mes documents\pendu\
  3.  
  4. Frame function:
  5. MAJ_L (C:/Documents and Settings/Toinou/Mes documents/pendu/main.c:161)
  6.  
  7. Frame address : 0023FD50
  8. --------------------------------------------------------------------------------
  9.  
  10. 004018CE push %ebp
  11.  
  12. 004018CF mov %esp,%ebp
  13.  
  14. 004018D1 sub $0x18,%esp
  15.  
  16. 004018D4 mov 0x8(%ebp),%eax
  17.  
  18. 004018D7 cmpb $0x60,(%eax)
  19.  
  20. 004018DA jle 0x4018f4 <MAJ_L+38>
  21.  
  22. 004018DC mov 0x8(%ebp),%eax
  23.  
  24. 004018DF cmpb $0x7a,(%eax)
  25.  
  26. 004018E2 jg 0x4018f4 <MAJ_L+38>
  27.  
  28. 004018E4 mov 0x8(%ebp),%eax
  29.  
  30. 004018E7 mov 0x8(%ebp),%edx
  31.  
  32. 004018EA movzbl (%edx),%edx
  33.  
  34. 004018ED sub $0x20,%dl
  35.  
  36. 004018F0 mov %dl,(%eax)
  37.  
  38. 004018F2 jmp 0x401925 <MAJ_L+87>
  39.  
  40. 004018F4 mov 0x8(%ebp),%eax
  41. 004018F7 cmpb $0x40,(%eax)
  42.  
  43. 004018FA jle 0x401906 <MAJ_L+56>
  44.  
  45. 004018FC mov 0x8(%ebp),%eax
  46.  
  47. 004018FF cmpb $0x5a,(%eax)
  48.  
  49. 00401902 jg 0x401906 <MAJ_L+56>
  50.  
  51. 00401904 jmp 0x401925 <MAJ_L+87>
  52.  
  53. 00401906 mov 0x8(%ebp),%eax
  54.  
  55. 00401909 movzbl (%eax),%eax
  56.  
  57. 0040190C mov %eax,0x4(%esp)
  58.  
  59. 00401910 movl $0x404568,(%esp)
  60.  
  61. 00401917 call 0x401fa8 <printf>
  62.  
  63. 0040191C movl $0xffffffff,-0x4(%ebp)
  64.  
  65. 00401923 jmp 0x401925 <MAJ_L+87>
  66.  
  67. 00401925 mov -0x4(%ebp),%eax
  68.  
  69. 00401928 leave
  70.  
  71. 00401929 ret


Merci pour les autres infos !

PS : J'ai l'impression que je n'arrive à désassembler que cette fonction, pas encore saisi le fonctionnement. Je fais des Debug, et quand il me le permet (pour désassembler) "debug - next instruction"
m
0
l
a b L Programmation
17 Octobre 2009 11:41:45

Oui, il ne te désassemble que la fonction dans laquelle tu te trouves.
Les instruction asm sont écrites à l'envers. "mov 0x8(%ebp),%eax" se lit en fait MOV EAX,[EBP+8].

En 004018D7, on voit bien la comparaison (inverse) *caractere >= 97. Si non vérifiée, saute en 004018F4
En 004018DF => comparaison *caractere <= 122. Si non vérifiée, saute en 004018F4
Bloc de 004018E4 à 004018F2 => intérieur du if qui décrémente bien *caractère de 0x20, puis saute en 00401925 pour terminer

En 004018F7 => comparaison *caractere >= 65. Si non vérifiée, saute en 00401906
En 004018FF => comparaison *caractere <= 90. Si non vérifiée, saute en 00401906
Saut direct en 00401925 (le "else if" est vide) pour terminer

Intérieur du "else":
Bloc de 00401906 à 00401917 => appel au printf
En 0040191C => on met -1 en [EBP - 4] (au sommet de la pile), puis saut en 00401925 (saut qui d'ailleurs ne sert à rien puisque c'est l'instruction suivante).

En 00401925, c'est le registre EAX qui sert de valeur de retour, et on y met [EBP - 4].
Donc, si l'on est passé dans le "else", on a -1 dans cette zone mémoire. Si on est passé par le "if" ou le "else if", ça met ce qu'il y avait en mémoire à cet endroit, et vu qu'on n'y a rien mis, ça peut être n'importe quoi. C'est un petit buffer overflow.
Si dans ta fonction, tu regardes en mémoire ce qu'il y avait à cette adresse, tu verrais ce qui est retourné par ta fonction. Un programme compilé en debug ou en optimisé peut donner des résultats différents.
m
0
l
23 Octobre 2009 10:55:48

Hello,

Intéressant cette analyse :) 

Citation :
# 004018D7 cmpb $0x60,(%eax)
#
# 004018DA jle 0x4018f4 <MAJ_L+38>
#
# 004018DC mov 0x8(%ebp),%eax
#
# 004018DF cmpb $0x7a,(%eax)

Ici, par curiosité, où est le supérieur à et inférieur à ?
Ou alors c'est jle et jg qui précisent cette différence :)  C'est sûrement ça, je me souviens, on en avait parlé. Les différents jump se réfèrent à une valeur précise dans certains bits du registre EFLAGS et jumpent en conséquence.

Faudrait que je lise la doc ASM..

Merci
m
0
l
a b L Programmation
23 Octobre 2009 19:14:13

Oui, le "CMP EAX, 60h" compare le contenu du registre EAX avec 60h et allume les flags qui vont bien (zero flag, sign flag...). En fait, il calcule la soustraction (EAX - 60h)
Ensuite, le JLE=Jump if Less or Equal (saut si EAX <= 60h).
m
0
l
26 Octobre 2009 10:27:39

Oki, merci pour toutes ces infos :)  :) 
m
0
l
Tom's guide dans le monde
  • Allemagne
  • Italie
  • Irlande
  • Royaume Uni
  • Etats Unis
Suivre Tom's Guide
Inscrivez-vous à la Newsletter
  • ajouter à twitter
  • ajouter à facebook
  • ajouter un flux RSS