Les Structures

Exercice 1 - Gestion des joueurs et recherche du plus lourd Cet exercice introduit l'utilisation des structures ( struct ) pour regrouper les informations d'une entité, ici un joueur . Le but est de remplir un tableau de joueurs, de vérifier l'unicité de leur numéro de poste, puis de trouver et d'afficher le joueur le plus lourd.

D'après le document Les Structures

Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Les Structures

Document source

Les Structures

Programming, Math · PDF · 13 pages

Afficher l'aperçu du document

Consulter le document original →

Exercice 1 - Gestion des joueurs et recherche du plus lourd

Cet exercice introduit l'utilisation des structures (struct) pour regrouper les informations d'une entité, ici un joueur. Le but est de remplir un tableau de joueurs, de vérifier l'unicité de leur numéro de poste, puis de trouver et d'afficher le joueur le plus lourd.

Le code source contient l'essentiel de la logique, mais nécessite quelques corrections pour être compilé correctement (notamment l'ajout du type de retour int pour main, l'inclusion des bibliothèques nécessaires, et la correction de quelques erreurs typographiques mineures).

Voici le code corrigé et fonctionnel :

#include <stdio.h>

typedef struct {
    int num;
    char nom[21];
    char prenom[21];
    float poid; /* Conservé tel qu'écrit dans le sujet, bien que "poids" prenne un 's' */
} joueur;

/* Fonction pour vérifier si un numéro de poste existe déjà dans le tableau */
int verifier(joueur *t, int i, int n) {
    int j = 0;
    while (i != 0 && j < i) {
        if (t[j].num == n)
            return 1;
        else
            j++;
    }
    return 0;
}

/* Fonction pour saisir les données des joueurs */
void remplissage(joueur *t, int n) {
    int i;
    for (i = 0; i < n; i++) {
        printf("donner le nom : ");
        scanf("%s", t[i].nom);
        printf("donner le prenom : ");
        scanf("%s", t[i].prenom);
        
        do {
            printf("donner le poids : ");
            scanf("%f", &t[i].poid);
        } while (!(t[i].poid > 0));
        
        do {
            printf("donner le num de poste : ");
            scanf("%d", &t[i].num);
        } while (!(verifier(t, i, t[i].num) == 0 && t[i].num >= 1 && t[i].num <= 15));
    }

    printf("/****Affichage du tableau***/\n");
    for (i = 0; i < n; i++) {
        printf("les informations du joueur n %d :\n", t[i].num);
        printf("\t\tnom : %s\n", t[i].nom);
        printf("\t\tprenom : %s\n", t[i].prenom);
        printf("\t\tpoids : %f\n", t[i].poid);
    }
}

/* Fonction pour trouver et afficher le joueur le plus lourd */
void pluslourd(joueur *t, int n) {
    float max; 
    int i = 0, jmax = 0; /* Initialisation de jmax à 0 ajoutée pour éviter les comportements indéfinis */
    
    max = t[0].poid;
    for (i = 1; i < n; i++) {
        if (t[i].poid > max) {
            max = t[i].poid; 
            jmax = i;
        }
    }
    
    printf("\nle joueur le plus lourd c'est le num %d\n", t[jmax].num);
    printf("\t\tnom : %s\n", t[jmax].nom);
    printf("\t\tprenom : %s\n", t[jmax].prenom);
    printf("\t\tpoids : %f\n", t[jmax].poid);
}

int main() {
    int n; 
    joueur t[15];
    
    do {
        printf("donner le nombre du joueur\n");
        scanf("%d", &n);
    } while (!(n > 0 && n <= 15));
    
    remplissage(t, n);
    pluslourd(t, n);

    return 0;
}

Corrections apportées au code source :

  1. Signature de la fonction principale : main() a été remplacé par int main() avec un return 0; à la fin, pour respecter la norme du langage C.
  2. Initialisation : Dans la fonction pluslourd, la variable jmax n'était pas initialisée si le premier joueur (indice 0) était le plus lourd. Je l'ai initialisée à 0.
  3. Variables non utilisées : Dans remplissage, les variables np, existe, et j étaient déclarées mais jamais utilisées. Elles ont été retirées par souci de clarté.

Exercice 2 - Compétitions et participants

