Essayer de coder ← Retour aux sujets

Tableaux à une dimension

type t[n]

Un tableau est une suite de cases contiguës en mémoire, toutes du même type. Les indices vont de 0 à n−1.

int t[5];
5 entiers, non initialisés (valeurs quelconques)
int t[3] = {10, 20, 30};
Valeurs de départ
int t[100] = {0};
Toutes les cases à 0
t[0] = 42;
L'indice 0 désigne le premier élément
int n = sizeof(t) / sizeof(t[0]);
Nombre de cases (là où t est déclaré)
t[n]
Hors limites : comportement indéfini, aucune vérification
moyenne.c
#include <stdio.h>

int main(void) {
    int notes[] = {12, 15, 9, 18};
    int n = sizeof(notes) / sizeof(notes[0]);
    int somme = 0;

    for (int i = 0; i < n; i++) {
        somme += notes[i];
    }
    printf("moyenne = %.2f\n", (double) somme / n);
    return 0;
}
Affichage
moyenne = 13.50

Deux dimensions et fonctions

t[i][j]

Un tableau 2D est un tableau de lignes. Une fonction qui reçoit un tableau ne connaît pas sa taille : il faut la lui passer.

int g[3][4];
3 lignes, 4 colonnes
g[i][j] = 1;
Ligne i, colonne j
void afficher(int t[], int n)
Tableau en paramètre : on passe aussi sa taille
void f(int g[][4], int lignes)
En 2D, le nombre de colonnes est obligatoire
memcpy(b, a, sizeof a);
Copie un tableau (string.h)
matrice.c
#include <stdio.h>

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

    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", g[i][j]);
        }
        printf("\n");
    }
    return 0;
}
Affichage
1 2 3
4 5 6

📊 Le cours : ranger plusieurs valeurs

Un tableau range plusieurs valeurs du même type sous un seul nom. C'est la structure de données de base du C. Contrairement à une liste Python, sa taille est fixée à la création et ne change plus. Les notions générales (indices, parcours, copies) sont celles du chapitre Tableaux 1D.

Déclarer et initialiser

Si on donne moins de valeurs que de cases, les cases restantes sont mises à 0. Si on ne donne rien, un tableau local contient des valeurs quelconques : initialise-le.

init.c
#include <stdio.h>

