Apprentissage C

Bonjour,
depuis quelques temps je me suis mis à apprendre le C (premier langage).
Pour se faire je cherche à réaliser le programme suivant (un minuteur pour mon thé :033 ) :

39 /* 40 ############################################################################################################ 41 42 Teatime 43 Opérations à effectuer par le programme : 44 1. Afficher : 45 ===== Teatime ==== 46 Veuillez entrer votre temps d'infusion, en minutes [défaut : 4] : 47 2. Récupérer le nombre saisi dans une variable 48 3. 49 3.1 Si la variable est un nombre entre 1 et 255 : 50 - On affiche : 51 Une cloche vous avertira dans <variable> minutes. 52 - On décompte le temps et on joue le son d'une cloche (en arrière plan) 53 54 3.2 Si la variable n'est pas un nombre entre 1 et 255 : 55 - On affiche : 56 Veuillez entrer un nombre compris entre 1 et 255 ! 57 - On retourne au début (Veuillez entrer votre temps...) et on recommence tout. 58 59 ############################################################################################################# 60 61 Améliorations : 62 - Ajouter un fichier de conf où l'on décide : 63 1. du temps par défaut 64 2. du chemin vers le fichier du son de cloche 65 3. du nombre de répétition du son de cloche (nombre défini ou jusqu'à l'arrêt du programme) 66 67 - Permettre l'utilisation d'une seule commande du style : 68 teatime -t 4 (pour choisir le temps) -f cloche.ogg (pour choisir la cloche) -r 8 (pour choisir le nombre de répétition de la cloche) 69 70 - Afficher une barre de progression en console dans le style de celle d'abcde 71 - Afficher une barre de progression dans une petite fenêtre sous X ou dans le tray 72 - Utiliser les notifications système sous X pour signifier que le temps est écoulé. 73 74 ############################################################################################################# 75 */

Je bloque déjà à l’opération 3 :

  • soit je fait un while et tant que ma variable n’est pas comprise entre 1 et 255 il redemande d’entrer le nombre, mais le message d’erreur ne s’affiche pas…
  • soit je fait if (variable entre 1 et 255, suite du programme) else (afficher le message d’erreur), mais dans ce cas je ne sais pas comment reprendre à la saisie de la variable…

J’espère ne pas avoir été trop confus…
Si vous pouviez m’expliquer la bonne manière pour faire cela (mais sans m’écrire la code sinon ça n’a plus d’intérêt),

Merci à vous :slightly_smiling: !

Salut,

Tu peux faire un ‘if’ dans un ‘while’ :laughing:

Il me semble qu’une boucle do…while est toute indiquée ici… L’idée est d’exécuter systématiquement une première fois le contenu de la boucle, puis tant que le test n’est pas bon, de répéter cette boucle. En pseudo code, ça donnerait :

do { afficher_message; récupérer_durée; } while (test_durée_est_correcte) // Ensuite tu passes à 3.1

Cela te permet de réunir tes opérations (1, 2) et (3.2) en une seule (ça s’appelle la factorisation du code :033 ).