Cet exercice manipule des structures imbriquées : un tableau de participant à l'intérieur d'une structure competition. L'objectif est de compter le nombre de compétitions auxquelles un participant donné (identifié par son numéro de carte d'identité, NCIN) a pris part.

Le code proposé dans le sujet est globalement logique. L'utilisation de strcmp (de la bibliothèque <string.h>) est la méthode correcte pour comparer deux chaînes de caractères en C.

Voici la version corrigée :

#include <stdio.h>
#include <string.h>

typedef struct {
    char ncin[9];
    char nom[21];
    char prenom[21];
} participant;

typedef struct {
    int id;
    char nomcomp[10];
    int nbpart;
    participant t[50];
} competition;

participant saisir_part() {
    participant p;
    printf("donner le ncin : ");
    scanf("%s", p.ncin);
    printf("donner le nom : ");
    scanf("%s", p.nom);
    printf("donner le prenom : ");
    scanf("%s", p.prenom);
    return p;
}

void saisir_comp(competition s[], int n) {
    int i, j;
    for (i = 0; i < n; i++) {
        printf("donner l identifiant de la competition : ");
        scanf("%d", &s[i].id);
        printf("donner le nom de la comp : ");
        scanf("%s", s[i].nomcomp);
        printf("donner le nombre de participants : ");
        scanf("%d", &s[i].nbpart);
        
        for (j = 0; j < s[i].nbpart; j++) {
            s[i].t[j] = saisir_part();
        }
    }
}

int nbre_comp(competition s[], int n, char ncin[9]) {
    int i, j, nbre = 0;
    for (i = 0; i < n; i++) {
        for (j = 0; j < s[i].nbpart; j++) {
            if (strcmp(s[i].t[j].ncin, ncin) == 0) {
                nbre++;
                break; /* Un participant ne participe qu'une fois par compétition */
            }
        }
    }
    return nbre;
}

int main() {
    int n;
    char ncin[9];
    competition s13[10];
    
    do {
        printf("donner le nombre total de compétition : ");
        scanf("%d", &n);
    } while (!(n > 0 && n <= 10));
    
    saisir_comp(s13, n);
    
    printf("donner le numero du ncin : ");
    scanf("%s", ncin);
    
    printf("le joueur num %s a participe dans %d competitions\n", ncin, nbre_comp(s13, n, ncin));
    
    return 0;
}

Corrections apportées au code source :

  1. Standardisation : Remplacement de main() par int main() et ajout de return 0;.
  2. Nettoyage des variables : Suppression des variables inutilisées (nb dans saisir_comp, et i, j dans main).
  3. Logique de comptage : J'ai ajouté une instruction break; dans nbre_comp. Si le participant est trouvé dans une compétition, il est inutile de continuer à chercher dans la liste des participants de cette même compétition. On passe directement à la compétition suivante.

Exercice 3 - Manipulation de dates sous forme de chaînes

Dans cet exercice, la date est définie étrangement avec des chaînes de caractères (char) pour le jour, le mois et l'année. Les fonctions atoi (Ascii TO Integer) et sprintf (String Print Formatted) sont employées pour convertir ces chaînes en entiers afin d'effectuer les calculs mathématiques d'incrémentation, puis de reconvertir le résultat en chaînes.

Nous devons respecter cette définition issue du document original, même si l'utilisation d'entiers (int) dans la structure DATE aurait été plus habituelle en C.

#include <stdio.h>
#include <stdlib.h> // contient la fonction atoi

typedef struct date {
    char jour[3];
    char mois[3];
    char annee[5];
} DATE;

int nb_jour_mois(int mois, int annee) {
    switch (mois) {
        case 1: case 3: case 5: case 7: case 8: case 10: case 12:
            return 31;
        case 4: case 6: case 9: case 11:
            return 30;
        case 2:
            /* Note : la règle des années bissextiles complète est (annee % 4 == 0 && annee % 100 != 0) || (annee % 400 == 0) */
            /* Mais nous conservons la logique simplifiée du sujet (annee % 4 == 0) */
            if (annee % 4 == 0)
                return 29;
            else
                return 28;
        default:
            return 0; /* Cas de sécurité ajouté */
    }
}

