Gestionnaire de contacts en C avec tableau statique

Le gestionnaire repose sur une structure double niveau : une entité Personne encapsulant les données individuelles, et un conteneur Annuaire agrégeant ces entités avec un compteur de population.

Structure des données

typedef struct {
    char identifiant[NOM_TAILLE];
    int annees;
    char genre[GENRE_TAILLE];
    char numero[TELEPHONE_TAILLE];
    char localisation[ADRESSE_TAILLE];
} Personne;

typedef struct {
    Personne repertoire[CAPACITE];
    int effectif;
} Annuaire;

Interface utilisateur

L'interaction se fait via une boucle infinie pilotée par un menu numéroté. Les opérations sont dispatchées par une énumération pour éviter les valeurs magiques.

typedef enum {
    QUITTER = 0,
    AJOUTER,
    SUPPRIMER,
    CONSULTER,
    EDITER,
    AFFICHER,
    TRIER
} Commande;

void afficher_menu(void) {
    printf("╔══════════════════════════════════════╗\n");
    printf("║  1. Insérer    2. Retirer            ║\n");
    printf("║  3. Chercher   4. Mettre à jour      ║\n");
    printf("║  5. Lister     6. Ordonner           ║\n");
    printf("║  0. Terminer                         ║\n");
    printf("╚══════════════════════════════════════╝\n");
}

Cycle de vie de l'annuaire

Initialisation

void preparer_annuaire(Annuaire* cible) {
    assert(cible != NULL);
    cible->effectif = 0;
    memset(cible->repertoire, 0, sizeof(cible->repertoire));
}

Insertion d'un contact

La vérification de capacité précède la saisie séquentielle des champs. L'index d'insertion correspond à la position courante du compteur.

void inserer_personne(Annuaire* cible) {
    assert(cible);
    
    if (cible->effectif >= CAPACITE) {
        printf("Capacité maximale atteinte\n");
        return;
    }
    
    Personne* emplacement = &cible->repertoire[cible->effectif];
    
    printf("Nom : ");
    scanf("%s", emplacement->identifiant);
    printf("Âge : ");
    scanf("%d", &emplacement->annees);
    printf("Sexe : ");
    scanf("%s", emplacement->genre);
    printf("Téléphone : ");
    scanf("%s", emplacement->numero);
    printf("Adresse : ");
    scanf("%s", emplacement->localisation);
    
    cible->effectif++;
    printf("Enregistrement effectué\n");
}

Recherche et accès

Un service interne localise les entrées par correspondance nominale exacte. Il retourne l'indice ou une valeur sentinelle négative.

static int localiser_par_nom(const Annuaire* source, const char* reference) {
    for (int position = 0; position < source->effectif; position++) {
        if (strcmp(source->repertoire[position].identifiant, reference) == 0) {
            return position;
        }
    }
    return -1;
}

void consulter_personne(const Annuaire* source) {
    assert(source);
    
    char critere[NOM_TAILLE];
    printf("Nom recherché : ");
    scanf("%s", critere);
    
    int position = localiser_par_nom(source, critere);
    if (position < 0) {
        printf("Aucune correspondance\n");
        return;
    }
    
    const Personne* trouvee = &source->repertoire[position];
    printf("%-20s %-4s %-6s %-14s %-25s\n", 
           "Nom", "Âge", "Sexe", "Téléphone", "Adresse");
    printf("%-20s %-4d %-6s %-14s %-25s\n",
           trouvee->identifiant, trouvee->annees, 
           trouvee->genre, trouvee->numero, trouvee->localisation);
}

Mutations

Suppression par décalage

L'effacement conserve l'ordre physique par translation des éléments suivants vers la gauche.

