Licence CC 0

Les tableaux

Dernière mise à jour :

Poursuivons notre tour d’horizon des données complexes avec les tableaux.

Comme les structures, les tableaux sont des regoupements de plusieurs objets. Cependant, à l’inverse de celles-ci, les tableaux regroupe des données de même type et de manière contiguë (ce qui exclut la présence de multiplets de bourrage).

Représentation d’un tableau en mémoire

Un tableau est donc un gros bloc de mémoire de taille finie qui commence à une adresse déterminée : celle de son premier élément.

Les tableaux simples (à une dimension)

Définition

La définition d’un tableau nécessite trois informations :

  • le type des éléments du tableau (rappelez-vous : un tableau est une suite de données de même type) ;
  • le nom du tableau (en d’autres mots, son identificateur) ;
  • la longueur du tableau (le nombre d’éléments qui le composent). Cette dernière doit obligatoirement être une constante entière.
1
type identificateur[longueur];

Comme vous le voyez, la syntaxe de la déclaration d’un tableau est similaire à celle d’une variable, la seule différence étant qu’il vous est nécessaire de préciser le nombre d’éléments entre crochets à la suite de l’identificateur du tableau.

Ainsi, si nous souhaitons par exemple définir un tableau contenant vingt int, nous devons procéder comme suit.

1
int tab[20];

Initialisation

Comme pour les variables, il est possible d’initialiser un tableau ou, plus précisément, tout ou partie de ses éléments.

Initialisation avec une longueur explicite

L’initialisation se réalise de la même manière que pour les structures, c’est-à-dire à l’aide d’une liste d’initialisation.

1
int tab[3] = { 1, 2, 3 };

L’exemple ci-dessus initialise les trois membres du tableau avec les valeurs 1, 2 et 3.

Comme pour les structures, dans le cas où vous ne fournissez pas un nombre suffisant de valeurs, les éléments oubliés seront initialisés à zéro ou, s’il s’agit de pointeurs, seront des pointeurs nuls.

Pour un tableau de structures, la liste d’initialisation comportera elle-même une liste d’initialistation pour chaque structure composant le tableau.

1
struct temps tab[2] = { { 12, 45, 50.6401 }, { 13, 30, 35.480 } } ; 

Notez que, inversément, une structure peut comporter des tableaux comme membres.

Initialisation avec une longueur implicite

Lorsque vous initialisez un tableau, il vous est permis d’omettre la longueur de celui-ci, car le compilateur sera capable d’en déterminer la taille en comptant le nombre d’éléments présents dans la liste d’initialisation. Ainsi, l’exemple ci-dessous est correct et défini un tableau de trois int valant respectivement 1, 2 et 3.

1
int tab[] = { 1, 2, 3 };

Accès aux éléments d’un tableau

L’accès aux éléments d’un tableau se réalise à l’aide d’un indice, un nombre entier correspondant à la position de chaque élément dans le tableau (premier, deuxième, troisième, etc). Cependant, il y a une petite subtilité : les indices commencent toujours à zéro.

Ceci tient au fait que l’accès aux différents éléments est réalisé à l’aide de l’adresse du premier élément à laquelle est ajouté l’indice (qui doit donc être nul pour conserver l’adresse du premier élément). Étant donné que tous les éléments ont la même taille et se suivent en mémoire, leurs adresses peuvent effectivement se calculer à l’aide de l’adresse du premier élément et d’un décalage par rapport à celle-ci (l’indice, donc).

Prenons un exemple avec un tableau composés de int (ayant une taille de quatre octets) et dont le premier élément est placé à l’adresse 1008. Si vous déterminez à la main les adresses de chaque élèment, vous obtiendrez ceci.

Indice Adresse de l’élément
0 1008 (1008 + 0)
1 1012 (1008 + 4)
2 1016 (1008 + 8)
3 1020 (1008 + 12)
4 1024 (1008 + 16)
5 1028 (1008 + 20)

En fait, il est possible de reformuler ceci à l’aide d’une multiplication entre l’indice et la taille d’un int.

Indice Adresse de l’élément
0 1008 (1008 + (0 * 4))
1 1012 (1008 + (1 * 4))
2 1016 (1008 + (2 * 4))
3 1020 (1008 + (3 * 4))
4 1024 (1008 + (4 * 4))
5 1028 (1008 + (5 * 4))

