Manipulation avancée des tableaux et matrices en langage C

Organisation mémoire et adressage des structures linéaires

En C, un tableau uni-dimensionnel réserve une bloc de mémoire contigu. Le nom du tableau agit comme un pointeur constant vers le premier indice. L'opérateur adresse (&) appliqué au premier élément renvoie strictement la même valeur.

#include <stdio.h>

#define TAILLE 4
#define LIGNES 2

void analyser_tableau_1d() {
    int buffer[TAILLE] = {1, 9, 8, 4};
    size_t idx;

    printf("Taille mémoire totale : %zu octets\n", sizeof(buffer));
    
    for (idx = 0; idx < TAILLE; ++idx) {
        printf("%p → %d\n", (void*)&buffer[idx], buffer[idx]);
    }
    printf("Pointeur tableau : %p\n", (void*)buffer);
}

void analyser_matrice_2d() {
    int grille[LIGNES][TAILLE] = {{1, 9, 8, 4}, {2, 0, 4, 9}};
    size_t i, j;

    printf("Emplacement global : %zu octets\n", sizeof(grille));
    
    for (i = 0; i < LIGNES; ++i) {
        for (j = 0; j < TAILLE; ++j) {
            printf("%p → %d\n", (void*)&grille[i][j], grille[i][j]);
        }
    }
    printf("\nAdresses comparatives :\n");
    printf("Matrice brute      : %p\n", (void*)grille);
    printf("Première ligne     : %p\n", (void*)grille[0]);
    printf("Seconde ligne      : %p\n", (void*)grille[1]);
}

int main() {
    analyser_tableau_1d();
    analyser_matrice_2d();
    return 0;
}

La disposition interne suit un schéma séquentiel par rangée. L'écart entre deux indices de colonne successifs correspond précisément à la taille d'un type int. De même, la différence entre l'adresse de début d'une ligne et celle de la suivante équivaut à la surface mémoire occupée par l'intégralité d'une rangée complète.

Découlement en pointeurs et agrégats statistiques

Lorsqu'un tableau est transmis à une routine, il se transforme implicitement en pointeur vers son premier élément. Cette mécanique permet de manipuler les données sans duplication, mais nécessite de passer explicitement la longueur du segment traité.

#include <stdio.h>

#define CAPACITE_MAX 100

void charger_saisie(int *tab, int compte);
double calculer_moyenne_retenu(int *tab, int compte);

int main() {
    int donnees[CAPACITE_MAX];
    int quantite;
    double resultat;

    while ((printf("Quantité souhaitée : ")) && scanf("%d", &quantite) != EOF) {
        charger_saisie(donnees, quantite);
        resultat = calculer_moyenne_retenu(donnees, quantite);
        printf("Moyenne ajustée : %.2f\n\n", resultat);
    }
    return 0;
}

void charger_saisie(int *tab, int compte) {
    for (int k = 0; k < compte; ++k) {
        scanf("%d", &tab[k]);
    }
}

double calculer_moyenne_retenu(int *tab, int compte) {
    int borne_inf, borne_sup, accumulateur;
    int k;

    borne_inf = tab[0];
    borne_sup = tab[0];
    accumulateur = 0;

    for (k = 0; k < compte; ++k) {
        accumulateur += tab[k];
        if (tab[k] > borne_sup) borne_sup = tab[k];
        if (tab[k] < borne_inf) borne_inf = tab[k];
    }

    return (double)(accumulateur - borne_inf - borne_sup) / (compte - 2);
}

La première routine parcourt la plage demandée pour peupler le tampon fourni en paramètre. La seconde effectue un balayage unique afin de cumuler les valeurs tout en capturant les extrêmes. Le retrait des bornes maximales et minimales avant la division garantit un résultat médian pondéré, insensible aux valeurs aberrantes.

Pas de paramètres bidimensionnels et remplissage initial

Le compilateur exige la connaissance de la largeur (seconde dimension) lors de la déclaration formelle. Cette contrainte permet le calcul correct des offsets mémoire via la formule (ligne × COLONNES + colonne).

#include <stdio.h>

#define DIM_MAX 100

void afficher_grille(int mat[][DIM_MAX], int taille);
void préremplir_matrice(int mat[][DIM_MAX], int taille, int valeur_init);

int main() {
    int maillage[DIM_MAX][DIM_MAX];
    int n, val_a_injecter;

    while ((printf("Dimensions et valeur : ")) && scanf("%d %d", &n, &val_a_injecter) != EOF) {
        préremplir_matrice(maillage, n, val_a_injecter);
        afficher_grille(maillage, n);
        printf("\n");
    }
    return 0;
}

void afficher_grille(int mat[][DIM_MAX], int taille) {
    for (int r = 0; r < taille; ++r) {
        for (int c = 0; c < taille; ++c) {
            printf("%d ", mat[r][c]);
        }
        printf("\n");
    }
}