Merci beaucoup pour vos réponses !
(ça vous parît simple mais j’ai buté quelques heures là dessus :mrgreen: , pas évident de débuter…
Je teste ça tout de suite,
encore merci à vous :slightly_smiling: !

J’ai réussis (enfin presque) mon programme (excepté le décompte et la cloche sur lesquels je me renseigne) grâce à vos conseils.
Je précise que je suis un tuto du site du zéro sur le C.

Voici mon code :

#include <stdio.h> 2 #include <stdlib.h> 3 4 5 6 int main(int argc, char *argv[]) 7 { 8 int choix = 4; 9 int compteur = 0; 10 11 printf("\n===== Teatime =====\n\n"); 12 13 do 14 { 15 if (compteur > 0) 16 { 17 printf("Vous devez entrer un nombre compris entre 1 et 255 !\n"); 18 } 19 20 printf("Veuillez entrer votre temps d'infusion, en minutes [4] : "); 21 scanf("%d", &choix); 22 printf("\n"); 23 compteur++; 24 } 25 while (choix <= 0 || choix >= 256); 26 27 28 printf("Une cloche vous avertira dans %d minutes.\n", choix); 29 printf("\n\n"); 30 31 return 0; 32 }
Je ne me souviens plus comment utiliser la coloration des posts sur le forum…

Il y a deux problèmes :

  • j’initialise ma variable choix à 4 mais si je tape entrée sans nombre lors du scanf il revient à la ligne et me demande toujours un nombre alors que je voudrais qu’il prenne la valeur par défaut comme font beaucoup de programmes console…

  • si je teste à rentrer une lettre ou un mot il me réaffiche la ligne printf jusqu’à ce que je stoppe le programme (plantage) (c’est normal il attend un int mais comment gérer les entrées de l’utilisateur ? (pas un mot là dessus dans le tuto)…

Si vous avez des remarques et suggestions sur mon code se sera avec plaisir,

Merci à vous !!!

Salut,

Oui, le tutoriel de M@teo21 se veut simple et évite les sujets délicats comme la sécurisation des entrées et sorties (note il y a une annexe sur ce sujet à la fin du tuto). Si tu veux vérifier la validité de ce qui a été entré et utiliser une valeur par défaut en cas d’erreur, tu peux utiliser ce bout de code:

if (scanf ("%d", &choix) != 1 || (choix <= 0 || choix >= 256)) {
   choix = 4;
}

while (getchar () != '\n')
   ;

Pour l’explication, la fonction scanf retourne le nombre d’entrées correctes récupérées (en l’occurrence il nous faut un entier). On utilise donc le choix par défaut si ce qu’a entré l’utilisateur est invalide ou si le nombre est négatif ou supérieur à 255. La boucle qui suit vise à vider le tampon du flux d’entrée (cela sera expliquer plus tard) :wink:

Bonjour,

Moi j’utiliserais plutôt fgets que scanf.
(fgets plutôt que gets pour éviter les ‘buffer overrun’).

Ensuite après contrôle de la chaîne retournée par fgets tu convertis en décimal. (par atoi, sscanf…).

et avant de demander une saisie tu vides le tampon d’entrée par fflush(stdin)

Remarque: pour pouvoir faire un "man " dans une fenêtre shell il faut que le paquet “Manual pages about using GNU/Linux for development” soit installé.

Passé par fgets pour moi c’est se compliquer la vie alors que l’on souhaite juste récupérer un nombre. Aussi, plutôt que atoi, il est préférable d’utiliser la fonction strtol, notamment pour pouvoir traiter d’éventuelles erreurs.

Le comportement de fflush sur un flux d’entrée est indéterminé, il est donc nécessaire d’utiliser une boucle si l’on ne veut pas sortir du standard.

Le choix de fgets est pour se faire des traitements génériques.

Pour fflush, le standard pour moi c’est Debian :smiley: .

Il existe aussi la fonction fpurge(…

Merci de vos réponses…
Je continue d’utiliser scanf car fgets me semble compliqué…

Voici ce que ça donne :

[code]#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>

int main(int argc, char *argv[])
{
unsigned char choix = 4; // variable contenant le temps d’infusion
unsigned char compteur = 0; // variable contenant le compteur du nombre d’essais

FILE* conf = NULL; // initialisation du pointeur conf à NULL


if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER) == -1) // initialisation de la sdl audio et timer si erreur :
{
	fprintf(stderr, "Erreur d'initialisation de la SDL");
	exit(EXIT_FAILURE);
}

printf("\n===== Teatime =====\n\n");

/*conf = fopen("/etc/fteatime/fteatime.conf", "r"); // tentative d'ouverture en lecture seule du fichier de conf

if (conf != NULL)
{
// lecture du fichier de conf
fclose(conf); // on ferme le fichier de conf
}
else
{
	// affichage du message d'erreur
	printf("Le fichier de configuration /etc/fteatime/fteatime.conf est introuvable : utilisation des paramètres par défaut.\n\n");
}*/

do
{
	if (compteur > 0) // initialisation du compteur à 0
	{
	printf("Vous devez entrer un nombre compris entre 1 et 255 !\n\n");
	}
	
	printf("Veuillez entrer votre temps d'infusion, en minutes [%d] : ", choix);

	if (scanf ("%d", &choix) != 1 || (choix < 0 || choix >= 256)) // saisie du temps d'infusion par l'utilisateur
	{
	choix = 0;
	}

	while (getchar () != '\n');
	printf("\n");

	compteur++; // incrémentation du compteur à chaque passage de la boucle
}
while (choix <= 0 || choix >= 256);


printf("Une cloche vous avertira dans %d minutes.\n", choix);
SDL_Delay(60000 * choix); //on met le programme en pause 60000ms * le temps d'infusion choix
printf("Votre thé est prêt !\n");
printf("\n\n");
SDL_Quit(); // arrêt de la sdl
return EXIT_SUCCESS;

}
[/code]

J’ai un peu modifié ton code pour continuer d’avoir mon message d’erreur en cas de mauvaise saisie…
L’inconvénient est que “entrée” est considéré comme une mauvaise saisie et ne donne donc plus le choix par défaut…
J’ai tenté :

if (scanf ("%d", &choix) != 1 || != "\n" || (choix < 0 || choix >= 256)) // saisie du temps d'infusion par l'utilisateur
{
choix = 0;
}
else if (scanf ("%d", &choix) == "\n")
{
choix = 4;
}

Mais ça ne marche pas :mrgreen:

Je n’arrive pas non plus à utiliser la SDL :
J’ai installé le paquet libsdl1.2-dev sans problèmes,
les fichiers sont bien dans /usr/include/SDL/ mais à la compilation j’ai ceci :

[code]cc main.c -o main
/tmp/ccbvYD59.o: In function main': main.c:(.text+0x25): undefined reference toSDL_Init’
main.c:(.text+0xfe): undefined reference to SDL_Delay' main.c:(.text+0x117): undefined reference toSDL_Quit’
collect2: ld returned 1 exit status
make: *** [main] Erreur 1

Appuyez sur ENTRÉE ou tapez une commande pour continuer[/code]
Là je sèche…

je cherche également une bibliothèque plutôt haut niveau si possible dans les dépôts (squeeze) pour lire mon fichier de son (.ogg), (le tuto recommande Fmod mais je préfèrerais quelque chose d’intégré à debian)

Merci à vous !

Salut,

J’ai fait ça:

[code]#include <stdio.h>

int main()
{
int choix = 0;

while ((choix < 1)||(choix >256))
{
    fflush(stdin);
    printf("Votre choix:");
    scanf("%d", &choix);
    if (choix >0 && choix < 0xff)
        break;
    printf("Vous devez entrer un nombre entre 1 et 256 !\n");
}
printf("Vous avez choisit:%d\n", choix);
sleep(choix);
printf("Votre thé...");
system("gnome-mplayer ma_music.ogg");

}[/code]

Ne connaissant pas SDL j’ai utilisé “sleep” qui compte en secondes et “system” qui lance un programme système,
ici gnome-mplayer avec une musique ogg.

Si tu veux utiliser SDL il faut que tu link ton programme avec: gcc x.c -llalibrairiequivabien
Chez moi la librairie SDL installée est dynamique (.so) … :smiley:

Pour la “cloche”, printf("\a"); ? :slightly_smiling:

Merci Dixippe !

Donc j’obtiens un programme fonctionnel avec ceci :

[code]#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
unsigned char choix = 4; // variable contenant le temps d’infusion
unsigned char compteur = 0; // variable contenant le compteur du nombre d’essais

FILE* conf = NULL; // initialisation du pointeur conf à NULL



printf("\n===== Teatime =====\n\n");

/*conf = fopen("/etc/fteatime/fteatime.conf", "r"); // tentative d'ouverture en lecture seule du fichier de conf

if (conf != NULL)
{
// lecture du fichier de conf
fclose(conf); // on ferme le fichier de conf
}
else
{
	// affichage du message d'erreur
	printf("Le fichier de configuration /etc/fteatime/fteatime.conf est introuvable : utilisation des paramètres par défaut.\n\n");
}*/

do
{
	if (compteur > 0) // initialisation du compteur à 0
	{
	printf("Vous devez entrer un nombre compris entre 1 et 255 !\n\n");
	}
	
	printf("Veuillez entrer votre temps d'infusion, en minutes [%d] : ", choix);

	if (scanf ("%d", &choix) != 1 || (choix < 0 || choix >= 256)) // saisie du temps d'infusion par l'utilisateur
	{
	choix = 0;
	}

	while (getchar () != '\n');
	printf("\n");

	compteur++; // incrémentation du compteur à chaque passage de la boucle
}
while (choix <= 0 || choix >= 256);


printf("Une cloche vous avertira dans %d minutes.\n", choix);
sleep(60 * choix);
printf("Votre thé est prêt !\n");
system("mplayer -loop 3 /usr/share/sounds/KDE-Sys-App-Message.ogg");
printf("\n\n");
return EXIT_SUCCESS;

}
[/code]
Je ne savais pas que sleep fonctionnait en c aussi.
En fait j’ai déjà un script bash qui fait le même travail, j’ai voulu réaliser ça en C pour le plaisir d’apprendre…
et je ne comprends pas fflush(stdin); si tu voulais bien me l’expliquer, ou mieux je ne sais pas où chercher de la doc sur le C adapté à linux (le tuto du site du zéro est pour windows en grande partie :119 ), où trouve t on les explications de fonctions de bases, et quelles sont les bonnes bibliothèques pour débuter sous linux ?

