Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[débutant] 1er programme, j'aimerais vos commentaires.

91 réponses
Avatar
Beware
Bonjour,

D=E9butant dans l'apprentissage du langage C, j'ai cr=E9e un petit
programme, un jeu du pendu. Le jeu =E0 l'air de fonctionner. Je dis
"l'air de", car il est probable qu'il reste des bugs que je dois
corriger.
Cependant ce n'est pas l'objet de ma question. En effet, dans un souci
de m'am=E9liorer je d=E9sirerais avoir les commentaires de personnes
connaissant et maitrisant mieux le langage C que moi.

Les fichiers du programme (main.c, pendu.h et dico.txt) sont
disponible ici :
http://beware007.free.fr/Projet_C/Pendu/


Merci d'avance pour votre aide, vos commentaire et critiques.

10 réponses

Avatar
Beware
On 15 mar, 19:21, (Marc Espie) wrote:
In article .com>,

Beware   wrote:
>Est-ce qu'une macro qui me permette de remplacer ces deux lignes est
>une chose "orthodoxe" ou pas? Du genre :
>#define PRINT(chaine)   printf("%s", chaine);
>                                   ffl ush(stdout);

>et ensuite utiliser PRINT quand j'en ai besoin.

>La question peut vous paraitre bete, mais comme je n'ai jamais utilisé
>les macros je me demande.

Tu n'as jamais utilise les macros: c'est bien, ne commence pas.

Dans ce cas precis, ca n'apporte rien en lisibilite, bien au contraire.

Si tu veux ecrire une fonction, n'hesite pas. Tu peux la marquer comme
etant inline en C99, ce qui te donnera tous les benefices de la macro,
sans aucun de ses inconvenients...



D'accord.
J'ai bien fait de demander alors.
Merci.
Avatar
Jean-Marc Bourguet
JKB writes:

Juste une question pour ma culture personnelle. Est-on sûr que
quelque chose va s'afficher lorsque l'argument du printf() contient un
retour à la ligne ? Je me souviens avoir dû coller une macro du type



Un flux est bufferisé par block si et seulement si la destination n'est pas
interactive. Unix, me semble-t'il; ne considère comme interactif que les
terminaux, donc pas les pipes, socket, ...

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Pierre Maurette
Marc Espie, le 15/03/2009 a écrit :
In article
, Beware
wrote:
Est-ce qu'une macro qui me permette de remplacer ces deux lignes est
une chose "orthodoxe" ou pas? Du genre :
#define PRINT(chaine) printf("%s", chaine);
fflush(stdout);

et ensuite utiliser PRINT quand j'en ai besoin.

La question peut vous paraitre bete, mais comme je n'ai jamais utilisé
les macros je me demande.



Tu n'as jamais utilise les macros: c'est bien, ne commence pas.



Il faut quand même au moins les connaitre, ne serait-ce parce qu'on
peut être amené à cotoyer du code non "sui generi". Et aussi parce
certains trucs du langage sont des macros. Et parce que de temps en
temps ça peut aider.
En fait il me semble que les macros c'est très bien, à ceci près que le
préprocesseur de C est calamiteux.

Dans ce cas precis, ca n'apporte rien en lisibilite, bien au contraire.

Si tu veux ecrire une fonction, n'hesite pas. Tu peux la marquer comme
etant inline en C99, ce qui te donnera tous les benefices de la macro,
sans aucun de ses inconvenients...



Je comprends bien que votre remarque est générale et très juste. Mais
comme vous écrivez "Dans ce cas precis", j'ajouterai que "Dans ce cas
precis" d'une fonction d'E/S qui va certainement faire un appel à
puts() ou à printf(), le fait qu'elle soit ou non inlinée est
totalement sans importance.

--
Pierre Maurette
Avatar
Antoine Leca
Le 15/03/2009 14:59, Beware écrivit :
Alors commençons par un retour sur certaines de vos remarques :


[...]
En fait, pour certains commentaire, j'avoue qu'ils sont plus une
aide pour moi qu'un veritable apport à la lecture du code.