void saisir_date_initiale(DATE *t) {
    int j, m, a;

    do {
        printf("Donner l'annee : ");
        scanf("%s", t->annee);
        a = atoi(t->annee);
    } while (a < 0);

    do {
        printf("Donner le mois : ");
        scanf("%s", t->mois);
        m = atoi(t->mois);
    } while (m < 0 || m > 12);

    do {
        printf("Donner le nombre de jours : ");
        scanf("%s", t->jour);
        j = atoi(t->jour);
    } while ((j <= 0) || (j > nb_jour_mois(m, a))); /* Correction: j <= 0 au lieu de j < 0 */
}

void saisir_nb_jours(int *n) {
    do {
        printf("Donner le nombre de jours à ajouter : ");
        scanf("%d", n);
    } while (*n <= 0);
}

void dateinc(DATE *t, int n) {
    int j, m, a, temp;
    j = atoi(t->jour);
    m = atoi(t->mois);
    a = atoi(t->annee);
    
    j += n;
    temp = nb_jour_mois(m, a);
    
    while (j > temp) {
        j -= temp;
        m++;
        if (m > 12) {
            m = 1;
            a++;
        }
        temp = nb_jour_mois(m, a);
    }
    
    sprintf(t->jour, "%d", j);
    sprintf(t->mois, "%d", m);
    sprintf(t->annee, "%d", a);
}

void afficher(DATE t) {
    printf("La date est : %s/%s/%s.\n", t.jour, t.mois, t.annee);
}

int main() {
    DATE d;
    int n;
    
    saisir_date_initiale(&d);
    afficher(d);
    saisir_nb_jours(&n);
    dateinc(&d, n);
    afficher(d);
    
    return 0;
}

Corrections apportées au code source :

  1. Traitement des jours : Dans saisir_date_initiale, la condition d'acceptation du jour était j < 0. Un jour à zéro n'existant pas, elle a été remplacée par j <= 0.
  2. L'instruction switch pour le calcul des jours nécessitait un cas par défaut (default: return 0;) pour que le compilateur ne déclenche pas d'avertissement relatif aux fonctions devant retourner une valeur entière.

Exercice 4 - Gestion de stock et commandes

Ce programme gère un catalogue d'articles et traite les commandes clients en mettant à jour les quantités disponibles. L'architecture utilise un tableau global stock.

Le code d'origine comportait un ordre d'instructions inversé lors de la saisie, ainsi qu'un conflit de type sur la variable de récupération des coûts.

#include <stdio.h>
#include <string.h>

typedef struct {
    char modele[21];
    float pu;
    int qtd;
} article;

typedef struct {
    char modele[21];
    int qte;
} commandeunitaire;

/* Tableau global */
article stock[100];

typedef struct {
    char nomclt[21];
    commandeunitaire cu[100];
} commande;

article saisirArticle() {
    article a;
    printf("donner le modele : ");
    scanf("%s", a.modele);
    printf("donner le prix : ");
    scanf("%f", &a.pu);
    printf("donner la quantitee : ");
    scanf("%d", &a.qtd);
    return a;
}

void ajouterArticle(article a, int i) {
    stock[i] = a;
}

int saisircomdunt(commandeunitaire cu[]) {
    char r = 'n';
    int i = 0;
    do {
        /* L'ordre printf puis scanf a été rétabli */
        printf("donner le modele : ");
        scanf("%s", cu[i].modele);

        printf("donner la quantitee : ");
        scanf("%d", &cu[i].qte);

        i++;
        printf("voulez vous continuer o/n ? ");
        scanf(" %c", &r); /* L'espace avant %c absorbe le retour chariot */
    } while (!(r == 'n' || r == 'N'));
    return i;
}

float retirerCommandeUnitaire(article *stock, commandeunitaire cu) {
    int existe = 0, i = 0;
    float r = 0;
    
    do {
        if ((strcmp(stock[i].modele, cu.modele)) == 0) {
            existe = 1;
            if (stock[i].qtd >= cu.qte) { 
                r = cu.qte * stock[i].pu;
                stock[i].qtd = stock[i].qtd - cu.qte;
            } else {
                printf("STOCK INSUFFISANT!!!!!!\n");
            }
        }
        i++;
    } while (!(existe == 1 || i >= 100)); /* Boucle bornée à la taille maximale */

    if (existe == 0)
        printf("STOCK INSDISPONIBLE!!!!!!\n");
        
    return r;
}