Nous pouvons désormais formaliser mathématiquement tout ceci en posant $T$ la taille d’un élément du tableau, $i$ l’indice de cet élément, et $A$ l’adresse de début du tableau (l’adresse du premier élément, donc). L’adresse de l’élément d’indice $i$ s’obtient en calculant $A + T \times i$. Ceci étant posé, voyons à présent comment mettre tout cela en œuvre en C.

Le premier élément

Pour commencer, nous avons besoin de l’adresse du premier élément du tableau. Celle-ci s’obtient en fait d’une manière plutôt contre-intuitive : lorsque vous utilisez une variable de type tableau dans une expression, celle-ci est convertie implicitement en un pointeur constant sur son premier élément. Comme vous pouvez le constater dans l’exemple qui suit, nous pouvons utiliser la variable tab comme nous l’aurions fait s’il s’agissait d’un pointeur.

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(void)
{
    int tab[3] = { 1, 2, 3 };

    printf("Premier élément : %d\n", *tab);
    return 0;
}
1
Premier élément : 1

Notez que comme il s’agit d’une conversion implicite vers un pointeur constant, il n’est pas possible d’affecter une valeur à une variable de type tableau. Ainsi, le code suivant est incorrect.

1
2
3
4
int t1[3];
int t2[3];

t1 = t2; /* Incorrect. */

La règle de conversion implicite comprends néanmoins deux exceptions : l’opérateur & et l’opérateur sizeof.

Lorsqu’il est appliqué à une variable de type tableau, l’opérateur & produit comme résultat l’adresse du premier élément du tableau. Si vous exécutez le code ci-dessous, vous constaterez que les deux expressions donnent un résultat identique.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>


int main(void)
{
    int tab[3];

    printf("%p == %p\n", (void *)tab, (void *)&tab);
    return 0;
}

Dans le cas où une expression de type tableau est fournie comme opérande de l’opérateur sizeof, le résultat de celui-ci sera bien la taille totale du tableau (en multiplets) et non la taille d’un pointeur.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>


int main(void)
{
    int tab[3];
    int *ptr;

    printf("sizeof tab = %u\n", (unsigned)sizeof tab);
    printf("sizeof ptr = %u\n", (unsigned)sizeof ptr);
    return 0;
}
1
2
sizeof tab = 12
sizeof ptr = 8

Cette propriété vous permet d’obtenir le nombre d’éléments d’un tableau à l’aide de l’expression suivante.

1
sizeof tab / sizeof tab[0]

Les autres éléments

Pour accéder aux autres éléments, il va nous falloir ajouter la position de l’élément voulu à l’adresse du premier élément et ensuite utiliser l’adresse obtenue. Toutefois, recourir à la formule présentée au-dessus ne marchera pas car, en C, les pointeurs sont typés. Dès lors, lorsque vous additionnez un nombre à un pointeur, le compilateur multiplie automatiquement ce nombre par la taille du type d’objet référencé par le pointeur. Ainsi, pour un tableau de int, l’expression tab + 1 est implicitement convertie en tab + sizeof(int).

Voici un exemple affichant la valeur de tous les éléments d’un tableau.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>


int main(void)
{
    int tab[3] = { 1, 2, 3 };

    printf("Premier élément : %d\n", *tab);
    printf("Deuxième élément : %d\n", *(tab + 1));
    printf("Troisième élément : %d\n", *(tab + 2));
    return 0;
}
1
2
3
Premier élément : 1
Deuxième élément : 2
Troisième élément : 3

L’expression *(tab + i) étant quelque peu lourde, il existe un opérateur plus concis pour réaliser cette opération : l’opérateur []. Celui-ci s’utilise de cette manière.

1
expression[indice]

Ce qui est équivalent à l’expression suivante.

1
*(expression + indice)

L’exemple suivant est donc identique au précédent.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>


int main(void)
{
    int tab[3] = { 1, 2, 3 };

    printf("Premier élément : %d\n", tab[0]);
    printf("Deuxième élément : %d\n", tab[1]);
    printf("Troisième élément : %d\n", tab[2]);
    return 0;
}

Parcours et débordement

Une des erreurs les plus fréquente en C consiste à dépasser la taille d’un tableau, ce qui est appelé un cas de débordement (overflow en anglais). En effet, si vous tentez d’accéder à un objet qui ne fait pas partie de votre tableau, vous réalisez un accès mémoire non autorisé, ce qui provoquera un comportement indéfini. Cela arrive généralement lors d’un parcours de tableau à l’aide d’une boucle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>