Alors ne change rien ! Les commentaires ne sont pas là pour faire
plaisir aux experts en revue de code, ils sont d'abord là pour guider
les lecteurs humains à comprendre le programme ; et le principal lecteur
humain, c'est toi.

En fait, quand tu reprendras ce programme dans quelques mois, tu
comprendras bien mieux quels sont les commentaires inutiles et quels
sont les commentaires qui manquent !


- les parentheses de:
((etat_rejouer == REJOUER_MENU) || (etat_rejouer == REJOUER_NORMAL))
sont inutiles.



Tu as raison, c'est peut etre bete, mais je vois mieux les conditions
comme ca.



Idem. Au niveau des parenthèses, en dehors de certaines qui sont
nécessaires, le juge suprême c'est toi, si cela te sert il ne faut
surtout pas t'en passer ; de toutes manières tu es encore très loin de
l'excès en la matière.


char lettre;
Un variable de type char est le plus souvent mal typée. En C, une
constante caractère (comme 'a') a pour type int, et ce devrait être le
cas pour les variables qui contiennent des choses similaires. Il y a de
très bonnes raisons (en particulier de pouvoir coder EOF, et de ne pas
perdre bêtement les caractères accentués pour des questions de signe)
pour cela, et le fait d'utiliser char n'a aucun intérêt pratique (cela
ne gagne rien).



Je ne comprends pas cette remarque, il faut que je modifie mes char en
int?



Oui. Par exemple, dans ta nouvelle version de pendu.h, tu as
struct Partie_Pendu {
int nb_coups_joues;
int nb_coups_restants;
int nb_coups_max;
int nb_mots_disponibles;
int mots_ajoutes; /* Valeurs possibles : OUI,
NON */
int premiere_partie; /* Valeurs possibles : OUI,
NON */
char lettre_entest; /* Lettre joué par le joueur
a chaque tour */
char lettres_jouees[TAILLE_MOT_MAX]; /* Contient les lettres
joués par le joueur */
char motSecret[TAILLE_MOT_MAX + 1]; /* Contient le mot a
decouvrir */
int longeur_motSecret; /* Nombre de caractére du
mot secret */
int lettre_cachee_restante; /* Nombre de lettre restante
a decouvrir */
char *lettres_decouvertes; /* Utiliser pour l'affichage
des lettres decouvertes et a decouvrir */
};

Rien qu'à lire ce morceau, je sais que je chercherais des bogues à deux
endroits : d'une part dans la logique qui alimente ou utilise
lettre_entest, lettre accentuée, problème de signé/non signé, EOF ; et
d'autre part débordement de buffer dans lettres_jouees.