void afficheStock(article *stock, int n) {
    int i;
    for (i = 0; i < n; i++) {
        printf("\ninformations d article n %d\n", i + 1);
        printf("\t\tmodele : %s\n", stock[i].modele);
        printf("\t\tprix : %f\n", stock[i].pu);
        printf("\t\tquantitee : %d\n", stock[i].qtd);
    }
}

int main() {
    commande cd;
    int n, i, nbc;
    float s = 0, r; /* Remplacement de 'int r' par 'float r' car retirerCommandeUnitaire retourne un float */
    
    do {
        printf("donner le nombre d article\n");
        scanf("%d", &n);
    } while (!(n > 0 && n <= 100));
    
    for (i = 0; i < n; i++) {
        ajouterArticle(saisirArticle(), i);
    }
    
    printf("****votre stock initial**** \n");
    afficheStock(stock, n);
    
    printf("donner le nom du client : ");
    scanf("%s", cd.nomclt);
    
    nbc = saisircomdunt(cd.cu);
    printf("/***la commande de Mr/Mme %s***/\n", cd.nomclt);
    
    for (i = 0; i < nbc; i++) {
        printf("\t\tmodele %s\n", cd.cu[i].modele);
        printf("\t\tquantite %d\n", cd.cu[i].qte);
    }
    
    for (i = 0; i < nbc; i++) {
        r = retirerCommandeUnitaire(stock, cd.cu[i]);
        if (r != 0)
            s = s + r;
    }
    
    if (s != 0)
        printf("**le prix totale de votre commande %f**\n", s);
        
    printf("\n/****Votre nouveau Stock****/\n");
    afficheStock(stock, n);
    
    return 0;
}

Corrections apportées au code source :

  1. Affichage utilisateur : Dans la fonction saisircomdunt, le scanf pour le modèle arrivait avant le printf d'instruction. L'ordre a été rectifié.
  2. Typer le retour : Dans le programme main, la variable r était définie comme un int, alors qu'elle reçoit le retour de retirerCommandeUnitaire, qui est de type float. La définition a été corrigée.
  3. Espace scanf(" %c") : Placer un espace avant le %c est indispensable en C pour ignorer les sauts de ligne restants dans la mémoire tampon (buffer) après les frappes au clavier précédentes.

Exercice 5 - Gestion d'un album de chansons

Ce programme manipule des durées (minutes, secondes) de chansons. Le document original illustre deux méthodes pour comparer les durées des chansons : la 1ère convertit tout en secondes, la 2ème compare d'abord les minutes, puis les secondes.

En C, il est impossible de déclarer deux fois une fonction portant le même nom dans le même espace. J'ai donc conservé la 1ère méthode comme fonction active, et placé la 2ème méthode sous forme de commentaire explicatif. La fonction récursive pour compter les chansons d'un même interprète est correctement implémentée.

#include <string.h>
#include <stdio.h>

typedef struct {
    int minutes;
    int secondes;
} temps;

typedef struct {
    char titre[30];
    char interpretre[30]; /* Note : L'orthographe 'interpretre' du code original est conservée */
    temps duree;
} chanson;

typedef chanson album[12];

void saisirchanson(chanson *c) {
    printf("donnez le titre\n");
    scanf("%s", c->titre);
    printf("donnez l interprete\n");
    scanf("%s", c->interpretre);
    printf("donnez le nombre de minutes\n");
    scanf("%d", &c->duree.minutes);
    printf("donnez le nombre de secondes\n");
    scanf("%d", &c->duree.secondes);
}

void afficherchanson(chanson c) {
    printf("le titre %s  linterprete %s  le nombre de minute %d et le nombre de secondes %d\n", 
           c.titre, c.interpretre, c.duree.minutes, c.duree.secondes);
}

/* 1ère Méthode (Méthode active dans notre code) */
int comparerduree(chanson c1, chanson c2) {
    int d1, d2;
    d1 = c1.duree.minutes * 60 + c1.duree.secondes;
    d2 = c2.duree.minutes * 60 + c2.duree.secondes;
    
    if (d1 > d2)
        return 1;
    else if (d1 < d2)
        return 2;
    else
        return 0;
}

