1 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

CHAPITRE VII

2 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Un tableau est un ensemble d’éléments de même type désignés par un identificateur unique ; chaque élément est repéré par un indice précisant sa position au sein de l’ensemble

3 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

1) Tableaux à un indice

  • Exemple d’utilisation d’un tableau en C
    • Nous souhaitions déterminer, à partir de vingt notes d’élèves (fournies en données), combien d’entre elles sont supérieures à la moyenne de la classe
    • S’il s’agissait de calculer la moyenne de ces notes, il nous suffirait d’en calculer la somme, en les cumulant dans une variable, au fur et à mesure de leur lecture
    • Mais, ici, il nous faut à nouveau pouvoir consulter les notes pour déterminer combien d’entre elles sont supérieures à la moyenne ainsi obtenue
    • Il est donc nécessaire de pouvoir mémoriser ces vingt notes
    • Le tableau va nous offrir une solution convenable à ce problème

4 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

1) Tableaux à un indice

  • Exemple d’utilisation d’un tableau en C

#include <stdio.h>

main()

{

int i, som, nbm ;

float moy ;

int t[20] ;

for (i=0 ; i<20 ; i++)

{

printf ("donnez la note numéro %d : ", i+1) ;

scanf ("%d", &t[i]) ;

}

for (i=0, som=0 ; i<20 ; i++) som += t[i] ;

moy = som / 20 ;

printf ("\n\n moyenne de la classe : %f\n", moy) ;

for (i=0, nbm=0 ; i<20 ; i++ )

if (t[i] > moy) nbm++ ;

printf ("%d élèves ont plus de cette moyenne", nbm) ;

}

5 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

1) Tableaux à un indice

  • Exemple d’utilisation d’un tableau en C
    • La déclaration :

int t[20]

      • Réserve l’emplacement pour 20 éléments de type int
      • Chaque élément est repéré par sa position dans le tableau, nommée indice
      • La première position porte le numéro 0. Ici, donc, nos indices vont de 0 à 19. Le premier élément du tableau sera désigné par t[0], le troisième par t[2], le dernier par t[19]
      • La notation &t[ i ] désigne l’adresse de cet élément t[ i ]

6 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

1) Tableaux à un indice

  • Quelques règles
    • Un élément de tableau est une lvalue

t[2] = 5 //à gauche d’un opérateur d’affectation « OK »

t[3]++ --t[ i ] //opérande d’un opérateur d’incrémentation « OK »

t1 = t2 /pas d’affectations globales de tableaux « NO »

    • Un indice peut prendre la forme de n’importe quelle expression arithmétique de type entier (ou caractère, compte tenu des règles de conversion systématique)

t[n-3]

t[3*p-2*k+j%l]

t[c1+3]

t[c2-c1]

Avec n, p, k et j (de type int)

c1 et c2 (de type char)

7 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

1) Tableaux à un indice

  • Quelques règles
    • La dimension d’un tableau doit être une constante ou une expression constante Ainsi, la construction suivante est correcte

#define N 50

.....

int t[N] ;

float h[2*N-1] ;

    • En revanche, elle ne le serait pas (en C) si N était une constante symbolique définie par const int N=50

Les expressions N et 2*N-1 ne sont plus calculables par le compilateur (elle sera cependant acceptée en C++)

    • Pas de contrôle de débordement d’indice n’est mis en place par la plupart des compilateurs

8 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

2) Tableaux à plusieurs indices

  • Déclaration
    • C autorise les tableaux à plusieurs indices (plusieurs dimensions)

int t[5][3] //réserve un tableau de 15 (5 x 3) éléments

    • la notation désignant un élément d’un tel tableau est une lvalue

t[3][2] t[ i ][ j ] t[ i-3][ i+j ]

    • Pas de limitation sur le nombre d’indices que peut comporter un tableau
  • Arrangement en mémoire des tableaux à plusieurs indices
    • Les éléments d’un tableau sont rangés suivant l’ordre

obtenu en faisant varier le dernier indice en premier

lorsque l’un des indices « déborde »

t[0][5] désigne en fait l’élément t[1][2]

Par contre, t[5][0] désigne un emplacement