int main(void)
{
    int tableau[5] = {784, 5, 45, -12001, 8};
    int somme = 0;
    unsigned i; 

    for (i = 0; i <= 5; ++i)
        somme += tableau[i];

    printf("%d\n", somme);
    return 0;
}

Le code ci-dessus est volontairement erroné et tente d’accéder à un élément qui se situe au-delà du tableau. Ceci provient de l’utilisation de l’opérateur <= à la place de l’opérateur < ce qui entraîne un tour de boucle avec i qui est égal à 5, alors que le dernier indice du tableau doit être quatre.

N’oubliez pas : les indices d’un tableau commencent toujours à zéro. En conséquence, les indices valides d’un tableau de $n$ éléments vont de 0 à $n-1$.

Tableaux et fonctions

Passage en argument

Étant donné qu’un tableau peut être utilisé comme un pointeur sur son premier élément, lorsque vous passer un tableau en argument d’une fonction, celle-ci reçoit un pointeur vers le premier élément du tableau. Le plus souvent, il vous sera nécessaire de passer également la taille du tableau afin de pouvoir le parcourir.

Le code suivante utilise une fonction pour parcourir un tableau d’entier et afficher la valeur de chacun de ses éléments.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>


void affiche_tableau(int *tab, unsigned taille)
{
    unsigned i;

    for (i = 0; i < taille; ++i)
        printf("tab[%u] = %d\n", i, tab[i]);
}


int main(void)
{
    int tab[5] = { 2, 45, 67, 89, 123 };

    affiche_tableau(tab, 5);
    return 0;
}
1
2
3
4
5
tab[0] = 2
tab[1] = 45
tab[2] = 67
tab[3] = 89
tab[4] = 123

Notez qu’il existe une syntaxe alternative pour déclarer un paramètre de type tableau héritée du langage B (voyez la dernière section).

1
void affiche_tableau(int tab[], unsigned taille)

Toutefois, nous vous conseillons de recourir à la première écriture, cette dernière étant plus explicite.

Retour de fonction

De la même manière que pour le passage en argument, retourner un tableau revient à retourner un pointeur sur le premier élément de celui-ci. Toutefois, n’oubliez pas les problématiques de classe de stockage ! Si vous retournez un tableau de classe de stockage automatique, vous fournissez à la fonction appelante un pointeur vers un objet qui n’existe plus (puisque l’exécution de la fonction appelée est terminée).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>


int *tableau(void)
{
    int tab[5] = { 1, 2, 3, 4, 5 };

    return tab;
}


int main(void)
{
    int *p = tableau(); /* Incorrect. */

    printf("%d\n", p[0]);
    return 0;
}

La vérité sur les tableaux

Nous vous avons dit qu’une variable de type tableau pouvait être utilisée comme un pointeur constant sur son premier élément. Cependant, ce n’est pas tout à fait vrai.

Un peu d’histoire

Le prédécesseur du langage C était le langage B. Lorsque le développement du C a commencé, un des objectifs était de le rendre autant que possible compatible avec le B, afin de ne pas devoir (trop) modifier les codes existants (un code écrit en B pourrait ainsi être compilé avec un compilateur C sans ou avec peu de modifications). Or, en B, un tableau se définissait comme suit.

1
auto tab[3];

Le langage B était un langage non typé, ce qui explique l’absence de type dans la définition. Le mot-clé auto (toujours présent en langage C, mais devenu obsolète) servait à indiquer que la variable définie était de classe de stockage automatique.

Toutefois, à la différence du langage C, cette définition créée un tableau de trois éléments et un pointeur initialisé avec l’adresse du premier élément. Ainsi, pour créer un pointeur, il suffisait de définir une variable comme un tableau de taille nulle.

1
auto ptr[];

Le langage C, toujours en gestation, avait repris ce mode de fonctionnement. Cependant, les structures sont arrivées et les problèmes avec. En effet, prenez ce bout de code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

struct exemple {
    int tab[3];
};


struct exemple exemple_init(void)
{
    struct exemple init = { { 1, 2, 3 } };

    return init;
}


int main(void)
{
    struct exemple s = exemple_init();

    printf("%d\n", s.tab[0]);
    return 0;
}

La fonction exemple_init() retourne une structure qui est utilisée pour initialiser la variable de la fonction main(). Dans un tel cas, comme pour n’importe quelle variable, le contenu de la première structure sera intégralement copié dans la deuxième. Le souci, c’est que si une définition de tableau créer un tableau et un pointeur initialisé avec l’adresse du premier élément de celui-ci, alors il est nécessaire de modifier le champ tab de la structure s lors de la copie sans quoi son champ tab pointera vers le tableau de la structure init (qui n’existera plus puisque de classe de stockage automatique) et non vers le sien. Voilà qui complexifie la copie de structures, particulièrement si sa définition comprend plusieurs tableaux possiblement imbriqués…