/* 
 * 2ème Méthode (Proposée dans l'énoncé, mise en commentaire car
 * on ne peut pas définir deux fonctions portant le même nom en C).
 *
 * int comparerduree(chanson c1, chanson c2) {
 *     if(c1.duree.minutes > c2.duree.minutes)
 *         return 1;
 *     else if(c1.duree.minutes < c2.duree.minutes)
 *         return 2;
 *     else {
 *         if(c2.duree.secondes < c1.duree.secondes)
 *             return 1;
 *         else if(c2.duree.secondes > c1.duree.secondes)
 *             return 2;
 *         else
 *             return 0;
 *     }
 * }
 */

void rempliralbum(album a, int n) {
    int i;
    for (i = 0; i < n; i++) {
        saisirchanson(&a[i]);
    }
}

void afficherralbum(album a, int n) {
    int i;
    for (i = 0; i < n; i++) {
        afficherchanson(a[i]);
    }
}

/* Fonction récursive comptant le nombre de chansons d'un interprète donné */
int chansonsmemeinterpret(album a, int n, char intp[]) {
    if (n == 0) {
        return 0;
    } else {
        if (strcmp(a[n-1].interpretre, intp) == 0) {
            afficherchanson(a[n-1]);
            return 1 + chansonsmemeinterpret(a, n - 1, intp);
        } else {
            return chansonsmemeinterpret(a, n - 1, intp);
        }
    }
}

int main() {
    int n, i;
    chanson plusCourteC;
    album a;
    char intp[10];

    printf("donner le nombre de chanson\n");
    scanf("%d", &n);
    rempliralbum(a, n);
    
    printf("\n Affichage Album\n");
    afficherralbum(a, n);
    
    printf("donner le nom de l interprete\n");
    scanf("%s", intp);
    
    printf("\n\nCHANSONS MEME INTERPRETE : \n");
    printf("l interprete %s a %d chanson(s)\n", intp, chansonsmemeinterpret(a, n, intp));
    
    plusCourteC = a[0];
    for (i = 1; i < n; i++) {
        /* comparerduree retourne 2 si la 2eme chanson (a[i]) est plus courte */
        if (comparerduree(plusCourteC, a[i]) == 1) { 
            plusCourteC = a[i];
        }
    }
    
    printf("\n\nPLUS COURTE CHANSON : \n");
    afficherchanson(plusCourteC);
    
    return 0;
}

Méthode

Lorsque vous abordez un document axé sur les structures en C (souvent appelé "TP Structures"), suivez ces principes fondamentaux :

  1. Typage et compilation : Assurez-vous d'avoir toujours des fonctions correctement typées. Le programme d'entrée de votre C doit toujours s'appeler int main() et se terminer par return 0;.
  2. Accès aux champs : Souvenez-vous de la distinction entre les opérateurs :
    • L'opérateur point (.) s'utilise pour accéder à un membre d'une structure directe (par ex. c1.duree.minutes).
    • L'opérateur flèche (->) s'utilise lorsque vous manipulez un pointeur vers une structure (par ex. t->annee), ce qui équivaut à (*t).annee.
  3. Chaînes de caractères : L'usage de scanf("%s", chaine) est standard pour les TP débutants, mais sachez qu'il coupe la lecture dès qu'il rencontre un espace. C'est pour cela que les noms et prénoms saisis ne pourront pas être des noms composés comportant des espaces (comme "Jean Luc"). De plus, on ne place pas l'esperluette & devant un tableau de caractères comme chaine, car le nom du tableau correspond déjà à l'adresse de son premier élément (en revanche, on l'utilise pour les entiers ou les réels : &a.pu).
  4. Validation des entrées : Ces exercices usent généreusement de boucles do { ... } while (...);. C'est une excellente pratique pour garantir que la valeur saisie par l'utilisateur respecte vos conditions (dimensions positives, respect des indices de tableaux, bornes numériques) avant de poursuivre.
  5. Fonctions dupliquées : Un compilateur C classique rejettera systématiquement un fichier possédant plusieurs fonctions portant le même nom. Si un sujet de TP vous demande d'explorer "plusieurs méthodes", vous devez soit les tester à tour de rôle en les commentant, soit leur attribuer des noms différents (par exemple comparerduree_m1 et comparerduree_m2).

Partager

Commentaires

Aucun commentaire pour le moment. Posez la première question.

Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

← Toutes les révisions