situé juste au-delà du tableau

t[0][0]

t[0][1]

t[0][2]

t[1][0]

t[1][1]

t[1][2]

....

t[4][0]

t[4][1]

t[4][2]

9 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

  • Initialisation de tableaux à un indice
    • La déclaration

int tab[5] = { 10, 20, 5, 0, 3 } ; //place les valeurs 10, 20, 5, 0 et 3 dans chacun des //cinq éléments du tableau tab

int tab[5] = { 10, 20 } ; //mentionner que les premières valeurs

int tab[5] = { 10, 20, 5 } ;

int tab[ ] = { 10, 20, 5, 0, 3 } ; //omettre la dimension du tableau

3) Initialisation des tableaux

10 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

  • Initialisation de tableaux à plusieurs indices

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

{ 5, 6, 7, 8 },

{ 9,10,11,12 } }

int tab [3] [4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 } ;

    • La première forme revient à considérer notre tableau comme formé de trois tableaux de quatre éléments chacun
    • La seconde exploite la manière dont les éléments sont effectivement rangés en mémoire et elle se contente d’énumérer les valeurs du tableau suivant cet ordre

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

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

    • les dernières valeurs peuvent être omises. Les déclarations au dessus sont correctes (mais non équivalentes)

3) Initialisation des tableaux

11 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Un pointeur, est une variable destinée à contenir une adresse d’un autre objet (variable, fonction...)

  • Introduction

int * ad ; //réserve une variable ad comme étant un pointeur sur des entiers

int n ;

n = 20 ;

ad = &n ; //affecte à la variable ad la valeur de l’adresse de la variable n

*ad = 30 ; //affecter à la lvalue *ad la valeur 30 (même résultat avec n = 30 ;)

4) Notion de pointeur – Les opérateurs * et &

12 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

  • Exemples
    • Supposons les déclarations :

int * ad1, * ad2, * ad ; //Les variables ad1, ad2 et ad sont des pointeurs sur des entiers

int n = 10, p = 20 ;

    • Soit les instructions :

ad1 = &n ; //place dans ad1 l’adresse de n

ad2 = &p ; //place dans ad2 l’adresse de p

* ad1 = * ad2 + 2 ; // exécute (n = p + 2) ;

* ad1 += 3 est équivalent à (n = n + 3)

( * ad1 ) ++ est équivalent à (n++)

    • Si ad est un pointeur, les expressions ad et *ad sont des lvalue ; càd ad et *ad sont Modifiables
    • En revanche, &ad désigne l’adresse de la variable ad telle qu’elle a été définie par le compilateur, elle n’est pas modifiable

(&ad)++ ou (&p)++ //seront rejetées à la compilation

4) Notion de pointeur – Les opérateurs * et &

13 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

  • Incrémentation de pointeurs

int * ad ;

ad + 1 ; //représente l’adresse de l’entier suivant

Cette notion sera très utile dans le traitement des tableaux ou des chaînes

La différence entre ad+1 et ad est ici de sizeof(int) octets

double * ad ; //cette différence serait de sizeof(double) octets dans ce cas

ad++ //incrémente l’adresse contenue dans ad

4) Notion de pointeur – Les opérateurs * et &

14 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Exemple de permutation

5) Transmission par adresse avec un pointeur

#include <stdio.h>

main()

{

void echange (int * ad1, int * ad2) ;

int a = 10, b = 20 ;

printf ("avant appel %d %d\n", a, b) ;

echange (&a, &b) ;

printf ("après appel %d %d", a, b) ;

}

void echange (int * ad1, int * ad2)

{

int x ;

x = * ad1 ;

* ad1 = * ad2 ;

* ad2 = x ;

}

avant appel 10 20

après appel 20 10

15 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Les arguments effectifs de l’appel de echange sont les adresses des variables n et p (et non plus leurs valeurs).

Donc on transmet à la fonction echange les valeurs des expressions &n et &p.

5) Transmission par adresse avec un pointeur

16 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

L’identificateur d’un tableau, lorsqu’il est employé seul (sans indices à sa suite),

est considéré comme un pointeur (constant) sur le début du tableau

    • Tableaux à un indice

Soit la déclaration suivante :