Pour contourner ce problème, les concepteurs du langage C ont imaginé une solution (tordue) qui est à l’origine d’une certaine confusion dans l’utilisation des tableaux : une variable de type tableau ne sera plus un pointeur, mais sera convertie en un pointeur sur son premier élément lors de son utilisation.

Conséquences de l’absence d’un pointeur

Étant donné qu’il n’y a plus de pointeur alloué, la copie de structures s’en trouve simplifiée et peut être réalisée sans opération particulière (ce qui était l’objectif recherché).

Toutefois, cela entraîne une autre conséquence : il n’est plus possible d’assigner une valeur à une variable de type tableau, seuls ses éléments peuvent se voir affecter une valeur. Ainsi, le code suivant est incorrect puisqu’il n’y a aucun pointeur pour recevoir l’adresse du premier élément du tableau t2.

1
2
3
4
int t1[3];
int t2[3];

t1 = t2; /* Incorrect. */

Également, puisqu’une variable de type tableau n’est plus un pointeur, celle-ci n’a pas d’adresse. Dès lors, lorsque l’opérateur & est appliqué à une variable de type tableau, le résultat sera l’adresse du premier élément du tableau puisque seuls les éléments du tableau ont une adresse.

Les tableaux multidimensionnels

Jusqu’à présent, nous avons travaillés avec des tableaux linéaires, c’est-à-dire dont les éléments se suivaient les uns à la suite des autres. Il s’agit de tableaux dit à une dimension ou unidimensionnels.

Cependant, certaines données peuvent être représentées plus simplement sous la forme de tableaux à deux dimensions (autrement dit, organisées en lignes et en colonnes). C’est par exemple le cas des images (non vectorielles) qui sont des matrices de pixels ou, plus simplement, d’une grille de Sudoku qui est organisée en neuf lignes et en neuf colonnes.

Le langage C vous permet de créer et de gérer ce type de tableaux dit multidimensionnels (en fait, des tableaux de tableaux) et ce, bien au-delà de deux dimensions.

Définition

La définition d’un tableau multidimensionnel se réalise de la même manière que celle d’un tableau unidimensionnel si ce n’est que vous devez fournir la taille des différentes dimensions.

Par exemple, si nous souhaitons définir un tableau de int de vingt lignes et trente-cinq colonnes, nous procèderons comme suit.

1
int tab[20][35];

De même, pour un tableau de double à trois dimensions.

1
double tab[3][4][5];

Initialisation

Initialisation avec une longueur explicite

Comme pour les tableaux de structures, l’initialisation d’un tableau multidimensionnel s’effectue à l’aide d’une liste d’initialisation comprenant elle-même des listes d’initialisations.

1
2
int t1[2][2] = { { 1, 2 }, { 3, 4 } };
int t2[2][2][2] = { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } };

Comme pour les tableaux unidimensionnel, dans le cas où vous ne fournissez pas un nombre suffisant de valeurs, les éléments omis seront initialisés à zéro ou, s’il s’agit de pointeurs, seront des pointeurs nuls.

Initialisation avec une longueur implicite

Lorsque vous initialisez un tableau multidimensionnel, il vous est permis d’omettre la taille de la première dimension. La taille des autres dimensions doit en revanche être spécifiée, le compilateur ne pouvant déduire la taille de toutes les dimensions.

1
2
int t1[][2] = { { 1, 2 }, { 3, 4 } };
int t2[][2][2] = { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } };

Utilisation

Techniquement, un tableau multidimensionnel est un tableau dont les éléments sont eux-mêmes des tableaux. Dès lors, vous avez besoin d’autant d’indices qu’il y a de dimensions. Par exemple, pour un tableau à deux dimensions, vous avez besoin d’un premier indice pour accéder à l’élément souhaité du premier tableau, mais comme cet élément est lui-même un tableau, vous devez utiliser un second indice pour sélectionner un élément de celui-ci. Illustration.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>


int main(void)
{
    int tab[2][2] = { { 1, 2 }, { 3, 4 } };

    printf("tab[0][0] = %d\n", tab[0][0]);
    printf("tab[0][1] = %d\n", tab[0][1]);
    printf("tab[1][0] = %d\n", tab[1][0]);
    printf("tab[1][1] = %d\n", tab[1][1]);
    return 0;
}
1
2
3
4
tab[0][0] = 1
tab[0][1] = 2
tab[1][0] = 3
tab[1][1] = 4