void préremplir_matrice(int mat[][DIM_MAX], int taille, int valeur_init) {
    for (int r = 0; r < taille; ++r) {
        for (int c = 0; c < taille; ++c) {
            mat[r][c] = valeur_init;
        }
    }
}

La fonction de visualisation restitue le contenu bouchon par bouchon avec une indentation propre. Celle de configuration traverse systématiquement chaque intersection pour y déposer la constante fournie, préparant ainsi la structure pour des calculs ultérieurs.

Détermination de la position centrale

Certains algorithmes nécessitent que les données soient ordonnées préalablement. Une fois la séquence triée, la valeur médiane s'obtient directement par indexation selon la parité du compteur d'éléments.

#include <stdio.h>

#define LIMITE_TAB 100

void recevoir_projections(int *liste, int nbr);
double localiser_mediane(int *liste, int nbr);

int main() {
    int serie[LIMITE_TAB];
    int nb_items;
    double repere_central;

    while ((printf("Nombre d'entrées : ")) && scanf("%d", &nb_items) != EOF) {
        recevoir_projections(serie, nb_items);
        repere_central = localiser_mediane(serie, nb_items);
        printf("Valeur centrale : %g\n\n", repere_central);
    }
    return 0;
}

void recevoir_projections(int *liste, int nbr) {
    for (int pos = 0; pos < nbr; ++pos) {
        scanf("%d", &liste[pos]);
    }
}

double localiser_mediane(int *liste, int nbr) {
    int tampon, cours, passage;
    
    // Tri croissant par échange adjacent
    for (passage = 0; passage < nbr - 1; ++passage) {
        for (cours = 0; cours < nbr - passage - 1; ++cours) {
            if (liste[cours] > liste[cours + 1]) {
                tampon = liste[cours];
                liste[cours] = liste[cours + 1];
                liste[cours + 1] = tampon;
            }
        }
    }

    if (nbr % 2 != 0) {
        return (double)liste[nbr / 2];
    } else {
        return (liste[nbr / 2 - 1] + liste[nbr / 2]) / 2.0;
    }
}

Le processus de réorganisation compare les couples voisins et inverse leur ordre si nécessaire, répétant l'opération jusqu'à obtention d'une séquence ascendante stable. L'accès direct à l'indice central ou la moyenne des deux indices médians fournit alors le critère recherché.

Transformation circulaire de colonnes matricielles

La rotation horizontale impose de décaler chaque rangée vers la droite d'un cran, en faisant revenir l'extrême droit vers la gauche. Une variable intermédiaire protège la donnée migrante durant le glissement.

#include <stdio.h>

#define MAX_DIM 100

void charger_configuration(int mat[][MAX_DIM], int dim);
void exposer_resultat(int mat[][MAX_DIM], int dim);
void appliquer_decalage_droit(int mat[][MAX_DIM], int dim);

int main() {
    int tuile[MAX_DIM][MAX_DIM];
    int taille_matrice;

    printf("Entrez la dimension : ");
    scanf("%d", &taille_matrice);
    
    charger_configuration(tuile, taille_matrice);
    printf("Grille initiale :\n");
    exposer_resultat(tuile, taille_matrice);
    
    appliquer_decalage_droit(tuile, taille_matrice);
    printf("Configuration modifiée :\n");
    exposer_resultat(tuile, taille_matrice);
    
    return 0;
}

void charger_configuration(int mat[][MAX_DIM], int dim) {
    for (int x = 0; x < dim; ++x) {
        for (int y = 0; y < dim; ++y) {
            scanf("%d", &mat[x][y]);
        }
    }
}

void exposer_resultat(int mat[][MAX_DIM], int dim) {
    for (int x = 0; x < dim; ++x) {
        for (int y = 0; y < dim; ++y) {
            printf("%4d", mat[x][y]);
        }
        printf("\n");
    }
}

void appliquer_decalage_droit(int mat[][MAX_DIM], int dim) {
    int sauvegarde_bloc;
    int lig, col;

    for (lig = 0; lig < dim; ++lig) {
        sauvegarde_bloc = mat[lig][dim - 1];
        
        for (col = dim - 1; col > 0; --col) {
            mat[lig][col] = mat[lig][col - 1];
        }
        mat[lig][0] = sauvegarde_bloc;
    }
}

Rapprochement décimal vers bases ciblées

Convertir un entier vers une base quelconque repose sur la division successive et la conservation des restes. L'inversion de la séquence résiduelle permet d'afficher le résultat conforme à la notation standard.

#include <stdio.h>

#define PROFONDEUR_PILE 32