/* Le jeu se deroule tant que le joueur veut rejouer */
do {
...
else if (choix_menu == 3) {
affichage(...);
return (0);
...
}
while((etat_rejouer == REJOUER_MENU) ...
return 0;
Cette boucle est beaucoup trop longue. De plus, la logique de sortie est
trop complexe, c'est en partie lié à l'existence de deux variables,
etat_rejouer et choix_menu. En fait, le return(0) est assez horrible,
c'est un saut déguisé, qui montre un problème de conception général de
l'algorithme qui n'a pas été assez poussée.



La boucle est longue car elle gere le deroulement d'une partie. Et
doit se repeter autant de fois que le joueur a envie de rejouer.



Ce n'est pas une bonne raison. En fait, tu devrais arriver à quelque
chose du genre :

/* Le jeu se deroule tant que le joueur veut rejouer */
do {
partie(...);
demande_rejouer(&etat_rejouer);
}
while((etat_rejouer == REJOUER_MENU) ...
return 0;

et comme tu le vois, la boucle n'est pas trop longue.

Et si tu veux poser la question avant le début de la première partie, tu
vas arriver à un truc similaire mais un peu différent

/* Le jeu se deroule tant que le joueur veut rejouer */
while( demande_rejouer() == REJOUER_MENU )
{
partie(...);
}
return 0;

Ce qui montre bien qu'il y a un rapport avec l'algorithmique.


while (motJoue[indice_tableau] != '') {
printf(" %c", motJoue[indice_tableau]);
indice_tableau++;
}
En C, cela s'écrit
printf(" %s", motJoue);



Oui je sais bien. En fait j'ai utilisé - a tort? - cette facon de
faire pour une
raison simple, en utilisant %s j'aurais eu **** alors que je voulais
* * * *.



Au temps pour moi, tu avais raison.
Histoire de me sauver la face, je dirais alors qu'un commentaire ne
serait pas de trop ;-).


Permettre 4 caractères est une intéressante initiative
(en effet, des lettres comme Œ ou Ç peuvent occuper plusieurs bytes;
mais on préferera MBLEN_MAX), mais elle est n'est pas exploitée par la
suite ; l'utilisation de sscanf est sujette à erreur, par exemple ici si
l'utilisateur tape un espace au début cela va être utilisé comme
proposition...


Quel est l'utilisation de MBLEN_MAX?



C'est MB_LEN_MAX en fait, et c'est la longueur maximale que peut avoir
une chaîne de caractère contenant une seule lettre.
En effet, si le C garantit que "a" est une chaîne de longueur 1+1 (pour
le final), il n'en va pas de même pour les lettre accentuées ou
complexes comme Ç ou Œ, ou les lettres des autres alphabets ; pour
manipuler ce genre de lettres, il y a plusieurs solutions (que je ne
vais pas détailler ici parce que cela va sortir du cadre), mais si l'on
utiliser fgets/sscanf il faut alors utiliser des chaînes de taille
MB_LEN_MAX (et ensuite probablement mbtowc ou mbrtowc).

Ici il faut passer outre tout cela, se limiter aux lettres simples de A
à Z, donc il est inutile de permettre à saisie de dépasser un caractère.


À partir de là, il faut regarder un peu le code et on s'aperçoit que
l'on cherche à capter un seul caractère. La bonne fonction pour ce faire
est getchar(), dont l'utilité n'est pas limitée à la vidange du tampon !
Et si tu utilises getchar(), tu vas pouvoir réécrire l'interface de
saisie pour ignorer les espaces en début de ligne, refuser ce qui n'est
pas une lettre ou bien les doublons, deux saisies trop rapprochées
(parce que le clavier a rebondi).
L'avantage d'avoir découpé le code, c'est que cette reformulation du
code peut être réalisée et déboguée sans que le reste du code soit
modifié, c'est tout l'avantage de la modularité : en faisant cela, tu
vas probablement découvrir que ton code réalise plusieurs fois la même
fonction élémentaire (saisir un caractère), donc qu'il faut que tu
extrais cette action dans une fonction, et que tu sous-traites
l'opération d'entrée exclusivement à cette fonction.
Une fois que tu auras mené à bien cette reformulation (en incluant le
cas où tu demandes une option numérique entre 1 et 4 plutôt qu'une
lettre, en effet du point de vue de la gestion des entrées c'est très
similaire, et il n'est nul besoin de faire appel à scanf"%d"), tu
devrais avoir un code beaucoup plus facile à appréhender, avec moins de
chose potentiellement dangereuses.

Et si tu utilises getchar(), alors le résultat doit être rangé dans un
int, ce qui reboucle sur les commentaires précédents sur le type de
lettre_jouee.


printf( "nt Vous avez perdu. Le mot a trouver etait : %s",
On peut utiliser sans souci les accents dans les messages (sauf si on
s'appelle Marc E. et que l'on travaille sur des terminaux zarbi), c'est
quand même plus lisible.



J'aimerais bien, mais sous la console windows j'ai que des erreurs
d'affichages.



Je pense que tu vas trouver des solutions dans l'enfilade qui commence
avec <news:gph7me$pun$
http://groups.google.com/groups?selm=gph7me$pun$

Mais bon, c'est mon avis hein, et je sais que je fais une fixation sur
les problèmes de jeux de caractères...


Antoine
Avatar
Antoine Leca
Le 15/03/2009 18:37, JKB écrivit :
Le 15-03-2009, à propos de
Re: 1er programme, j'aimerais vos commentaires.,
Marc Espie écrivait dans fr.comp.lang.c :
Si tu veux afficher des lignes incompletes, il faut rajouter un appel a
fflush:
printf("Entrez votre valeur: ");
fflush(stdout);



Juste une question pour ma culture personnelle. Est-on sûr que
quelque chose va s'afficher lorsque l'argument du printf() contient un
retour à la ligne ?



man setvbuf, regarder _IOLBF et _IOFBF

Et savoir que « normalement », stdout est positionné en mode _IOLBF
_sauf_ si la bibliothèque est sûre que le flux est redirigé vers autre
chose qu'un dispositif interactif, auquel cas stdout (peut) est mis en
mode _IOFBF.

Voir alors man isatty (Posix)

stderr pour sa part est en mode _IONBF, et c'est une catastrophe en
terme de perfs (genre le programme se traîne à mettre à jour l'écran
caractère par caractère, en général l'indice c'est quand le programme
accélère quand on cache la fenêtre...)


Antoine
Avatar
Mickaël Wolff
Beware wrote:
Est-ce qu'une macro qui me permette de remplacer ces deux lignes est
une chose "orthodoxe" ou pas? Du genre :
#define PRINT(chaine) printf("%s", chaine);
fflush(stdout);




puts(chaine) ;

:)
--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
JKB
Le 15-03-2009, ? propos de
Re: 1er programme, j'aimerais vos commentaires.,
Antoine Leca ?crivait dans fr.comp.lang.c :
Le 15/03/2009 18:37, JKB écrivit :
Le 15-03-2009, à propos de
Re: 1er programme, j'aimerais vos commentaires.,
Marc Espie écrivait dans fr.comp.lang.c :
Si tu veux afficher des lignes incompletes, il faut rajouter un appel a
fflush:
printf("Entrez votre valeur: ");
fflush(stdout);



Juste une question pour ma culture personnelle. Est-on sûr que
quelque chose va s'afficher lorsque l'argument du printf() contient un
retour à la ligne ?



man setvbuf, regarder _IOLBF et _IOFBF

Et savoir que « normalement », stdout est positionné en mode _IOLBF
_sauf_ si la bibliothèque est sûre que le flux est redirigé vers autre
chose qu'un dispositif interactif, auquel cas stdout (peut) est mis en
mode _IOFBF.

Voir alors man isatty (Posix)

stderr pour sa part est en mode _IONBF, et c'est une catastrophe en
terme de perfs (genre le programme se traîne à mettre à jour l'écran
caractère par caractère, en général l'indice c'est quand le programme
accélère quand on cache la fenêtre...)



Merci pour ces informations. On en apprend tous les jours ;-)

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
espie
In article <49bd5922$0$5069$,
Mickaël Wolff wrote:
Beware wrote:
Est-ce qu'une macro qui me permette de remplacer ces deux lignes est
une chose "orthodoxe" ou pas? Du genre :
#define PRINT(chaine) printf("%s", chaine);
fflush(stdout);




puts(chaine) ;



Oui, sauf que ca ne fait pas la meme chose. C'est genant quand meme...
Avatar
Mickaël Wolff
Marc Espie wrote:
Oui, sauf que ca ne fait pas la meme chose. C'est genant quand meme...



Certes, mais c'est toujours intéressant de connaitre cette fonction,
meme si un print("toton") est remplacé par puts("toto") ; par gcc. Je
préfère puts quand printf n'est pas absolument nécessaire.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
Beware
Bonjour,

J'ai pas trop eu le temps de bosser sur mon programme aujourd'hui.
J'ai quand même un peu cherché sur l'utilisation de getchar() et j'en
suis arrivé la pour l'instant :

int main()
{
int c;
int etat = 0;

puts("quel est votre choix : ");

do
{
c = getchar();
etat = 0;

if ( (c >= 97 && c <= 122) || (c >= 65 && c <= 90))
{
puts("BINGO");
printf("saisie : %cn",c);
etat = 1;
}
}
while ((etat != 1 && c != EOF));

return 0;
}

Je vérifie donc que le getchar récupéré un lettre (valeur selon la
table ascii) et j'attends pour quitter une lettre valide (même si une
fin de ligne est arrivé) ou un EOF. Mais je sais pas si la qualité du
code est correcte ou s'il faut que j'arrête de boire avant de
réfléchir :)