int main(void) {
    int a[5] = {1, 2};      /* 1 2 0 0 0 */
    int b[] = {7, 8, 9};    /* taille déduite : 3 cases */
    int c[4] = {0};         /* quatre zéros */

    for (int i = 0; i < 5; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
    printf("b a %d cases\n", (int) (sizeof(b) / sizeof(b[0])));
    printf("c[3] = %d\n", c[3]);
    return 0;
}
Affichage
1 2 0 0 0
b a 3 cases
c[3] = 0

La taille d'un tableau est une constante connue à la compilation. Pour une taille connue seulement à l'exécution, on utilise la mémoire dynamique (voir Mémoire dynamique).

Accéder aux éléments

t[i] désigne la case d'indice i, qu'on peut lire ou modifier. Les cases sont côte à côte en mémoire, ce qui rend l'accès à n'importe quelle case immédiat.

indice :     0      1      2      3
          +------+------+------+------+
notes     |  12  |  15  |   9  |  18  |
          +------+------+------+------+
Aucune vérification des bornes

Python lève une IndexError si l'indice est trop grand. Le C, lui, lit ou écrit simplement la mémoire voisine : t[5] dans un tableau de 5 cases est un comportement indéfini (résultat faux, plantage, ou « ça marche » par hasard).

Parcourir un tableau

Le parcours classique utilise un for avec i < n. Voici la recherche de l'indice du maximum : on garde l'indice du meilleur candidat et on le met à jour quand on trouve plus grand.

maximum.c
#include <stdio.h>

int main(void) {
    int t[] = {3, 9, 2, 7};
    int n = sizeof(t) / sizeof(t[0]);
    int imax = 0;

    for (int i = 1; i < n; i++) {
        if (t[i] > t[imax]) {
            imax = i;
        }
    }
    printf("max = %d a l'indice %d\n", t[imax], imax);
    return 0;
}
Affichage
max = 9 a l'indice 1

Tableaux et fonctions

Quand on passe un tableau à une fonction, il n'est pas copié : la fonction reçoit l'adresse de la première case. Elle peut donc le modifier, mais elle ne connaît plus sa taille : on la passe en second paramètre. Ajoute const si la fonction ne doit pas modifier le contenu.

fonctions_tableaux.c
#include <stdio.h>

int somme(const int t[], int n) {
    int s = 0;
    for (int i = 0; i < n; i++) {
        s += t[i];
    }
    return s;
}

void doubler(int t[], int n) {
    for (int i = 0; i < n; i++) {
        t[i] *= 2;          /* modifie le tableau de l'appelant */
    }
}

int main(void) {
    int t[] = {1, 2, 3, 4};
    doubler(t, 4);
    printf("%d\n", somme(t, 4));
    return 0;
}
Affichage
20

Ce comportement s'explique par les pointeurs : le nom d'un tableau se transforme en adresse de sa première case (voir Pointeurs).

Tableaux à deux dimensions

int g[L][C] est un tableau de L lignes de C cases. Les lignes sont rangées les unes derrière les autres en mémoire. On parcourt avec deux boucles imbriquées (voir l'exemple de la deuxième carte) ; le coût est proportionnel à L × C, comme pour les matrices du chapitre Tableaux 2D.

Copier un tableau

On ne peut pas écrire b = a pour deux tableaux. Il faut copier case par case, ou utiliser memcpy de <string.h>. (En Python, b = a crée un alias ; en C, c'est tout simplement refusé. Voir Aliasing et copies.)

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

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

    memcpy(b, a, sizeof a);   /* copie les 3 entiers */
    b[0] = 99;
    printf("%d %d\n", a[0], b[0]);
    return 0;
}
Affichage
1 99

⚠️ Erreurs fréquentes

Sortir des limites du tableau

À éviter
faux.c
int t[4] = {1, 2, 3, 4};
for (int i = 0; i <= 4; i++) {
    t[i] = 0;          /* t[4] n'existe pas */
}
Correct
correct.c
int t[4] = {1, 2, 3, 4};
for (int i = 0; i < 4; i++) {
    t[i] = 0;
}

Écrire hors du tableau abîme les variables voisines. Les bugs apparaissent parfois bien plus tard, à un autre endroit du programme : c'est l'une des erreurs les plus pénibles à déboguer.

Lire un tableau non initialisé

int t[3]; printf("%d", t[0]); affiche une valeur imprévisible. Écris int t[3] = {0}; pour partir de zéros.

Utiliser sizeof sur un paramètre

À éviter
faux.c
void f(int t[]) {
    int n = sizeof(t) / sizeof(t[0]);   /* sizeof(t) : taille d'un pointeur ! */
}
Correct
correct.c
void f(int t[], int n) {   /* la taille est un paramètre */
    …
}

Comparer deux tableaux avec ==

a == b compare les adresses des deux tableaux, pas leur contenu : le résultat est toujours faux pour deux tableaux distincts. Compare case par case avec une boucle, ou utilise memcmp.

Confondre taille et dernier indice

Un tableau de n cases a pour dernier indice n - 1. Dans une boucle, la condition est i < n ; écrire i <= n est la faute la plus fréquente.

📋 Aide-mémoire

  • type t[n]; : n cases d'indices 0 à n - 1 ; {…} initialise, les cases omises valent 0.
  • Nombre de cases : sizeof(t) / sizeof(t[0]), seulement là où le tableau est déclaré.
  • En paramètre : int t[] + sa taille n ; const int t[] si on ne modifie pas ; le tableau n'est pas copié.
  • Pas de contrôle des bornes : t[n] est un bug silencieux.
  • g[i][j] : ligne i, colonne j ; en paramètre, le nombre de colonnes est obligatoire.
  • Copier : une boucle ou memcpy ; comparer : une boucle ou memcmp, jamais ==.