Représentation en mémoire

Techniquement, les données d’un tableau multidimensionnel sont stockées les unes à coté des autres en mémoire : elles sont rassemblées dans un tableau à une seule dimension. Si les langages comme le FORTRAN mémorisent les colonnes les unes après les autres (column-major order en anglais), le C mémorise les tableaux lignes par lignes (row-major order).

Exemple de tableau en deux dimensions
Column-major order
Row-major order

Le calcul d’adresse à effectuer est une généralisation du calcul vu au chapitre précédent.

Parcours

Le même exemple peut être réalisé à l’aide de deux boucles imbriquées afin de parcourir le tableau.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>


int main(void)
{
    int tab[2][2] = { { 1, 2 }, { 3, 4 } };
    unsigned i;
    unsigned j;

    for (i = 0; i < 2; ++i)
        for (j = 0; j < 2; ++j)
            printf("tab[%u][%u] = %d\n", i, j, tab[i][j]);

    return 0;
}

Tableaux multidimensionnels et fonctions

Passage en argument

Souvenez-vous : sauf exceptions, un tableau est converti en un pointeur sur son premier élément. Dès lors, qu’obtenons-nous lors du passage d’un tableau à deux dimensions en argument d’une fonction ? Le premier élément du tableau est un tableau, donc un pointeur sur… Un tableau ( oui). :p

La syntaxe d’un pointeur sur tableau est la suivante.

1
type (*identificateur)[taille];

Vous remarquerez la présence de parenthèses autour du symbole * et de l’identificateur afin de signaler au compilateur qu’il s’agit d’un pointeur sur un tableau et non d’un tableau de pointeurs. Également, notez que la taille du tableau référencé doit être spécifiée. En effet, sans celle-ci, le compilateur ne pourrait pas opérer correctement le calcul d’adresses puisqu’il ne connaîtrait pas la taille des éléments composant le tableau référencé par le pointeur.

La même logique peut-être appliquée pour créer des pointeur sur des tableaux de tableaux.

1
type (*identificateur)[N][M];

Et ainsi de suite jusqu’à ce que mort s’en suive… :-°

L’exemple ci-dessous illustre ce qui vient d’être dit en utilisant une fonction pour afficher le contenu d’un tableau à deux dimensions de int.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>


void affiche_tableau(int (*tab)[2], unsigned n, unsigned m)
{
    unsigned i;
    unsigned j;

    for (i = 0; i < n; ++i)
        for (j = 0; j < m; ++j)
            printf("tab[%u][%u] = %d\n", i, j, tab[i][j]);
}


int main(void)
{
    int tab[2][2] = { { 1, 2 }, { 3, 4 } };

    affiche_tableau(tab, 2, 2);
    return 0;
}

Retour de fonction

Même remarque que pour les tableaux unidimensionnels : attention à la classe de stockage ! Pour le reste, nous vous laissons admirer la syntaxe particulièrement dégoûtante d’une fonction retournant un pointeur sur un tableau de deux int.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>


int (*tableau(void))[2] /* Ouh ! Que c'est laid ! */
{
    int tab[2][2] = { { 1, 2 }, { 3, 4 } };

    return tab;
}


int main(void)
{
    int (*p)[2] = tableau(); /* Incorrect. */

    printf("%d\n", p[0][0]);
    return 0;
}

Exercices

Somme des éléments

Réalisez une fonction qui calcule la somme de tous les éléments d’un tableau de int.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int somme(int *tab, unsigned taille)
{
    unsigned i;
    int res = 0 ;

    for (i = 0; i < taille; ++i)
        res += tableau[i];

    return res;
}

Maximum et minimum

Créez deux fonctions : une qui retourne le plus petit élément d’un tableau de int et une qui renvoie le plus grand élément d’un tableau de int.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int minimum(int *tab, unsigned taille)
{
    unsigned i;
    int min = tab[0];

    for (i = 1; i < taille; ++i)
        if (tab[i] < min)
            min = tab[i];

    return min;
}


int maximum(int *tab, unsigned taille)
{
    unsigned i;
    int max = tab[0];

    for (i = 1; i < taille; ++i)
        if (tab[i] > max)
            max = tab[i];

    return max
}

Recherche d’un élément