int t [10]

La notation t est équivalente à & t [0].

L’identificateur t est considéré comme étant de type pointeur sur le type correspondant aux éléments du tableau (int *).

Exemples de notations équivalentes :

t +1 & t [1]

t + i & t [ i ]

t [ i ] * ( t + i )

6) Un nom de tableau est un pointeur constant

17 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Voici plusieurs façons de placer la valeur 1 dans chacun des 10 éléments d’un tableau t utilisant ces notations

6) Un nom de tableau est un pointeur constant

int i ;

for (i=0 ; i<10 ; i++)

* (t+i) = 1 ;

int i ;

int * p :

for (p=t, i=0 ; i<10 ; i++, p++)

* p = 1 ;

Dans le second cas, on a recopier la valeur représentée par t dans un pointeur nommé p. En effet, le symbole t représente une adresse constante (t est une constante de type pointeur sur des entiers).

Ainsi, t++ serait invalide, au même titre que 3++

Un nom de tableau est un pointeur constant ; ce n’est pas une lvalue.

18 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Tableaux à plusieurs indices

L’identificateur d’un tableau, employé seul, représente son adresse de début. Mais, il ne s’agit plus d’un pointeur sur des éléments du tableau.

Soit la déclaration :

int t [3] [4] ;

t désigne un tableau de 3 éléments,

chacun de ces éléments étant lui-même un tableau de 4 entiers.

t représente bien l’adresse de début de notre tableau t, il n’est plus de type int * (comme c’était le cas pour un tableau à un indice) mais d’un type « pointeur sur des blocs de 4 entiers »

t +1 correspond à l’adresse de t, augmentée de 4 entiers

t [0] & t [0][ ]

t [1] & t [1][ ]

6) Un nom de tableau est un pointeur constant

19 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Comparaison de pointeurs

7) Opérateurs réalisables sur des pointeurs

int * p ;

for (p=t ; p<t+10 ; p++)

*p = 1 ;

    • Soustraction de pointeurs

La différence de deux pointeurs de même type fournit le nombre d’éléments du type en question situés entre les deux adresses correspondantes.

    • Affectations de pointeurs et le pointeur nul

Cas déjà rencontré des cas d’affectation de la valeur d’un pointeur à un pointeur de même type.

Il est conseillé d’employer la constante NULL prédéfinie dans stdio.h, et également dans stddef.h (qui sera remplacée par la constante entière 0 lors du traitement par le préprocesseur).

int * n ;

double * x ;

n = NULL ; // équivalent de n = 0 ;

x = NULL ; // équivalent de x = 0 ;

if (n == NULL) ... // équivalent de if (n == 0) ...

20 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Conversions de pointeurs

Il n’existe aucune conversion implicite d’un type pointeur dans un autre.

En revanche, il est toujours possible de faire appel à l’opérateur de cast.

En générale, (à éviter), compte tenu des risques qu’elle comporte.

Certaines machines imposent aux adresses des objets ce que l’on appelle des « contraintes d’alignement ».

Exemple, un objet de 2 octets sera toujours placé à une adresse paire, tandis qu’un caractère (objet d’un seul octet) pourra être placé à n’importe quelle adresse.

Dans ce cas, la conversion d’un char * en un int * peut conduire soit à l’adresse effective du caractère lorsque celle-ci est paire, soit à une adresse voisine lorsque celle-ci est impaire.

7) Opérateurs réalisables sur des pointeurs

21 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Pointeurs génériques

Le typage des pointeurs peut s’avérer gênant dans certaines circonstances telles que celles où une fonction doit manipuler les adresses d’objets de type non connu (ou, plutôt, susceptible de varier d’un appel à un autre).

Désigne un pointeur sur un objet de type quelconque (on parle souvent de « pointeur générique ») (pointeur sans type).

Ne peut pas intervenir dans des opérations arithmétiques

Parce qu’on ne connaît pas la taille des objets pointés

7) Opérateurs réalisables sur des pointeurs

void *

void * p ;

void * q ;

int i ;

p + i ; // Illégal

p - q ; // Illégal

p++ ; // Illégal

*p = 10 ; // Illégal

22 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Pointeurs génériques

