Les structures et les tableaux

Gaël Thomas

Du type primitif au type composé


Les structures

Une structure est une définition d’un nouveau type de données

Remarque: les sous-types d’une structure s’appellent des champs

Définition d’une nouvelle structure avec :

struct nom_de_la_structure {
  type1 nom_du_champs1;
  type2 nom_du_champs2;
  ...
};

Par exemple:

struct nombre_complexe {
  int partie_reelle;
  int partie_imaginaire;
};

Par convention, les noms de structures commencent par une minuscule en C

Une structure peut aussi être composée à partir d’une autre structure, comme dans cet exemple:

struct point {
  int x;
  int y;
};

struct segment {
  struct point p1;
  struct point p2;
};

En revanche, une structure ne peut pas être composée à partir d’elle-même. À titre d’illustration, l’exemple suivant n’est pas correct:

struct personnage {
  struct personnage ami;
  int               point_de_vie;
};

Cette construction est impossible car il faudrait connaître la taille de la structure personnage pour trouver la taille de la structure personnage.


Déclaration d’une variable de type structure

struct nombre_complexe z1, z2, z3;
/* partie_relle de z prend la valeur 0 */
/* partie_imaginaire de z prend la valeur 1 */
struct nombre_complexe i = { 0, 1 };
/* autre solution : */
struct nombre_complexe j = { .partie_reelle=0, .partie_imaginaire=1 }; 

L’initialisation d’une variable de type structure est différente lorsque la variable est déclarée globalement ou localement. On vous rappelle qu’une variable globale si elle est déclarée en dehors de toute fonction. Sinon, on dit qu’elle est locale.

Lorsqu’une variable de type structure est déclarée en tant que variable globale sans être initialisée, le compilateur initialise chacun de ces champs à la valeur 0. En revanche, lorsqu’une structure est déclarée en tant que variable locale dans une fonction sans être initialisée, ces champs prennent une valeur aléatoire.

Par exemple, dans :

struct nombre_complexe i;

void f() {
  struct nombre_complexe j;
}

Les champs de i sont initialisés à 0 alors que ceux de j prennent une valeur aléatoire.

On peut aussi partiellement initialiser une structure comme dans l’exemple suivant :

struct nombre_complexe j = { 1 }; 

Dans ce cas, le champs partie_relle prend la valeur 1 et le champs partie_imaginaire prend soit la valeur 0 si la variable est globale, soit une valeur aléatoire si la variable est locale à une fonction.


Accès aux champs d’une variable de type structure

struct point {
  int x;
  int y;
};

struct ligne {
  struct point p1;
  struct point p2;
};
void f() {
  struct point p;
  struct ligne l;

  p.x = 42;
  p.y = 17;
  l.p1.x = 1;
  l.p1.y = 2;
  l.p2 = p; /* copie p.x/p.y dans l.p2.x/l.p2.y */

  printf("[%d %d]\n", p.x, p.y);
}

Les tableaux

    int          a[5];      /* tableau de 5 entiers */
    double       b[12];     /* tableau de 12 nombres flottants */
    struct point c[10];     /* tableau de 10 structures points */
    int          d[12][10]; /* tableau de 10 tableaux de 12 entiers */
                            /* => d est une matrice 12x10 */

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

void f() {
  int x[3];
  int y[3];
  int i;

  /* 0 est le premier élément, 2 est le dernier */
  for(i=0; i<3; i++) {
    x[i] = i;
    y[i] = x[i] * 2;
  }
}

Tableaux et structures

    struct point {
      int x;
      int y;
    };

    struct triangle {
      struct point sommets[3];
    };
void f() {
  struct triangle t;

  for(i=0; i<3; i++) {
    t.sommets[i].x = i;
    t.sommets[i].y = i * 2;
  }
}

Différences par rapport à Java

void f() {
  int x[3];

  x[4] = 42; /* Erreur silencieuse !!! */
             /* Écriture à un emplacement aléatoire en mémoire */
             /* le bug pourra apparaître n'importe quand */ 
}

Initialisation d’un tableau lors de sa déclaration

type_element nom_variable[taille] = { e0, e1, e2, ... };

En l’absence d’initialisation : * Si le tableau est une variable globale, chaque élément est initialisé à 0 * Sinon, chaque élément est initialisé à une valeur aléatoire

Lorsqu’on initialise un tableau lors de sa déclaration, on peut omettre la taille du tableau. Dans ce cas, la taille du tableau est donnée par la taille de la liste d’initialisation.

int x[] = { 1, 2, 3 };  /* tableau à trois éléments */
int y[6] = { 1, 2, 3 }; /* tableau à six éléments, avec les trois premiers initialisés */

Initialisation mixte de tableaux et structures

    struct point {
      int x;
      int y;
    };

    struct triangle {
      struct point sommets[3];
    };

    struct triangle t = {
      { 1, 1 },
      { 2, 3 },
      { 4, 9 }
    };

Tableaux et chaînes de caractères

Une chaîne de caractère est simplement un tableau de caractères terminé par le caractère '\0' (c’est à dire le nombre zéro)

char yes[] = "yes";

est équivalent à

char yes[] = { 'y', 'e', 's', '\0' };

Passage par valeur et par référence


Passage par valeur – les types primitifs

/* le x de f et le x du main sont deux variables distinctes */
/* le fait qu'elles aient le même nom est anecdotique */
void f(int x) {
  x = 666;
  printf("f : x = %d\n", x);           /* f : x = 666 */
}

int main() {
  int x = 42;
  f(x);                         /* x est copié dans f */
      /* => le x de main n'est donc pas modifié par f */
  printf("g : x = %d\n", x);            /* g : x = 42 */
  return 0;
}

Passage par valeur – les structures

struct point {
  int x;
  int y;
};

void f(struct point p) {
  p.x = 1;
  printf("(%d, %d)\n", p.x, p.y);        /* => (1, 2) */
}

int main() {
  struct point p = { -2, 2 };
  f(p);                       /* p est copié dans f */
  printf("(%d, %d)\n", p.x, p.y);      /* => (-2, 2)  */
  return 0;
}

Passage par référence – les tableaux

void print(int x[], int n) {
  for(int i=0; i<n; i++) {
    printf("%d ", x[i]);
  }
  printf("\n");
}
int main() {
  int tab[] = { 1, 2, 3 };

  print(tab, 3);  /* => 1 2 3 */
  f(tab);  
  print(tab, 3); /* => 1 42 3 */

  return 0;
}
/* x est une référence vers le tableau original */
void f(int x[]) {
  x[1] = 42;           /* => modifie l'original */
}

Notions clés