Construisez une fonction qui teste la présence d’une valeur dans un tableau de int. Celle-ci retournera 1 si un ou plusieurs éléments du tableau sont égaux à la valeur recherchée, 0 sinon.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int find(int * tab, unsigned taille, int val)
{
    unsigned i;

    for (i = 0; i < taille; ++i)
        if (tab[i] == val) 
            return 1;

    return 0;
}

Inverser un tableau

Produisez une fonction qui inverse le contenu d’un tableau (le premier élément devient le dernier, l’avant dernier le deuxième et ainsi de suite).

Indice

Pensez à la fonction swap() présentée dans le chapitre sur les pointeurs.

Correction

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void swap(int *pa, int *pb)
{
    int tmp;

    tmp = *pa;
    *pa = *pb;
    *pb = tmp;
}


void invert(int *tab , unsigned taille)
{
    unsigned i;

    for (i = 0; i < (taille / 2); ++i)
        swap(tab + i , tab + taille - 1 - i);
    }
}

Produit des lignes

Composez une fonction qui calcul le produit de la somme des éléments de chaque ligne d’un tableau de int à deux dimensions (ce tableau comprend cinq lignes et cinq colonnes).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int produit(int (*tab)[5])
{
    unsigned i;
    unsigned j;
    int res = 1;

    for (i = 0; i < 5; ++i)
    {
        int tmp = 0;

        for (j = 0; j < 5; ++j)
            tmp += tab[i][j];

        res *= tmp;
    }

    return res;
}

Triangle de Pascal

Les triangles de Pascal sont des objets mathématiques amusants. Voici une petite animation qui vous expliquera le fonctionnement de ceux-ci.

Explication des triangles de Pascal en image

Votre objectif va être de réaliser un programme qui affiche un triangle de Pascal de la taille souhaitée par l’utilisateur. Pour ce faire, nous allons diviser le triangle en lignes afin de pouvoir le représenter sous la forme d’un tableau à deux dimensions. Chaque élément du tableau se verra attribué soit une valeur du triangle soit zéro pour signaler qu’il n’est pas utilisé.

La première chose que nous allons faire est donc définir un tableau à deux dimensions (nous fixerons la taille des dimensions à dix) dont tous les éléments sont initialisés à zéro. Ensuite, nous allons demandés à l’utilisateur d’entrer la taille du triangle qu’il souhaite obtenir (celle-ci ne devra pas être supérieure aux dimensions du tableau).

À présent, passons à la fonction de création du triangle de Pascal. Celle-ci devra mettre en œuvre l’algorithme suivant.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
N = taille du triangle de Pascal fournie par l’utilisateur

Mettre la première case du tableau à 1

Pour i = 1, i < N, i = i + 1
    Mettre la première case de la ligne à 1

    Pour j = 1, j < i, j = j + 1
         La case [i,j] prend la valeur [i - 1, j - 1] + [i - 1, j]

    Mettre la dernière case de la ligne à 1

Enfin, il vous faudra écrire une petite fonction pour afficher le tableau ainsi créer.
Bon courage ! ;)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <stdlib.h>


void triangle_pascal(int (*tab)[10], unsigned taille)
{
    unsigned i;
    unsigned j;

    tab[0][0] = 1;

    for (i = 1; i < taille; ++i)
    {
        tab[i][0] = 1;

        for (j = 1; j < i; ++j)
            tab[i][j] = tab[i - 1][j - 1] + tab[i - 1][j];

        tab[i][i] = 1;
    }
}


void print_triangle(int (*tab)[10], unsigned taille)
{
    unsigned i;
    unsigned j;
    int sp;

    for (i = 0; i < taille; ++i)
    {
        for (sp = taille - 1 - i; sp > 0; --sp)
            printf(" ");

        for (j = 0; j < taille; ++j)
            if (tab[i][j] != 0)
                printf("%d ", tab[i][j]);

        printf("\n");
    }
}


int main(void)
{
    int tab[10][10] = { { 0 } };
    unsigned taille;

    printf("Taille du triangle: ");

    if (scanf("%u", &taille) != 1)
    {
        printf("Mauvaise saisie\n");
        return EXIT_FAILURE;
    }
    else if (taille > 10)
    {
        printf("La taille ne doit pas être supérieure à 10\n");
        return EXIT_FAILURE;
    }

    triangle_pascal(tab, taille);
    print_triangle(tab, taille);  
    return 0;
}

Dans le chapitre suivant, nous aborderons un nouveau type d’agrégat, un peu particulier puisqu’il se base sur les tableaux : les chaînes de caractères.