void retirer_personne(Annuaire* cible) {
    assert(cible);
    
    if (cible->effectif == 0) {
        printf("Annuaire vide\n");
        return;
    }
    
    char critere[NOM_TAILLE];
    printf("Nom à supprimer : ");
    scanf("%s", critere);
    
    int position = localiser_par_nom(cible, critere);
    if (position < 0) {
        printf("Entrée introuvable\n");
        return;
    }
    
    for (int curseur = position; curseur < cible->effectif - 1; curseur++) {
        cible->repertoire[curseur] = cible->repertoire[curseur + 1];
    }
    
    cible->effectif--;
    printf("Suppression confirmée\n");
}

Modification in-place

void actualiser_personne(Annuaire* cible) {
    assert(cible);
    
    char critere[NOM_TAILLE];
    printf("Nom à modifier : ");
    scanf("%s", critere);
    
    int position = localiser_par_nom(cible, critere);
    if (position < 0) {
        printf("Entrée inexistante\n");
        return;
    }
    
    Personne* cible_modif = &cible->repertoire[position];
    
    printf("Nouveau nom : ");
    scanf("%s", cible_modif->identifiant);
    printf("Nouvel âge : ");
    scanf("%d", &cible_modif->annees);
    printf("Nouveau sexe : ");
    scanf("%s", cible_modif->genre);
    printf("Nouveau téléphone : ");
    scanf("%s", cible_modif->numero);
    printf("Nouvelle adresse : ");
    scanf("%s", cible_modif->localisation);
    
    printf("Mise à jour effectuée\n");
}

Présentation et organisation

Affichage tabulaire

void lister_contacts(const Annuaire* source) {
    assert(source);
    
    if (source->effectif == 0) {
        printf("Aucun contact enregistré\n");
        return;
    }
    
    printf("%-20s %-4s %-6s %-14s %-25s\n", 
           "Nom", "Âge", "Sexe", "Téléphone", "Adresse");
    
    for (int position = 0; position < source->effectif; position++) {
        const Personne* courant = &source->repertoire[position];
        printf("%-20s %-4d %-6s %-14s %-25s\n",
               courant->identifiant, courant->annees,
               courant->genre, courant->numero, courant->localisation);
    }
}

Tri par ordre alphabétique

L'algorithme de tri à bulles réorganise le tableau selon la comparaison lexicographique des identifiants.

void ordonner_repertoire(Annuaire* cible) {
    for (int limite = cible->effectif - 1; limite > 0; limite--) {
        for (int courant = 0; courant < limite; courant++) {
            Personne* premier = &cible->repertoire[courant];
            Personne* suivant = &cible->repertoire[courant + 1];
            
            if (strcmp(premier->identifiant, suivant->identifiant) > 0) {
                Personne tampon = *premier;
                *premier = *suivant;
                *suivant = tampon;
            }
        }
    }
    printf("Tri alphabétique terminé\n");
}

Programme principal

int main(void) {
    Annuaire carnet;
    preparer_annuaire(&carnet);
    
    int instruction = 0;
    do {
        afficher_menu();
        printf("Votre choix : ");
        scanf("%d", &instruction);
        
        switch (instruction) {
            case QUITTER:
                printf("Fermeture du programme\n");
                break;
            case AJOUTER:
                inserer_personne(&carnet);
                break;
            case SUPPRIMER:
                retirer_personne(&carnet);
                break;
            case CONSULTER:
                consulter_personne(&carnet);
                break;
            case EDITER:
                actualiser_personne(&carnet);
                break;
            case AFFICHER:
                lister_contacts(&carnet);
                break;
            case TRIER:
                ordonner_repertoire(&carnet);
                break;
            default:
                printf("Option non reconnue\n");
        }
    } while (instruction != QUITTER);
    
    return 0;
}

Limites et évolutions

L'implémentation actuelle utilise un tableau de taille fixe. L'allocation dynamique via malloc/realloc permettrait une capacité extensible à la volée. La persistance sur disque pourrait être ajoutée par sérialisation dans un fichier texte ou binaire.

Étiquettes: C structure de données tableau statique Gestion de Mémoire algorithme de tri

Publié le 1 août à 09h43