Sunteți pe pagina 1din 7

Programmation C/Pointeurs

1
Programmation C/ Pointeurs
Dans cette section, nous allons prsenter un mcanisme permettant de manipuler les adresses, les pointeurs. Un
pointeur a pour valeur l'adresse d'un objet C d'un type donn (un pointeur est typ). Ainsi, un pointeur contenant
l'adresse d'un entier sera de type pointeur vers entier.
Usage et dclaration
L'oprateur & permet de connaitre l'adresse d'une variable, on dira aussi la rfrence. Toute dclaration de variable
occupe un certain espace dans la mmoire de l'ordinateur. La rfrence permet de savoir o cet emplacement se
trouve. En simplifiant l'extrme, on peut considrer la mmoire d'un ordinateur comme une gigantesque table
d'octets. Quand on dclare une variable de type int, elle sera alloue un certain emplacement (ou dit autrement :
un indice, une adresse ou une rfrence) dans cette table. Un pointeur permet simplement de stocker une rfrence, il
peut donc tre vu comme un nombre allant de 0 la quantit maximale de mmoire dont dispose votre ordinateur
(moins un, pour tre exact).
Un pointeur occupera habituellement toujours la mme taille (occupera la mme place en mmoire), quelque soit
l'objet se trouvant cet emplacement. Il s'agit en gnral de la plus grande taille directement grable par le
processeur : sur une architecture 32bits, elle sera de 4 octets, sur une architecture 64bits, 8 octets, etc. Le type du
pointeur ne sert qu' renseigner comment sont organises les donnes suivant l'adresse rfrence par le pointeur. Ce
code, par exemple, affiche la rfrence d'une variable au format hexadcimal:
int i;
printf("%p\n", &i);
Pouvoir rcuprer l'adresse n'a d'intrt que si on peut manipuler l'objet point. Pour cela, il est ncessaire de pouvoir
dclarer des pointeurs, ou dit autrement un objet pouvant contenir des rfrences. Pour cela on utilise l'toile (*)
entre le type et le nom de la variable pour indiquer qu'il s'agit d'un pointeur:
T * pointeur, * pointeur2, /* ..., */ * pointeurN;
Dclare les variables pointeur, pointeur2, ..., pointeurN de type pointeur vers le type T. noter la bizarrerie du
langage vouloir associer l'toile la variable et non au type, qui oblige rpter l'toile pour chaque variable.
/* Ce code contient une dclaration volontairement confuse */
int * pointeur, variable;
Cet exemple de code dclare un pointeur sur un entier de type int et une variable de type int. Dans un vrai
programme, il est rarement possible d'utiliser des noms aussi triviaux, aussi il est recommand de sparer la
dclaration des variables de celles des pointeurs (ou d'utiliser l'instruction typedef, qui, elle, permet d'associer
l'toile au type), la lisibilit du programme sera lgrement amliore.
Il est essentiel de bien comprendre ce qui a t dclar dans ces exemples. Chaque pointeur peut contenir une
rfrence sur un emplacement de la mmoire (un indice dans notre fameuse table). On peut obtenir une rfrence (ou
un indice) avec l'oprateur & (ou allouer une rfrence soit-mme avec des fonctions ddies, c.f la section
suivante). Cet oprateur transforme donc une variable de type T en un pointeur de type T *. Insistons sur le terme
variable, car videmment des expressions telles que '&23435' ou '&(2 * a / 3.)' n'ont aucun sens, dans la
mesure o les constantes et expressions du langage n'occupent aucun emplacement susceptible d'intresser votre
programme.
Ce code, par exemple, affiche la rfrence d'une variable dans un format dfini par l'implmentation (qui peut tre
hexadcimal, ou une combinaison "segment:offset", par exemple):
Programmation C/Pointeurs
2
int i;
printf("%p\n", &i);
Il ne faut pas oublier que, comme toutes les variables locales en C, un pointeur est l'origine non initialis. Une
bonne attitude de programmation est de s'assurer que lorsqu'il ne pointe pas vers un objet valide, sa valeur est mise
zro (ou NULL, qui est dclar entre autre dans <stdio.h>).
L'arithmtique des pointeurs
L'arithmtique associe aux pointeurs est sans doute ce qui a valu au C sa rputation d'assembleur plus compliqu
et plus lent que l'assembleur . On peut trs vite construire des expressions incomprhensibles avec les oprateurs
disponibles. Dans la mesure du possible, il est conseill de se limiter des expressions simples, quitte les
dcomposer, car la plupart des compilateurs savent trs bien optimiser un code C.
Drfrencement
Il s'agit de l'opration la plus simple sur les pointeurs. Comme son nom l'indique, il s'agit de l'opration rciproque
au rfrencement (&). L'oprateur associ est l'toile (*), qui est aussi utilis pour dclarer un type pointeur. Cet
oprateur permet donc de transformer un pointeur de type T *, en un objet de type T, les oprations affectant l'objet
point :
int variable = 10;
int * pointeur = &variable;
*pointeur = 20; /* Positionne 'variable' 20 */
Ici, pointeur contient une adresse valide, celle de variable; son drfrencement est donc possible. Par
contre, si pointeur tait une variable locale non initialise, son drfrencement provoquerait coup sr un arrt
brutal de votre programme.
Vous obtiendrez le mme rsultat, si pointeur est initialis NULL. Cette adresse est invalide et toute tentative
de drfrencement se soldera par un arrt du programme.
Arithmtique de base
L'arithmtique des pointeurs s'apparente celle des entiers, mais il est important de comprendre la distinction entre
ces deux concepts.
Les oprations arithmtiques permises avec les pointeurs sont:
Addition / soustraction d'une valeur entire un pointeur (on avance / recule d'un nombre de cases mmoires gal
la taille du type T) : le rsultat est donc un pointeur, de mme type que le pointeur de dpart.
Il faut bien faire attention avec ce genre d'opration ne pas sortir du bloc mmoire, car le C n'effectuera aucun test
pour vous. Considrez l'exemple suivant:
/* Parcours les lments d'un tableau */
int tableau[N];
int * p;
for (p = tableau; p < &tableau[N]; p ++)
{
/* ... */
Programmation C/Pointeurs
3
}
Normalement un tableau de N cases permet d'tre itr sur les indices allant de 0 N - 1, inclusivement. L'expression
&tableau[N] fait rfrence la case mmoire non alloue immdiatement aprs le plus grand indice, donc
potentiellement source de problme. Toutefois, par exception pour le premier indice aprs le plus grand, C garantit
que le rsultat de l'expression soit bien dfini. Bien sr, il ne faut pas drfrencer ce pointeur.
noter qu' l'issue de la boucle, p pointera sur la N+1me case du tableau, donc hors de l'espace allou. Le C
autorise tout fait ce genre de pratique, il faut juste faire attention ne pas dfrencer le pointeur cet endroit.
Soustraction de deux pointeurs de mme type (combien d'objet de type T y a t-il entre les deux pointeurs) : le
rsultat est donc un entier, de type ptrdiff_t. Ce code contient une erreur volontaire !
int autre_tableau[3];
int tableau[10];
int * p = &tableau[5]; /* p pointe sur le 6e lment du
tableau */
int * q = &tableau[3]; /* q pointe sur le 4e lment du
tableau */
ptrdiff_t diff1 = p - q; /* diff1 vaut 2 */
ptrdiff_t diff2 = q - p; /* diff2 vaut -2 */
q = &autre_tableau[2];
ptrdiff_t dif3 = p - q; /* Erreur ! */
Dans cet exemple, les deux premires soustractions sont dfinies, car p et q pointent sur des lments du mme
tableau. La troisime soustraction est indfinie, car on utilise des adresses d'lments de tableaux diffrents.
Notons que l'oprateur [] s'applique toujours une oprande de type entier et une autre de type pointeur. Lorsqu'on
crit tableau[i], il y a en fait une conversion de tableau pointeur avec l'application de l'oprateur []. On peut
donc bien sr utiliser l'oprateur [] avec un pointeur pour oprande:
int a;
int b;
int * p = &a; /* On peut accder la valeur de 'a' via 'p[0]' ou '*p'
*/
/* p[1] est indfini - n'esprez pas accder la valeur de b depuis
l'adresse de a */
Arithmtique avec effet de bord
C'est sans doute ce qui a donn des sueurs froides des gnrations de programmeurs dcouvrant le C : un usage
optimis de la priorit des oprateurs, le tout imbriqu dans des expressions rallonge. Par exemple 'while(
*d++ = *s++ );', pour copier une chaine de caractres.
En fait, en dcomposant l'instruction, c'est nettement plus simple qu'il ne parait. Par exemple :
int i;
int * entier;
/* ... */
i = *entier++; /* i = *(entier++); */
Programmation C/Pointeurs
4
Dans ce cas de figure, l'oprateur d'incrmentation ayant priorit sur celui de drfrencement, c'est celui-ci qui sera
appliqu en premier. Comme il est postfix, l'oprateur ne prendra effet qu' la fin de l'expression (donc de
l'affectation). La variable i sera donc tout simplement affecte de la valeur pointe par entier et aprs cela le pointeur
sera incrment. Voici les diffrents effets suivant les combinaisons de ces deux oprateurs :
i = *++entier; /* Incrmente d'abord le pointeur, puis drfrence la
nouvelle adresse pointe */
i = ++*entier; /* Incrmente la valeur pointe par "entier", puis
affecte le rsultat "i" */
i = (*entier)++; /* Affecte la valeur pointe par "entier" et
incrmente cette valeur */
On peut videmment complexifier les expressions outrance, mais privilgier la compacit au dtriment de la clart
et de la simplicit dans un hypothtique espoir d'optimisation est une erreur de dbutant viter.
Tableaux dynamiques
Un des intrts des pointeurs et de l'allocation dynamique est de permettre de dcider de la taille d'une variable au
moment de l'excution, comme par exemple pour les tableaux. Ainsi pour allouer un tableau de n entiers (n tant
connu l'excution), on dclare une variable de type pointeur sur entier laquelle on alloue une zone mmoire
correspondant n entiers :
int * alloue_tableau(int n, size_t taille)
{
return malloc(n * taille);
}
/* Ailleurs dans le programme */
int * tableau = alloue_tableau(256, sizeof *tableau);
if (tableau != NULL)
{
/* oprations sur le tableau */
/* ... */
free( tableau );
}
Cet exemple alloue un tableau de 256 cases. Bien que la variable soit un pointeur, il est dans ce cas permis d'accder
aux cases de 0 255, soit entre les adresses &tableau[0] et &tableau[255], incluses.
Tableaux dynamiques plusieurs dimensions
Tout comme on pouvait dclarer des tableaux statiques plusieurs dimensions, on peut dclarer des tableaux
dynamiques plusieurs dimensions. Pour dclarer un tel tableau, on dclare des pointeurs sur des pointeurs (etc.) sur
des types. Pour dclarer un tableau dynamique d'entiers deux dimensions :
int ** matrice;
L'allocation d'un tel objet va se drouler en plusieurs tapes (une par toile), on alloue d'abord l'espace pour un
tableau de pointeurs vers entier. Ensuite, on alloue pour chacun de ces tableaux l'espace pour un tableau d'entiers. Si
on veut une matrice 4x5 :
Programmation C/Pointeurs
5
#define LIGNES 4
#define COLONNES 5
int i;
matrice = malloc(sizeof *matrice * LIGNES);
for (i = 0; i < LIGNES; i++)
{
matrice[i] = malloc(sizeof **matrice * COLONNES);
}
Pour librer l'espace allou pour une telle structure, on procde de manire inverse, on commence par librer
chacune des lignes du tableau, puis le tableau lui mme :
for(i = 0; i < LIGNES; i++)
{
free(matrice[i]);
}
free(matrice);
Utilisation des pointeurs pour passer des paramtres par adresse
Toutes les variables en C, l'exception des tableaux, sont passs par valeurs aux paramtres des fonctions. C'est
dire qu'une copie est effectue sur la pile d'appel. Si bien que toutes les modifications de la variable effectues dans
la fonction seront perdues une fois de retour l'appelant. Or, il y a des cas o l'on aimerait bien pouvoir modifier une
variable passe en paramtre et que ces modifications perdurent dans la fonction appelante. C'est un des usages des
paramtres par adresse : permettre la modification d'une variable de l'appelant, comme dans l'exemple suivant:
#include <stdio.h>
/* Ce code change le contenu de deux variables */
void echange(int * a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int a = 5;
int b = 2;
printf("a = %d, b = %d.\n", a, b);
/* On passe 'echange' les adresses de a et b. */
echange(&a, &b);
printf("a = %d, b = %d.\n", a, b);
Programmation C/Pointeurs
6
echange(&a, &b);
printf("a = %d, b = %d.\n", a, b);
return 0;
}
Ce passage par adresse est extrmement rpandu pour optimiser la quantit de donnes qui doit transiter sur la pile
d'appel (qui est, sur beaucoup de systmes, de taille fixe). En fait, mme si la variable ne doit pas tre modifie, on
utilise quand mme un passage par adresse, juste pour viter la copie implicite des variables autres que les tableaux.
Ceci est particulirement intressant avec les structures, puisque celles-ci ont tendance tre assez imposantes, et
cela ne nuit pas trop la lisibilit du programme.
Pointeurs vers fonctions
Les pointeurs vers les fonctions sont un peu spciaux, parce qu'ils n'ont pas d'arithmtique associe (car une telle
arithmtique n'aurait pas beaucoup de sens). Les oprations permises avec les pointeurs sur fonctions sont en fait
relativement limites:
type_retour (*pointeur_fonction)(liste_paramtres);
Dclare pointeur_fonction, un pointeur vers une fonction prenant liste_paramtres comme paramtres et renvoyant
type_retour. Le parenthsage est ici obligatoire, sans quoi l'toile se rattacherait au type de retour. Pour faire pointer
un pointeur vers une fonction, on utilise une affectation normale :
pointeur_fonction = &fonction;
/* Qui est en fait quivalent : */
pointeur_fonction = fonction;
O fonction est compatible avec le pointeur (mmes paramtres et valeur de retour). Une fois que le pointeur pointe
vers une fonction, on peut appeler cette fonction :
(*pointeur_fonction)(paramtres);
/* Ou plus simplement, mais moins logique syntaxiquement */
pointeur_fonction(paramtres);
Sources et contributeurs de l'article
7
Sources et contributeurs de l'article
Programmation C/ Pointeurs Source: http://fr.wikibooks.org/w/index.php?oldid=239495 Contributeurs: Alveric, DavidL, Dhenry, Guillaumito, Marc Mongenet, Tados, Tavernier, Tpierron,
35 modifications anonymes
Source des images, licences et contributeurs
Image:Achtung.svg Source: http://fr.wikibooks.org/w/index.php?title=Fichier:Achtung.svg Licence: Public Domain Contributeurs: see below.
Licence
Creative Commons Attribution-Share Alike 3.0 Unported
http:/ / creativecommons. org/ licenses/ by-sa/ 3. 0/