void transcoder_vers_base(int valeur_dec, int cible) {
    int memoire[PROFONDEUR_PILE];
    int haut_pile = 0;
    int chiffre_actuel;

    if (valeur_dec == 0) {
        printf("0\n");
        return;
    }

    while (valeur_dec > 0) {
        memoire[haut_pile++] = valeur_dec % cible;
        valeur_dec /= cible;
    }

    while (haut_pile > 0) {
        chiffre_actuel = memoire[--haut_pile];
        if (chiffre_actuel < 10) {
            printf("%d", chiffre_actuel);
        } else {
            putchar(chiffre_actuel - 10 + 'A');
        }
    }
    printf("\n");
}

int main() {
    int entier_source;
    while ((printf("Valeur décimale : ")) && scanf("%d", &entier_source) != EOF) {
        transcoder_vers_base(entier_source, 2);
        transcoder_vers_base(entier_source, 8);
        transcoder_vers_base(entier_source, 16);
        printf("\n");
    }
    return 0;
}

Validation de structures symétriques

Un carré magique exige que la somme de chaque rangée, colonne et diagonale principale coïncide avec une valeur cible unique. Le test consiste à comparer tous ces agrégats partiels contre cette référence établie sur la première ligne.

#include <stdio.h>

#define BORNE_MAX 100

void importer_fascicule(int grp[][BORNE_MAX], int ct);
void exporter_fascicule(int grp[][BORNE_MAX], int ct);
int verifier_symétrie_totale(int grp[][BORNE_MAX], int ct);

int main() {
    int espace[BORNE_MAX][BORNE_MAX];
    int taille_order;

    while ((printf("Ordre de la matrice : ")) && scanf("%d", &taille_order) != EOF) {
        printf("Entrée des valeurs :\n");
        importer_fascicule(espace, taille_order);
        printf("Restitution :\n");
        exporter_fascicule(espace, taille_order);
        
        if (verifier_symétrie_totale(espace, taille_order)) {
            printf("Confirmation : structure harmonieuse.\n\n");
        } else {
            printf("Échec : déséquilibre détecté.\n\n");
        }
    }
    return 0;
}

void importer_fascicule(int grp[][BORNE_MAX], int ct) {
    for (int r = 0; r < ct; ++r)
        for (int c = 0; c < ct; ++c)
            scanf("%d", &grp[r][c]);
}

void exporter_fascicule(int grp[][BORNE_MAX], int ct) {
    for (int r = 0; r < ct; ++r) {
        for (int c = 0; c < ct; ++c)
            printf("%4d", grp[r][c]);
        printf("\n");
    }
}

int verifier_symétrie_totale(int grp[][BORNE_MAX], int ct) {
    int ref_somme = 0;
    int r, c;

    // Établissement de la norme sur la première rangée
    for (c = 0; c < ct; ++c) ref_somme += grp[0][c];

    // Vérification des lignes restantes
    for (r = 1; r < ct; ++r) {
        int total_ligne = 0;
        for (c = 0; c < ct; ++c) total_ligne += grp[r][c];
        if (total_ligne != ref_somme) return 0;
    }

    // Vérification des colonnes
    for (c = 0; c < ct; ++c) {
        int total_col = 0;
        for (r = 0; r < ct; ++r) total_col += grp[r][c];
        if (total_col != ref_somme) return 0;
    }

    // Diagonale descendante
    int diag1 = 0;
    for (r = 0; r < ct; ++r) diag1 += grp[r][r];
    if (diag1 != ref_somme) return 0;

    // Diagonale ascendante
    int diag2 = 0;
    for (r = 0; r < ct; ++r) diag2 += grp[r][ct - 1 - r];
    if (diag2 != ref_somme) return 0;

    return 1;
}

Recherche de racines numériques uniques

Certaines opérations arithmétiques produisent des combinaisons chiffrées particulières. Identifier le plus petit entier dont le carré et le cube fusionnés contiennent exactement une fois chacun des chiffres de 0 à 9 demande un comptage précis des occurrences.

#include <stdio.h>

int verifier_distribution_digitale(long nombre_cible) {
    long carre = nombre_cible * nombre_cible;
    long cubique = carre * nombre_cible;
    int historique[10] = {0};
    int tmp;

    // Extraction des chiffres du carré
    if (carre == 0) {
        historique[0]++;
    } else {
        while (carre > 0) {
            historique[carre % 10]++;
            carre /= 10;
        }
    }

    // Extraction des chiffres du cube
    if (cubique == 0) {
        historique[0]++;
    } else {
        while (cubique > 0) {
            historique[cubique % 10]++;
            cubique /= 10;
        }
    }

    // Validation stricte : chaque chiffre doit apparaître exactement une fois
    for (tmp = 0; tmp < 10; ++tmp) {
        if (historique[tmp] != 1) return 0;
    }
    return 1;
}

int main() {
    long candidat = 1;
    while (!verifier_distribution_digitale(candidat)) {
        candidat++;
    }
    printf("Résultat trouvé : %ld\n", candidat);
    return 0;
}

Étiquettes: Langage-C tables-mémoire pointeurs-décomposition matrices-rotation tri-insertion

Publié le 11 août à 15h47