Les pointeurs génériques sont théoriquement compatibles avec tous les autres

Exemple : fonction qui « met à zéro » un emplacement de la mémoire dont on lui fournit l’adresse et la taille (en octets)

7) Opérateurs réalisables sur des pointeurs

void * p

int * i

char * c;

p = i ; // Légal

i = p ; // Illégal pour tous les types sauf ’char’

c = p ; // Légal

void raz (void * adr, int n)

{

int i ;

for (i=0 ; i<n ; i++, adr++) *adr = 0 ; // Illégal

}

23 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Pointeurs génériques

Exemple : fonction qui « met à zéro » un emplacement de la mémoire dont on lui fournit l’adresse et la taille (en octets)

7) Opérateurs réalisables sur des pointeurs

void raz (void * adr, int n)

{

int i ;

char * ad = adr ;

for (i=0 ; i<n ; i++, ad++) *ad = 0 ;

}

main()

{

void raz (void *, int) ; /* prototype réduit */

int t[10] ; /* tableau à mettre à zéro */

double z ; /* double à mettre à zéro */

raz (t, 10*sizeof(int)) ;

raz (&z, sizeof (z)) ;

}

24 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Si on place le nom d’un tableau en argument effectif de l’appel d’une fonction, on transmet l’adresse du tableau à la fonction

Ce qui lui permet d’effectuer toutes les manipulations voulues sur ses éléments, qu’il s’agisse d’utiliser leur valeur ou de la modifier.

Exemples :

    • Tableaux à un indice

      • Tableau de taille fixe

Fonction qui met la valeur 1 dans tous les éléments d’un tableau de 10 éléments, l’adresse de ce tableau étant transmise en argument.

8) Tableaux transmis en argument

void fct (int t[10])

{

int i ;

for (i=0 ; i<10 ; i++) t[i]=1 ;

}

main()

{

int t1[10], t2[10] ;

fct(t1) ;

fct(t2) ;

}

25 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Tableaux à un indice

      • Tableau de taille fixe

L’en-tête de fct peut être écrit de l’une des manières suivantes

La définition de la fonction, utilise indifféremment le formalisme tableau ou le formalisme pointeur

8) Tableaux transmis en argument

void fct (int t[10])

void fct (int * t)

void fct (int t[]) //La réservation mémoire du tableau est réalisée par //la fonction appelante

for (i=0 ; i<10 ; i++) t[i] = 1 ;

for (i=0 ; i<10 ; i++, t++) *t = 1 ; //ici t est juste une copie de

for (i=0 ; i<10 ; i++) *(t+i) = 1 ; // l’adresse du tableau

int * p ;

for (p=t ; p<t+10 ; p++) *p = 1 ;

26 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Tableaux à un indice

      • Tableau de taille variable

Il suffit de transmettre la taille en argument.

Exemple, une fonction qui calcule la somme des éléments d’un tableau d’entiers de taille quelconque

8) Tableaux transmis en argument

int som (int t[],int nb)

{

int s = 0, i ;

for (i=0 ; i<nb ; i++)

s += t[i] ;

return (s) ;

}

main()

{

int t1[30], t2[15], t3[10] ;

int s1, s2, s3 ;

.....

s1 = som(t1, 30) ;

s2 = som(t2, 15) + som(t3, 10) ;

.....

}

27 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Tableaux à plusieurs indices

      • Tableau de taille fixe

Exemple d’une fonction qui place la valeur 1 dans chacun des éléments d’un tableau de dimensions 10 x 15 :

Pour l’en-tête de la fonction, le compilateur ne se contente plus de l’adresse de début ; il doit également connaître la seconde dimension du tableau

8) Tableaux transmis en argument

void raun (int t[10][15])

{

int i, j ;

for (i=0 ; i<10 ; i++)

for (j=0 ; j<15 ; j++)

t[i][j] = 1 ;

}

rau (int t[][15]) //ok

rau (int t[][]) //Pas ok

28 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Tableaux à plusieurs indices

      • Tableau de dimensions variables

Soit une fonction qui place la valeur 0 dans chacun des éléments de la diagonale d’un tableau carré de taille quelconque.

