Skip to content

EPST 2e année – TP4 Exercice 2

3 décembre 2012

Je vous re-mets l’énoncé du TP N°4 :


Ce TP est pour moi l’occasion de vous parler de deux concepts avancés :

Les tableaux à longueur variable

Pour commencer, voyez cette solution, qui n’utilise pas de fonctions (autres que main()):

/*
 *  EPST 2e année - 2012-2013
 *  TP4 - Tableaux et matrices
 *  Exercice 2 : transposée d'une matrice (version sans fonctions)
 *  Fichier source : transposee_sans_fonctions.c
 *  Compiler avec :
 *  gcc -Wall -o transposee_sans_fonctions transposee_sans_fonctions.c -std=c99
 *  Auteur : Amine "nh2" Brikci-Nigassa
 */
 
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int nblignA, nbcolA;
    printf("Entrez le nombre de lignes de A : ");
    scanf("%d", &nblignA);
    printf("Entrez le nombre de colonnes de A : ");
    scanf("%d", &nbcolA);
    
    double a[nblignA][nbcolA];
    for (int li = 0 ; li < nblignA ; ++li)
        for (int co = 0 ; co < nbcolA ; ++co) {
            printf("A[%d][%d] = ", li ,co);
            scanf("%lf", &a[li][co]);
        }
    
    puts("\nLa matrice A :");
    for (int li = 0 ; li < nblignA ; ++li) {
        for (int co = 0 ; co < nbcolA ; ++co) 
            printf("%g\t", a[li][co]);
        puts("");
    }

    double aprime[nbcolA][nblignA];
    for (int li = 0 ; li < nblignA ; ++li)
        for (int co = 0 ; co < nbcolA ; ++co) 
            aprime[co][li] = a[li][co];

    puts("\nLa transposée de A :");
    for (int li = 0 ; li < nbcolA ; ++li) {
        for (int co = 0 ; co < nblignA ; ++co) 
            printf("%g\t", aprime[li][co]);
        puts("");
    }
    
    return EXIT_SUCCESS;
}

La déclaration d’un tableau (et donc d’une matrice) en C peut se faire de plusieurs manières. Si l’on veut déclarer le tableau comme variable globale, ses dimensions doivent être constantes, déterminées à l’avance, pour que le compilateur fixe la taille de mémoire allouée. Il faut donc réfléchir à la taille qu’on lui donnera : trop grand, il gaspille de la mémoire ; trop petit, il risque de ne pas suffire…

Ce problème se posait autrefois même pour les variables locales. Heureusement, depuis la norme de 1999 (appelée ISO C99), les tableaux qui sont déclarés dans une fonction (main() ou autre), et qui sont donc des variables locales (ou automatiques), peuvent l’être en utilisant une variable (ou n’importe quelle expression) pour la taille. Ainsi, la taille de la mémoire allouée n’est plus fixée à la compilation, mais dynamiquement, à l’exécution. Cette nouvelle famille de tableaux est appelée les tableaux à longueur variable (VLA, variable-length arrays en anglais). Le compilateur GCC est parfaitement conforme à la norme C99 sur ce point depuis la version 4.5, j’en ai donc profité dans le programme ci-dessus.

Avant l’avènement des VLA, le seul moyen d’utiliser « juste ce qu’il faut » de mémoire pour un tableau, était d’utiliser l’allocation dynamique, en appelant les fonctions de la famille de malloc(). Nous verrons cela dans le prochain TP.

Le passage d’une matrice à une fonction

En C, quand un tableau est utilisé, le compilateur le convertit en un pointeur correspondant à son adresse en mémoire (sauf si son nom est cité après les opérateurs & ou sizeof). Quand on passe un tableau en paramètre d’une fonction, la conversion se fait également. Par exemple, si chaine est un tableau de caractères, dans l’instruction scanf("%s",chaine); la fonction scanf reçoit l’adresse de ce tableau, qui est traitée comme un pointeur.

Soit par exemple la déclaration suivante :

int v[10];

Pour déclarer une fonction qui reçoit ce vecteur (tableau à une seule dimension) en paramètre, il est équivalent de déclarer :

  • void fonction(int t[10]);
  • void fonction(int t[]);
  • void fonction(int *t);

Les deux premières manières ne sont que des « commodités » du langage (pour nous aider à comprendre qu’on agit sur un tableau). Seule la troisième montre la vraie nature du paramètre t : un pointeur.

