Quand je lance la fonction recherche, elle me trouve parfaitement les informations de l'etudiant mais avec la fonction supprimer on dirait que l'email que je passe en argument est fausse !
PierrotLeFou a écrit:
Ou bien tu as des caractères bizarres dans un des e-mail, ou bien il y a des espaces. Fais un printf des deux e-mail en mettant un caractère devant et après: printf("e-mail = '%s'\n", email); Sinon, affiches avec une fonction du genre: #include <ctype.h> void display(char *email) { for(int i=0; email[i]; i++) { if(isprint(email[i])) printf("%c", email[i]); else printf("?"); } printf("\n"); }
Ou bien tu affiches les caractères bizarres avec le format "\\%2X"
printf ("Rentrez l'email de l'etudiant : ");
valeur_de_retour = saisie(email,100);
qu'est le problème ! quel est la déclaration de ton tableau de char email ?
PS : Tu devrais ouvrir et fermer ton fichier dans la fonction, parce que la ça a l'air d'être le bazar on voit un fclose et le fopen doit être un peu plus haut perdu dans le code !
printf ("Rentrez l'email de l'etudiant : ");
valeur_de_retour = saisie(email,100);
qu'est le problème ! quel est la déclaration de ton tableau de char email ?
PS : Tu devrais ouvrir et fermer ton fichier dans la fonction, parce que la ça a l'air d'être le bazar on voit un fclose et le fopen doit être un peu plus haut perdu dans le code !
Merci rouloude l'erreur venait bien de là car je viens de me rendre compte que dans la déclaration j'avais fait :
char email[] = "";
Au lieu de :
char email[100] = ""
Du coup dans le 1er ça voulait dire que email contenait 1 place ( + '\0' )? Comment fait alors fgets pour y écrire plus de caractères ?
Pour le ps , le fichier est ouvert dans la fonction !
Du coup dans le 1er ça voulait dire que email contenait 1 place ( + '\0' )?
Non, email ne peut contenir qu'un caractère, et à la manière dont c'est écrit, juste le \0
Comment fait alors fgets pour y écrire plus de caractères ?
fgets(var,len,file) lit au plus len-1 caractères dans var. Maintenant, si var n'a pas la place pour len-1 caractère, tant pis: quelque chose sera écrasé en mémoire.
Edit: grillé par Pierrot
- Edité par edgarjacobs 17 septembre 2021 à 19:13:21
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le fgets écrit par dessus quelque chose d'autre. Tu détruisais des informations ailleurs.
Mais je croyais que fgets était plus sécurisé que scanf pour les saisie de chaine ?
C'est un peu plus sécurisé, à condition de l'employer correctement avec un paramètre taille compatible avec la taille du tampon. Et se rappeler aussi que le résultat peut contenir la marque de fin de ligne. Ou pas si la ligne est plus grande que le tampon.
- Edité par michelbillaud 17 septembre 2021 à 21:11:23
Le fgets écrit par dessus quelque chose d'autre. Tu détruisais des informations ailleurs.
Mais je croyais que fgets était plus sécurisé que scanf pour les saisie de chaine ?
C'est un peu plus sécurisé, à condition de l'employer correctement avec un paramètre taille compatible avec la taille du tampon. Et se rappeler aussi que le résultat peut contenir la marque de fin de ligne. Ou pas si la ligne est plus grande que le tampon.
- Edité par michelbillaud il y a environ 1 heure
Ah d'accord merci !
J'avais une question, mon menu principal est dans le main ( il n'y a que le menu dans le main en soit ) !
Pourrais-je appeler la fonction main() pour revenir à ce menu ou bien cela n'est pas recommandé en C ?
Si tu aimes les résultats imprévisibles, tu vas être servi ...
Quand tu vas faire un return dans le main, où crois-tu que tu vas aller?
La première fois dans la fonction, la seconde au système.
Et la fonction va retourner à quel endroit? ... Écris-toi une fonction qui contient le menu et qui retourne la valeur résultante. Tu pourras rappeler le menu à répétition au besoin.
Tu peux le faire dans une boucle qui teste le retour et arrête la boucle si on demande d'arrêter.
- Edité par PierrotLeFou 18 septembre 2021 à 7:10:08
Le Tout est souvent plus grand que la somme de ses parties.
Si tu aimes les résultats imprévisibles, tu vas être servi ...
Quand tu vas faire un return dans le main, où crois-tu que tu vas aller?
La première fois dans la fonction, la seconde au système.
Et la fonction va retourner à quel endroit? ... Écris-toi une fonction qui contient le menu et qui retourne la valeur résultante. Tu pourras rappeler le menu à répétition au besoin.
Tu peux le faire dans une boucle qui teste le retour et arrête la boucle si on demande d'arrêter.
En C, le main renvoie toujours un 'int'. (Même si on écrit « main » au lieu de « int main », il renverra un 'int'. C'est une syntaxe datant des années 1970 qui est tolérée par les compilateurs, probablement avec un warning.)
Mais bon, ça n'empêche que la bonne méthode, comme dit plus haut, est de créer une fonction menu qui retourne l'option choisie (par exemple par son numéro), et une fonction qui prend en argument l'option choisie et traite selon l'option. Une boucle « tant que pas terminé » contient les deux appels à ces fonctions. Quelque chose comme :
do {
num_choix = menu(); // affichage du menu, qui retourne le n° de l'option choisie
if (num_choix != 0) // si on n'a pas choisi l'option 0 = fin
traiter(num_choix);
} while (num_choix != 0)
En C, le main renvoie toujours un 'int'. (Même si on écrit « main » au lieu de « int main », il renverra un 'int'. C'est une syntaxe datant des années 1970 qui est tolérée par les compilateurs, probablement avec un warning.)
Mais bon, ça n'empêche que la bonne méthode, comme dit plus haut, est de créer une fonction menu qui retourne l'option choisie (par exemple par son numéro), et une fonction qui prend en argument l'option choisie et traite selon l'option. Une boucle « tant que pas terminé » contient les deux appels à ces fonctions. Quelque chose comme :
do {
num_choix = menu(); // affichage du menu, qui retourne le n° de l'option choisie
if (num_choix != 0) // si on n'a pas choisi l'option 0 = fin
traiter(num_choix);
} while (num_choix != 0)
- Edité par robun il y a environ 1 heure
AH d'accord merci
Pour quitter le programme j'avais fait pour le numero correspondant :
Ah ok du coup ça m'amène à une question : si je demande à l'utilisateur à la fin une fonction supprimer_Etudiant si il veut supprimer un autre etudiant , en cas de reponse favorable, pourrais-je appeler cette meme fonction ?
C.a.d que la fonction s'appellerait elle même et ça me semble pas très correct de procéder ainsi ( bien que j'avais retrouvé ce genre de fonctions en algorithme )
J'ai aussi pensé à faire un goto au tout debut de la fonction ce qui amènera à la redéclaration des variables et même là ça me semble pas très propre ( dans le sens ou goto est dit comme dangereux )
Il vaut mieux poser la question à la fin d'une boucle qui appelle la fonction. Évites les goto et les fonctions récursives (qui s'appellent elles-mêmes). Chaque fois que tu supprimes un étudiant, tu dois ouvrir le fichier, le lire et le récrire en sautant l'étudiant. Il faudrait sans doute revoir la conception de ceci: + ouvrir les fichiers + boucle qui : saisit l'étudiant à supprimer +faire la suppression en lisant et écrivant + demander si on a une autre correction + fermer les fichiers L'idéal serait de saisir tous les étudiants à supprimer et les mettre dans un tableau de structures Tu appelles la fonction avec ton tableau de structures Pour chaque entrée tu vérifies si l'entrée est dans le tableau. Tu l'écris ou pas selon le cas. Ce n'est qu'un exercice, mais si ton fichier était très gros, ça te prendrait moins de temps que de relire le fichier pour chaque suppression.
Le Tout est souvent plus grand que la somme de ses parties.
En fait le problème vient d'une formulation qui pousse dans une mauvaise direction
Si on dit "je voudrais que le traitement décrit par une fonction machin SE répète", on va se dire que la fonction machin SE rappelle elle-même. Et on se précipite à écrire :
void machin() {
.... // le boulot
if (...) {
machin(); // ici
}
}
int main() {
machin();
}
L'autre façon de voir (bien meilleure), c'est de dire que le truc qui appelle la fonction est chargé de la rappeler si il y a besoin. En boucle.
void machin()
{
// le boulot
...
}
int main()
{
do {
machin();
printf("Il faut continuer ?");
...
} while (la réponse est oui);
}
Le piège qui consiste à se précipiter sur la première formulation pour la programmer telle quelle, ça s'appelle la "programmation par mots-clés".
Ici on voit tomber dans ce piège les malheureuses victimes du cours calamiteux d'OC, sur l'exercice "plus ou moins". Ils écrivent le code pour faire une partie, et se demandent comment cette partie doit revenir au début pour rejouer.
C'est pas le code de la partie qui revient au début. C'est une boucle autour du code de la partie qui fait que ça repart.
(En plus ils n'ont pas vu les fonctions à ce moment là, alors bonjour la fonction main de 200 lignes).
- Edité par michelbillaud 18 septembre 2021 à 20:08:04
si je demande à l'utilisateur à la fin une fonction supprimer_Etudiant si il veut supprimer un autre etudiant , en cas de reponse favorable, pourrais-je appeler cette meme fonction ?
Si tu fais comme j'ai proposé, l'utilisateur retourne dans le menu après chaque opération (suppression, modification, etc.) S'il veut supprimer trois étudiants à la suite, eh bien il tapera trois fois le n° de l'option « supprimer un étudiant ».
Après, si tu veux pouvoir supprimer plusieurs étudiants sans revenir au menu, il faut une boucle dans la fonction qui supprime les étudiants. Ça peut être une boucle de ce type :
Faire :
demander quel étudiant on doit supprimer
supprimer l'étudiant
demander si on veut encore supprimer un étudiant
tant qu'on veut encore supprimer un étudiant
ou bien de ce type :
Demander n = le nombre d'étudiants à supprimer
Pour i de 1 à n :
demander quel étudiant on doit supprimer
supprimer l'étudiant
Fin pour
La structure complète :
/* Programme principal */
Faire :
afficher le menu
n = n° de l'option choisie dans le menu
traiter l'option n
tant que n <> 0 // option 0 = fin du programme
/* Affichage du menu */
Afficher le menu
Faire :
demander le n° de l'option choisie
tant que ce n° n'est pas valide
retourner n
/* Traitement de l'option n */
Selon n : // c'est un 'switch'
0 : exit // retour au programme principal
1 : Ajouter un nouvel étudiant
2 : Modifier un étudiant
3 : Supprimer un étudiant
// etc.
Fin selon
/* Suppression d'un étudiant */
Faire :
demander quel étudiant on veut supprimer
supprimer l'étudiant
demander si on veut en supprimer un autre
tant qu'on veut en supprimer un autre
Chaque phrase de cet algorithme correspond probablement à une fonction. Personnellement j'aime bien aller du plus global au plus détaillé, donc commencer par un plan de ce genre. Tu noteras qu'il y a des boucles partout, c'est normal.
Maintenant je suis vers la fin du projet mais un problème m'empêche d'avancer :
J'ai une structure contenant une variable qui provient d'une autre structure ( de type enumeration cependant )
Et donc j'arrive pas à accéder à cette variable ( dans le programme j'ai des erreurs qui me le montre bien )
Voici les deux structures :
typedef enum level
{
LICENSE,
MASTER
} level;
typedef struct classe
{
int code;
char nom[30];
level niveau;
} classe;
J'ai créé une variable :
classe Classe;
Et je pensais demander à l'utilisateur le niveau et de récupérer le résultat avec ma fonction saisie ( car je pensais que Classe.niveau était une chaîne de caractère ou bien une valeur numérique pour le compilateur )
Un type enum est un type entier selon la norme, qui commence à 0 sauf indication contraire. Comme tu sais que tu as 2 éléments seulement, tu peux récupérer la valeur souhaitée dans une variable de type int (ou un autre type entier compatible), puis en affecter le contenu à ton Classe.niveau (après avoir vérifié qu'il correspond à une valeur ayant un sens).
Comme tes choix affichés sont 1 ou 2, et non pas 0 et 1, soit tu adaptes ton printf pour demander 0 ou 1, soit tu changes la définition de ton enum pour qu'il commence à 1 et non pas à 0.
Un type enum est un type entier selon la norme, qui commence à 0 sauf indication contraire. Comme tu sais que tu as 2 éléments seulement, tu peux récupérer la valeur souhaitée dans une variable de type int (ou un autre type entier compatible), puis en affecter le contenu à ton Classe.niveau (après avoir vérifié qu'il correspond à une valeur ayant un sens).
Comme tes choix affichés sont 1 ou 2, et non pas 0 et 1, soit tu adaptes ton printf pour demander 0 ou 1, soit tu changes la définition de ton enum pour qu'il commence à 1 et non pas à 0.
Salut dlks oui finalement en réfléchissant un peu j'ai fait exactement comme tu as dit !
Mais du coup quand j'enregistre les informations de la classe dans un fichier, j'utilise %d pour Classe.niveau et donc l'ordinateur met bien 0 ou 1
Mais quand je veut lire le fichier , je suis donc aussi obligé de mettre cette valeur numérique ( 0 ou 1 ) dans une autre variable puis de l'adapter à Classe.niveau ?
Je pense avoir bien saisi et je peux donc bel et bien continuer ! Merci encore
edgarjacobs a écrit:
Si je lis bien, Classe.niveau est de type level (aka "enum level"), pas de type char *
Mais quand je veut lire le fichier , je suis donc aussi obligé de mettre cette valeur numérique ( 0 ou 1 ) dans une autre variable puis de l'adapter à Classe.niveau ?
Le standard du C indique seulement qu'un type emun est un type entier et que type entier effectivement choisi dépend de l'implémentation (c'est à dire des choix effectués par le concepteur du compilateur).
Par exemple, tu ne peux pas prévoir si un compilateur donné va utiliser un int ou un unsigned int, ou autre chose, pour représenter un enum.
Dans ces conditions, même si cela peut "marcher", il me paraît hasardeux de récupérer directement avec un scanf() une valeur, car tu ne peux pas savoir si un %d ou un %u doit être spécifié et scanf() n'est de toutes façons pas prévu pour récupérer des valeurs de type enum.
Utiliser scanf() avec un des types de spécificateurs supportés par cette instruction est préférable.
En fait, la façon plus sûre de le faire est probablement de ne pas utiliser scanf(), mais de récupérer d'abord la valeur sous forme de chaîne, puis de la convertir en un type long int avec strtol(), cette fonction donnant plus de contrôle sur les erreurs de conversion. Ensuite, tu peux vérifier que la valeur récupérée est une valeur convenant à l'enum, puis l'affecter à l'enum.
Edit : ou mieux, strtoul() puisque l'enum n'est pas sensé avoir des valeurs négatives
Ah d'accord merci maintenant tout fonctionne parfaitement !
Il me reste la gestion des notes maintenant ! Mais j'ai une petite question :
Comment je peut associer une classe à un étudiant ( cela est demandé dans le projet sans plus d'indication ) ? J'ai pensé à ajouter le code de la matière après le nom de chaque étudiant ( dans le fichier des étudiants ) mais je suis pas trop sûr que ça soit simple pour la suite. Car la fonction d'ajouter des notes doit être capable de :
- Ajout des notes d’un étudiant dans une matière : pour ajouter les notes de contrôle
continue (CC) et de DS obtenues par l’étudiant dans la matière dont la référence est fournie.
- Ajout des notes d’un étudiant dans toutes ses matières : Pour chaque matière que
l’étudiant est censé faire (les matières qui se font dans sa classe), les notes de CC et de DS
devront être fournies.
- Ajout des notes d’une classe dans une matière : Les notes de tous les étudiants
appartenant à la classe dont le code est fourni devront être ajoutées. La référence de la
matière en question devra également être renseignée par l’utilisateur.
voici le mcd ( qu'on a traduit depuis le début en mld )
Les structures qu'on a eu grâce au mld :
typedef enum level
{
LICENSE = 1,
MASTER
} level;
typedef struct date
{
int jour;
int mois;
int annee;
} date;
typedef struct etudiant
{
int numero;
char nom[30];
char prenom[50];
char email[100];
date date_naissance;
int code;
} etudiant;
typedef struct classe
{
int code;
char nom[30];
level niveau;
} classe;
typedef struct matiere
{
int reference;
char libelle[50];
int coefficient;
} matiere;
typedef struct etre_Note
{
int numero;
int reference;
double noteCC;
double noteDS;
} etre_Note;
typedef struct se_faire
{
int reference;
int code;
} se_faire;
Je ne sais pas trop quoi faire de la structure se_faire aussi ( créer un nouveau fichier ? Du coup ce serait lui qui associe les matières aux classes ? )
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Le Tout est souvent plus grand que la somme de ses parties.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.