Une façon de résoudre ce problème consiste à adresser les éléments voulus par des pointeurs en effectuant le calcul d’adresse approprié

Exemple d’appel de la fonction

8) Tableaux transmis en argument

void diag (int * p, int n)

{

int i ;

for (i=0 ; i<n ; i++)

{

* p = 0 ;

p += n+1 ;

}

}

int t[30][30] ;

diag (t, 30)

29 of 36

 

A

B

C

P1

P2

Init.

1

2

3

/

/

P1=&A

1

2

3

&A

/

P2=&C

*P1= (*P2)++

P1=P2

P2=&B

*P1-=*P2

++*P2

*P1*=*P2

A=++*P2**P1

P1=&A

*P2=*P1/=*P2

main()

{

int A = 1;

int B = 2;

int C = 3;

int *P1, *P2;

P1=&A;

P2=&C;

*P1=(*P2)++;

P1=P2;

P2=&B;

*P1-=*P2;

++*P2;

*P1*=*P2;

A=++*P2**P1;

P1=&A;

*P2=*P1/=*P2;

return 0;

}

30 of 36

Soit P un pointeur qui 'pointe' sur un tableau A:

 

int A[] = {12, 23, 34, 45, 56, 67, 78, 89, 90};

int *P;

P = A;

Quelles valeurs ou adresses fournissent ces expressions:

a) *P+2

b) *(P+2)

c) &P+1

d) &A[4]-3

e) A+3

f) &A[7]-P

g) P+(*P-10)

h) *(P+*(P+8)-A[7])

31 of 36

Quels résultats fournira ce programme :

#include <stdio.h>

main()

{

int t[4] = {10, 20, 30, 40} ;

int * ad [4] ;

int i ;

 

for (i=0 ; i<4 ; i++) ad[i] = t+i ; /* 1 */

for (i=0 ; i<4 ; i++) printf ("%d ", * ad[i]) ; /* 2 */

printf ("\n") ;

printf ("%d %d \n", * (ad[1] + 1), * ad[1] + 1) ; /* 3 */

}

32 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Le nom d’une fonction (employé seul) est traduit par le compilateur en l’adresse de cette fonction (Analogue aux noms de tableaux)

    • Paramétrage d’appel de fonctions

Soit la déclaration suivante:

(* adf) est une fonction à deux arguments (de type double et int) fournissant un

résultat de type int

adf est un pointeur sur une fonction à deux arguments (double et int) fournissant

un résultat de type int

9) Pointeurs sur des fonctions

int (* adf) (double, int) ;

33 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

Si fct1 et fct2 sont des fonctions ayant les prototypes suivants :

Placent, dans adf, l’adresse de la fonction correspondante (fct1 ou fct2).

Il devient possible de programmer un « appel de fonction variable » (c’est-à-dire

que la fonction appelée peut varier au fil de l’exécution du programme)

9) Pointeurs sur des fonctions

int fct1 (double, int) ;

int fct2 (double, int) ;

adf = fct1 ; //ok

adf = fct2 ; //ok

(* adf) (5.35, 4) ;

34 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Fonctions transmises en argument

Soit une fonction permettant de calculer l’intégrale d’une fonction quelconque suivant une méthode numérique donnée.

Cette fonction integ posséderait alors un en-tête de ce genre :

Le premier argument muet correspond à l’adresse de la fonction dont on cherche à calculer l’intégrale.

f est donc un pointeur sur une fonction recevant un argument de type float et fournissant un résultat de type float.

Au sein de la définition de la fonction integ, il sera possible d’appeler la fonction dont on aura ainsi reçu l’adresse

de la façon suivante :

9) Pointeurs sur des fonctions

float integ ( float(*f)(float), ..... )

(*f)(x)

main()

{

float fct1(float);

.....

res1 = integ (fct1, .....) ;

}

35 of 36

Langage C

I. II. III. IV. V. VI. VII. VIII. IX.

Les tableaux et les pointeurs

    • Écrire un programme qui lit 10 nombres entiers dans un tableau avant d’en rechercher le plus grand et le plus petit

4) Écrire une fonction calculant la somme de deux matrices dont les éléments sont de type (int).

Exercice

36 of 36