Merci à vous !

[quote=“LAV-FRED”]Merci Dixippe !

Donc j’obtiens un programme fonctionnel avec ceci :

[code]#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
unsigned char choix = 4; // variable contenant le temps d’infusion
unsigned char compteur = 0; // variable contenant le compteur du nombre d’essais

FILE* conf = NULL; // initialisation du pointeur conf à NULL



printf("\n===== Teatime =====\n\n");

/*conf = fopen("/etc/fteatime/fteatime.conf", "r"); // tentative d'ouverture en lecture seule du fichier de conf

if (conf != NULL)
{
// lecture du fichier de conf
fclose(conf); // on ferme le fichier de conf
}
else
{
// affichage du message d'erreur
printf("Le fichier de configuration /etc/fteatime/fteatime.conf est introuvable : utilisation des paramètres par défaut.\n\n");
}*/

do
{
if (compteur > 0) // initialisation du compteur à 0
{
printf("Vous devez entrer un nombre compris entre 1 et 255 !\n\n");
}

printf("Veuillez entrer votre temps d'infusion, en minutes [%d] : ", choix);

if (scanf ("%d", &choix) != 1 || (choix < 0 || choix >= 256)) // saisie du temps d'infusion par l'utilisateur
{
choix = 0;
}

while (getchar () != '\n');
printf("\n");

compteur++; // incrémentation du compteur à chaque passage de la boucle
}
while (choix <= 0 || choix >= 256);