Cependant, quand il s’agit d’un tableau à plusieurs dimensions, comme une matrice, on pourrait croire qu’il y a toujours ces trois façons d’écrire :

  • void fonction(int t[10][10]);
  • void fonction(int t[][]);
  • void fonction(int **t);

Eh bien non ! J’ai barré les deux dernières : elles sont illégales !

C’est plutôt simple à comprendre. Si on a déclaré :

int v[10][10];

alors il est toujours vrai que quand on écrit fonction(v), c’est un pointeur qui est transmis à la fonction, mais de type pointeur sur un tableau de 10 entiers, pas pointeur sur un pointeur ! Dans la fonction, si on écrit t+1,  cela désigne l’adresse où commence le deuxième tableau d’entiers. Comment le compilateur pourrait-il savoir où elle se trouve s’il ne sait pas qu’il y en a 10 ? Il est donc possible d’écrire :

  • void fonction(int t[][10]);
  • void fonction(int (*t)[10])

mais pas :

  • void fonction(int *t[10])

qui signifie tableau de 10 pointeurs sur des entiers.

De la même manière, pour un tableau à trois (resp. quatre,… n) dimensions, les tailles des deux (resp. trois, … n-1) dernières dimensions doivent être données. Par exemple :

void fonction(int t[][10][10]);

Mais alors… Comment allons-nous faire pour notre TP ? Nous n’avons pas la taille de notre matrice !

Si nous ne voulions pas trop nous casser la tête, eh bien, nous pourrions tout simplement fixer la taille de la matrice avec une constante et déclarer nos fonctions de l’une des manières légales (non barrées) ci-dessus… Cependant, je ne sais pas pour vous, mais moi j’aime me casser un peu la tête de temps en temps :-)

Une des solutions consiste à tricher un peu : on peut contourner le problème en déclarant la matrice comme un tableau à une seule dimension et en « linéarisant » la matrice : en mettant ses lignes les unes à la suite des autres, sur une même ligne. Ensuite, pour retrouver l’élément d’indice (i,j), i étant la ligne et j la colonne, il suffit de chercher l’élément du tableau  d’indice (i*taille_ligne+j).

/*
 *  EPST 2e année - 2012-2013
 *  TP4 - Tableaux et matrices
 *  Exercice 2 : transposée d'une matrice (matrices linéarisées)
 *  Fichier source : transposee_linearisee.c
 *  Compiler avec :
 *  gcc -Wall -o transposee_linearisee transposee_linearisee.c -std=c99
 *  Auteur : Amine "nh2" Brikci-Nigassa
 */
 
#include <stdio.h>
#include <stdlib.h>

/* saisir : lit les éléments d'une matrice
 * Paramètres :
 *  - matrice : tableau contenant la matrice
 *  - nblign : nbre de lignes de la matrice
 *  - nbcol : nbre de colonnes de la matrice
 *  - nom : nom de la matrice
 */ 
void saisir(double *matrice, int nblign, int nbcol, char *nom) {
    for (int li = 0 ; li < nblign ; ++li)
        for (int co = 0 ; co < nbcol ; ++co) {
            printf("%s[%d][%d] = ", nom, li ,co);
            scanf("%lf", &matrice[li * nbcol + co]);
        }
}

/* afficher : affiche les éléments d'une matrice
 * Paramètres :
 *  - matrice : tableau contenant la matrice
 *  - nblign : nbre de lignes de la matrice
 *  - nbcol : nbre de colonnes de la matrice
 */
void afficher(double *matrice, int nblign, int nbcol) {
    for (int li = 0 ; li < nblign ; ++li) {
        for (int co = 0 ; co < nbcol ; ++co) 
            printf("%g\t", matrice[li * nbcol + co]);
        puts("");
    }
}

/* trans : calcule la transposée d'une matrice
 * Paramètres :
 *  - matrice : tableau contenant la matrice
 *  - nblign : nbre de lignes de la matrice
 *  - nbcol : nbre de colonnes de la matrice
 *  - transposee : tableau recevant la transposée
 */
void trans(double *matrice, int nblign, int nbcol,
           double *transposee) {
    for (int li = 0 ; li < nblign ; ++li)
        for (int co = 0 ; co < nbcol ; ++co) 
            transposee[co*nblign+li] = matrice[li*nbcol+co];
}