S-ar putea să vă placă și

  • 100
    100
    Document5 pagini
    100
    Mehdi AjAj
    Încă nu există evaluări
  • S1 L1 Transcript
    S1 L1 Transcript
    Document4 pagini
    S1 L1 Transcript
    Seydou Ali Tahirou
    Încă nu există evaluări
  • S1 L1 Transcript
    S1 L1 Transcript
    Document4 pagini
    S1 L1 Transcript
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Gesman M2.managt - Projet.syst - Info.comm.2012
    Gesman M2.managt - Projet.syst - Info.comm.2012
    Document2 pagini
    Gesman M2.managt - Projet.syst - Info.comm.2012
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Les SIG
    Les SIG
    Document56 pagini
    Les SIG
    Seydou Ali Tahirou
    Încă nu există evaluări
  • QCM
    QCM
    Document3 pagini
    QCM
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Rediger Un Rapport Technique
    Rediger Un Rapport Technique
    Document9 pagini
    Rediger Un Rapport Technique
    farid10
    100% (1)
  • Cours Excel
    Cours Excel
    Document137 pagini
    Cours Excel
    smaich78
    100% (3)
  • Stsig
    Stsig
    Document62 pagini
    Stsig
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Cahier Méthodologique Sur La Mise en Œuvre D'un SIG
    Cahier Méthodologique Sur La Mise en Œuvre D'un SIG
    Document34 pagini
    Cahier Méthodologique Sur La Mise en Œuvre D'un SIG
    benanass
    Încă nu există evaluări
  • SIG Definition
    SIG Definition
    Document8 pagini
    SIG Definition
    Imen Mahjoub
    Încă nu există evaluări
  • Tests
    Tests
    Document4 pagini
    Tests
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Coursm
    Coursm
    Document56 pagini
    Coursm
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Coursm
    Coursm
    Document56 pagini
    Coursm
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Les Réseaux Sans Fils++++
    Les Réseaux Sans Fils++++
    Document129 pagini
    Les Réseaux Sans Fils++++
    karkac brico to
    100% (1)
  • 2G3G
    2G3G
    Document19 pagini
    2G3G
    Wadie Romdhane
    Încă nu există evaluări
  • Pointeur C
    Pointeur C
    Document7 pagini
    Pointeur C
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Aide Mémoire C
    Aide Mémoire C
    Document35 pagini
    Aide Mémoire C
    mrmrc963
    Încă nu există evaluări
  • Les Réseaux Sans Fils++++
    Les Réseaux Sans Fils++++
    Document129 pagini
    Les Réseaux Sans Fils++++
    karkac brico to
    100% (1)
  • Etude Et Conception D'algorithme Pour Les Reseaux Mobiles Et Had Hoc
    Etude Et Conception D'algorithme Pour Les Reseaux Mobiles Et Had Hoc
    Document175 pagini
    Etude Et Conception D'algorithme Pour Les Reseaux Mobiles Et Had Hoc
    emmanueldias
    Încă nu există evaluări
  • Wlss
    Wlss
    Document43 pagini
    Wlss
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Gms 3g
    Gms 3g
    Document16 pagini
    Gms 3g
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Boucle S
    Boucle S
    Document5 pagini
    Boucle S
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Opérateurs
    Opérateurs
    Document6 pagini
    Opérateurs
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Cdmav7 PDF
    Cdmav7 PDF
    Document20 pagini
    Cdmav7 PDF
    BshAek
    Încă nu există evaluări
  • Liste Simple
    Liste Simple
    Document19 pagini
    Liste Simple
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Types Avancés
    Types Avancés
    Document8 pagini
    Types Avancés
    Seydou Ali Tahirou
    Încă nu există evaluări
  • Fon Ctions
    Fon Ctions
    Document8 pagini
    Fon Ctions
    Seydou Ali Tahirou
    Încă nu există evaluări
  • GSM 4G-2
    GSM 4G-2
    Document15 pagini
    GSM 4G-2
    medali7
    Încă nu există evaluări