printf("Une cloche vous avertira dans %d minutes.\n", choix);
sleep(60 * choix);
printf("Votre thé est prêt !\n");
system("mplayer -loop 3 /usr/share/sounds/KDE-Sys-App-Message.ogg");
printf("\n\n");

return EXIT_SUCCESS;
}
[/code]
Je ne savais pas que sleep fonctionnait en c aussi.
En fait j’ai déjà un script bash qui fait le même travail, j’ai voulu réaliser ça en C pour le plaisir d’apprendre…
et je ne comprends pas fflush(stdin); si tu voulais bien me l’expliquer, ou mieux je ne sais pas où chercher de la doc sur le C adapté à linux (le tuto du site du zéro est pour windows en grande partie :119 ), où trouve t on les explications de fonctions de bases, et quelles sont les bonnes bibliothèques pour débuter sous linux ?

Merci à vous ![/quote]

Le meilleur endroit pour apprendre la libc, ca reste le man pour moi. fflush() sert a vider un buffer, en l’occurrence celui de stdin. Maintenant, ne prend pas cette mauvaise habitude: l’utilisation de fflush sur un input stream n’est pas definie et les resultats peuvent differer d’un compilateur a l’autre. Utilises plutot fgetc(stdin) jusqu’a ce qu’il retourne NULL.

Salut,

Tu peux aussi utiliser __fpurge(stdin) à la place de fflush(stdin) :wink:

Pour l’aide sur les fonctions tu fais Système->Administration->Logithèque et tu cherches le paquet:
"Manual pages about using GNU/Linux for development"
Ensuite tu ouvres une fenêtre shell: Applications->Accessoires->Terminal

Et dans le Terminal tu fais “man fpurge” par exemple.

Autrement tu installes de la logithèque “xman” et dans le terminal tu fais xman, dans “Section” tu choisis “(3) Subroutines”.

Tu peux aussi charger la documentation format pdf de la libc: http://www.gnu.org/s/libc/manual/pdf/libc.pdf

Une petite remarque sur ton programme: tu fais des comparaisons négatives sur une variable non signée et la taille d’un unsigned char est normalement de 1 octet soit des valeurs de 0 à 255 (donc la comparaison >256 …
Essais de faire une déclaration unsigned char c1=300 par exemple et gcc va réagir.

Je plussoies pour ne pas utiliser fflush sur un flux d’entrée, par contre la fonction fgetc ne renvoie pas NULL lorsqu’il n’y a plus de caractère à lire, mais EOF. De plus, étant donné qu’il lit ses données depuis un terminal, pour vider le tampon il doit boucler jusqu’à rencontrer un ‘\n’ ou EOF.

D’ailleurs j’ai oublié de gérer le cas où l’utilisateur entre EOF, la boucle devrait donc être:

int c;

while ((c = getchar ()) != '\n' && c != EOF)
   ;