int main(void) {
    int nblignA, nbcolA;
    printf("Entrez le nombre de lignes de A : ");
    scanf("%d", &nblignA);
    printf("Entrez le nombre de colonnes de A : ");
    scanf("%d", &nbcolA);
    
    double a[nblignA * nbcolA];
    saisir(a, nblignA, nbcolA, "A");
    
    puts("\nLa matrice A :");
    afficher(a, nblignA, nbcolA);
    
    double aprime[nbcolA * nblignA];
    trans(a, nblignA, nbcolA, aprime);
    puts("\nLa transposée de A :");
    afficher(aprime, nbcolA, nblignA);        
    
    return EXIT_SUCCESS;
}

Mais il y a mieux. La norme C99, avec ses VLA, permet également de régler le problème dont nous avons parlé en faisant une déclaration de fonction de ce genre :

void fonction(int taille_x, int taille_y, int t[taille_x][taille_y]);

N’est-ce pas merveilleux ? Il suffit donc de passer en paramètre les dimensions de la matrice pour pouvoir les transmettre à la fonction. Comme ça, le compilateur sait quelle est la taille du tableau qui a été passé en paramètre et il ne se plaindra pas. La seule contrainte, c’est que dans la liste des paramètres, les dimensions doivent précéder les tableaux (GCC permet de changer l’ordre en les déclarant deux fois mais ce n’est pas dans la norme).

On obtient donc enfin ma solution finale :

/*
 *  EPST 2e année - 2012-2013
 *  TP4 - Tableaux et matrices
 *  Exercice 2 : transposée d'une matrice
 *  Fichier source : transposee.c
 *  Compiler avec :
 *  gcc -Wall -o transposee transposee.c -std=c99
 *  Auteur : Amine "nh2" Brikci-Nigassa
 */
 
#include <stdio.h>
#include <stdlib.h>

/* saisir : lit les éléments d'une matrice
 * Paramètres :
 *  - matrice : tableau contenant la matrice
 *  - nblign : nbre de lignes de la matrice
 *  - nbcol : nbre de colonnes de la matrice
 *  - nom : nom de la matrice
 */ 
void saisir(int nblign, int nbcol, 
            double matrice[nblign][nbcol], char *nom) {
    for (int li = 0 ; li < nblign ; ++li)
        for (int co = 0 ; co < nbcol ; ++co) {
            printf("%s[%d][%d] = ", nom, li ,co);
            scanf("%lf", &matrice[li][co]);
        }
}

/* afficher : affiche les éléments d'une matrice
 * Paramètres :
 *  - matrice : tableau contenant la matrice
 *  - nblign : nbre de lignes de la matrice
 *  - nbcol : nbre de colonnes de la matrice
 */
void afficher(int nblign, int nbcol,
              double matrice[nblign][nbcol]){
    for (int li = 0 ; li < nblign ; ++li) {
        for (int co = 0 ; co < nbcol ; ++co) 
            printf("%g\t", matrice[li][co]);
        puts("");
    }
}

/* trans : calcule la transposée d'une matrice
 * Paramètres :
 *  - matrice : tableau contenant la matrice
 *  - nblign : nbre de lignes de la matrice
 *  - nbcol : nbre de colonnes de la matrice
 *  - transposee : tableau recevant la transposée
 */
void trans(int nblign, int nbcol, double matrice[nblign][nbcol],
           double transposee[nbcol][nblign]) {
    for (int li = 0 ; li < nblign ; ++li)
        for (int co = 0 ; co < nbcol ; ++co) 
            transposee[co][li] = matrice[li][co];
}

int main(void) {
    int nblignA, nbcolA;
    printf("Entrez le nombre de lignes de A : ");
    scanf("%d", &nblignA);
    printf("Entrez le nombre de colonnes de A : ");
    scanf("%d", &nbcolA);
    
    double a[nblignA][nbcolA];
    saisir(nblignA, nbcolA, a, "A");
    
    puts("\nLa matrice A :");
    afficher(nblignA, nbcolA, a);
    
    double aprime[nbcolA][nblignA];
    trans(nblignA, nbcolA, a, aprime);
    puts("\nLa transposée de A :");
    afficher(nbcolA, nblignA, aprime);        
    
    return EXIT_SUCCESS;
}
Publicités
Laisser un commentaire

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :