UMBB.org, Forum des étudiants Algériens
<img src=http://e2conf-2009.zerista.com/images/welcome_meet_icon.png>

Avons que vous pouvez visiter se site. je vous demande d'enregistrer en premier pour profiter touts avantage "gratuit"

Rejoignez le forum, c’est rapide et facile

UMBB.org, Forum des étudiants Algériens
<img src=http://e2conf-2009.zerista.com/images/welcome_meet_icon.png>

Avons que vous pouvez visiter se site. je vous demande d'enregistrer en premier pour profiter touts avantage "gratuit"
UMBB.org, Forum des étudiants Algériens
Vous souhaitez réagir à ce message ? Créez un compte en quelques clics ou connectez-vous pour continuer.
UMBB.org, Forum des étudiants Algériens

Forums des étudiants Algériens, Tous les indispensables et tous les nouveautés pour vous accompagner durant votre scolarité!

Connexion

Récupérer mon mot de passe



Mai 2024
DimLunMarMerJeuVenSam
   1234
567891011
12131415161718
19202122232425
262728293031 

Calendrier Calendrier

Marque-page social
Marque-page social reddit      

Conservez et partagez l'adresse de INIM sur votre site de social bookmarking

Conservez et partagez l'adresse de UMBB.org, Forum des étudiants Algériens sur votre site de social bookmarking

-45%
Le deal à ne pas rater :
WHIRLPOOL OWFC3C26X – Lave-vaisselle pose libre 14 couverts – ...
339 € 622 €
Voir le deal

Vous n'êtes pas connecté. Connectez-vous ou enregistrez-vous

Chapitre 4. Les pointeurs et références

3 participants

Aller en bas  Message [Page 1 sur 1]

Admin

Admin
Administrateur
Administrateur



chapitre 3 : les pointeurs










Chapitre 3    Les pointeurs












Toute variable manipulée dans un programme est stockée quelque part en
mémoire centrale. Cette mémoire est constituée d'octets qui sont
identifiés de manière univoque par un numéro qu'on appelle adresse. Pour retrouver une variable, il suffit donc de connaître
l'adresse de l'octet où elle est stockée (ou, s'il s'agit d'une
variable qui recouvre plusieurs octets contigus, l'adresse du premier
de ces octets). Pour des raisons évidentes de lisibilité, on désigne
souvent les variables par des identificateurs, et non par leur
adresse. C'est le compilateur qui fait alors le lien entre
l'identificateur d'une variable et son adresse en mémoire. Toutefois,
il est parfois très pratique de manipuler directement une variable
par son adresse.









3.1  Adresse et valeur d'un objet

On appelle Lvalue (left value) tout objet pouvant être placé à
gauche d'un opérateur d'affectation. Une Lvalue est caractérisée
par :


  • son adresse, c'est-à-dire l'adresse-mémoire à partir de laquelle
    l'objet est stocké ;

  • sa valeur, c'est-à-dire ce qui est stocké à cette adresse.

Dans l'exemple,

int i, j;
i = 3;
j = i;
Si le compilateur a placé la variable i à
l'adresse 4831836000 en mémoire, et la variable j à l'adresse
4831836004, on a















objetadressevaleur
i48318360003
j48318360043


Deux variables différentes ont des adresses différentes. L'affectation
i = j; n'opère que sur les valeurs des variables. Les variables
i et j étant de type int, elles sont stockées sur
4 octets. Ainsi la valeur de i est stockée sur les octets
d'adresse 4831836000 à 4831836003.




L'adresse d'un objet étant un numéro d'octet en mémoire, il s'agit
d'un entier quelque soit le type de l'objet considéré. Le format
interne de cet entier (16 bits, 32 bits ou 64 bits) dépend des
architectures.
Sur un DEC alpha, par exemple, une adresse a toujours le format d'un
entier long (64 bits).



L'opérateur & permet d'accéder à l'adresse d'une
variable. Toutefois &i n'est pas une Lvalue mais une constante : on
ne peut pas faire figurer &i à gauche d'un opérateur
d'affectation. Pour pouvoir manipuler des adresses, on doit donc
recourir un nouveau type d'objets, les pointeurs.










3.2  Notion de pointeur

Un pointeur est un objet (Lvalue) dont la valeur est
égale à l'adresse d'un autre objet.
On déclare un pointeur par l'instruction :
type *nom-du-pointeur;


type est le type de l'objet pointé. Cette déclaration
déclare un identificateur, nom-du-pointeur, associé à un
objet dont la valeur est l'adresse d'un autre objet de type type. L'identificateur nom-du-pointeur est donc en quelque
sorte un identificateur d'adresse. Comme pour n'importe quelle Lvalue,
sa valeur est modifiable.



Même si la valeur d'un pointeur est toujours un entier (éventuellement
un entier long), le type d'un pointeur dépend du type de l'objet
vers lequel il pointe. Cette distinction est indispensable à
l'interprétation de la valeur d'un pointeur. En effet, pour un
pointeur sur un objet de type char, la valeur donne l'adresse de
l'octet où cet objet est stocké. Par contre, pour un pointeur sur un
objet de type int,
la valeur donne l'adresse du premier des 4 octets où l'objet est
stocké.
Dans l'exemple suivant, on définit un pointeur p qui pointe vers
un entier i :


int i = 3;
int *p;

p = &i;
On se trouve dans la configuration














objetadressevaleur
i48318360003
p48318360044831836000


L'opérateur unaire d'indirection * permet d'accéder
directement à la valeur de l'objet pointé. Ainsi, si p est un
pointeur vers un entier i, *p désigne la valeur de i.
Par exemple, le programme


main()
{
int i = 3;
int *p;

p = &i;
printf("*p = %d \n",*p);
}
imprime *p = 3.



Dans ce programme, les objets i et *p sont identiques : ils
ont mêmes adresse et valeur. Nous sommes dans la configuration :



















objetadressevaleur
i48318360003
p48318360044831836000
*p48318360003


Cela signifie en particulier que toute modification de *p
modifie i. Ainsi, si l'on ajoute l'instruction *p = 0; à
la fin du programme précédent, la valeur de i devient nulle.




On peut donc dans un programme manipuler à la fois les objets p
et *p. Ces deux manipulations sont très différentes. Comparons
par exemple les deux programmes suivants :

main()
{
int i = 3, j = 6;
int *p1, *p2;
p1 = &i;
p2 = &j;
*p1 = *p2;
}
et

main()
{
int i = 3, j = 6;
int *p1, *p2;
p1 = &i;
p2 = &j;
p1 = p2;
}

Avant la dernière affectation de chacun de ces programmes, on est dans
une configuration du type :























objetadressevaleur
i48318360003
j48318360046
p148318359844831836000
p248318359924831836004


Après l'affectation *p1 = *p2; du premier programme, on a























objetadressevaleur
i48318360006
j48318360046
p148318359844831836000
p248318359924831836004


Par contre, l'affectation p1 = p2 du second programme, conduit à
la situation :
























objetadressevaleur
i48318360003
j48318360046
p148318359844831836004
p248318359924831836004







3.3  Arithmétique des pointeurs


La valeur d'un pointeur étant un entier, on peut lui appliquer un
certain nombre d'opérateurs arithmétiques classiques. Les seules
opérations arithmétiques valides sur les pointeurs sont :

  • l'addition d'un entier à un pointeur. Le résultat est un
    pointeur de même type que le pointeur de départ ;

  • la soustraction d'un entier à un pointeur. Le résultat est un
    pointeur de même type que le pointeur de départ ;

  • la différence de deux pointeurs pointant tous deux vers des
    objets de même type. Le résultat est un entier.


Notons que la somme de deux pointeurs n'est pas autorisée.



Si i est un entier et p est un pointeur sur un objet de type
type, l'expression
p + i
désigne un pointeur sur un objet de type type dont la valeur
est égale à la valeur de p incrémentée de i *
sizeof(
type). Il en va de même pour la soustraction d'un
entier à un pointeur, et pour les opérateurs d'incrémentation et de
décrémentation ++ et --.
Par exemple, le programme


main()
{
int i = 3;
int *p1, *p2;
p1 = &i;
p2 = p1 + 1;
printf("p1 = %ld \t p2 = %ld\n",p1,p2);
}
affiche p1 = 4831835984    p2 = 4831835988.



Par contre, le même programme avec des pointeurs sur des objets de
type double :

main()
{
double i = 3;
double *p1, *p2;
p1 = &i;
p2 = p1 + 1;
printf("p1 = %ld \t p2 = %ld\n",p1,p2);
}
affiche p1 = 4831835984    p2 = 4831835992.




Les opérateurs de comparaison sont également applicables aux
pointeurs, à condition de comparer des pointeurs qui pointent vers des
objets de même type.



L'utilisation des opérations arithmétiques sur les pointeurs est
particulièrement utile pour parcourir des tableaux. Ainsi, le
programme suivant imprime les éléments du tableau tab dans
l'ordre croissant puis décroissant des indices.

#define N 5
int tab[5] = {1, 2, 6, 0, 7};
main()
{
int *p;
printf("\n ordre croissant:\n");
for (p = &tab[0]; p <= &tab[N-1]; p++)
printf(" %d \n",*p);
printf("\n ordre decroissant:\n");
for (p = &tab[N-1]; p >= &tab[0]; p--)
printf(" %d \n",*p);
}
Si p et q sont deux pointeurs sur des objets de type

type, l'expression
p - q
désigne un entier dont la valeur est égale à (p - q)/sizeof(type) .










3.4  Allocation dynamique


Avant de manipuler un pointeur, et notamment de lui appliquer
l'opérateur d'indirection *, il faut l'initialiser. Sinon, par
défaut, la valeur du pointeur est égale à une constante symbolique
notée NULL définie dans stdio.h. En général, cette
constante vaut 0. Le test p == NULL permet de savoir si le
pointeur p pointe vers un objet.





On peut initialiser un pointeur p par une affectation sur p. Par exemple, on peut affecter à p l'adresse d'une autre
variable.
Il est également possible d'affecter directement une valeur à *p. Mais pour cela, il faut d'abord réserver à *p un
espace-mémoire de taille adéquate. L'adresse de cet espace-mémoire
sera la valeur de p.
Cette opération consistant à réserver un espace-mémoire pour stocker
l'objet pointé s'appelle allocation dynamique. Elle se fait
en C par la fonction malloc de la librairie standard stdlib.h. Sa syntaxe est
malloc(nombre-octets)


Cette fonction retourne un pointeur de type char * pointant vers
un objet de taille nombreoctets octets. Pour initialiser
des pointeurs vers des objets qui ne sont pas de type char, il
faut convertir le type de la sortie de la fonction malloc à
l'aide d'un cast.
L'argument nombre-octets est souvent donné à l'aide
de la fonction sizeof() qui renvoie le nombre d'octets
utilisés pour stocker un objet.




Ainsi, pour initialiser un pointeur vers un entier, on écrit :

#include <stdlib.h>
int *p;
p = (int*)malloc(sizeof(int));
On aurait pu écrire également
p = (int*)malloc(4);
puisqu'un objet de type int est stocké sur 4 octets. Mais on
préférera la première écriture qui a l'avantage d'être portable.









Le programme suivant

#include <stdio.h>
#include <stdlib.h>
main()
{
int i = 3;
int *p;
printf("valeur de p avant initialisation = %ld\n",p);
p = (int*)malloc(sizeof(int));
printf("valeur de p apres initialisation = %ld\n",p);
*p = i;
printf("valeur de *p = %d\n",*p);
}
définit un pointeur p sur un objet *p de type int,
et affecte à *p la valeur de la variable i.
Il imprime à l'écran :


valeur de p avant initialisation = 0
valeur de p apres initialisation = 5368711424
valeur de *p = 3
Avant l'allocation dynamique, on se trouve dans la configuration














objetadressevaleur
i48318360003
p48318360040


A ce stade, *p n'a aucun sens. En particulier, toute
manipulation de la variable *p générerait une violation mémoire,
détectable à l'exécution par le message d'erreur Segmentation
fault
.




L'allocation dynamique a pour résultat d'attribuer une valeur à p et de réserver à cette adresse un espace-mémoire
composé de 4 octets pour stocker la valeur de *p. On a alors



















objetadressevaleur
i48318360003
p48318360045368711424
*p5368711424? (int)



*p est maintenant définie mais sa valeur n'est pas
initialisée. Cela signifie que *p peut valoir n'importe quel
entier (celui qui se trouvait précédemment à cette adresse).
L'affectation *p = i; a enfin pour résultat d'affecter à *p la valeur de i. A la fin du programme, on a donc






















objetadressevaleur
i48318360003
p48318360045368711424
*p53687114243



Il est important de comparer le programme précédent avec


main()
{
int i = 3;
int *p;

p = &i;
}
qui correspond à la situation


















objetadressevaleur
i48318360003
p48318360044831836000
*p48318360003



Dans ce dernier cas, les variables i et *p sont identiques (elles
ont la même adresse) ce qui implique que toute modification de l'une
modifie l'autre. Ceci n'était pas vrai dans l'exemple précédent où
*p et i avaient la même valeur mais des adresses
différentes.



On remarquera que le dernier programme ne nécessite pas d'allocation
dynamique puisque l'espace-mémoire à l'adresse &i est déjà
réservé pour un entier.









La fonction malloc permet également d'allouer un espace
pour plusieurs objets contigus en mémoire. On peut écrire par exemple

#include <stdio.h>
#include <stdlib.h>
main()
{
int i = 3;
int j = 6;
int *p;
p = (int*)malloc(2 * sizeof(int));
*p = i;
*(p + 1) = j;
printf("p = %ld \t *p = %d \t p+1 = %ld \t *(p+1) = %d \n",p,*p,p+1,*(p+1));
}
On a ainsi réservé, à l'adresse donnée par la valeur de p,
8 octets en mémoire, qui permettent de stocker 2 objets de type int.
Le programme affiche
p = 5368711424   *p = 3   p+1 = 5368711428   *(p+1) =
6
.




La fonction calloc de la librairie stdlib.h a le même rôle
que la fonction malloc mais elle initialise en plus l'objet
pointé *p à zéro.
Sa syntaxe est
calloc(nb-objets,taille-objets)



Ainsi, si p est de type int*, l'instruction

p = (int*)calloc(N,sizeof(int));
est strictement équivalente à

p = (int*)malloc(N * sizeof(int));
for (i = 0; i < N; i++)
*(p + i) = 0;
L'emploi de calloc est simplement plus rapide.









Enfin, lorsque l'on n'a plus besoin de l'espace-mémoire alloué
dynamiquement (c'est-à-dire quand on n'utilise plus le pointeur p), il faut libérer cette place en mémoire. Ceci se fait à l'aide
de l'instruction free qui a pour syntaxe
free(nom-du-pointeur);

A toute instruction de type malloc ou calloc doit être
associée une instruction de type free.










3.5  Pointeurs et tableaux

L'usage des pointeurs en C est, en grande partie, orienté vers la
manipulation des tableaux.









3.5.1  Pointeurs et tableaux à une dimension

Tout tableau en C est en fait un pointeur constant. Dans la
déclaration


int tab[10];
tab est un pointeur constant (non modifiable) dont la valeur
est l'adresse du premier élément du tableau. Autrement dit,
tab a pour valeur &tab[0].
On peut donc utiliser un pointeur initialisé à tab pour
parcourir les éléments du tableau.

#define N 5
int tab[5] = {1, 2, 6, 0, 7};
main()
{
int i;
int *p;
p = tab;
for (i = 0; i < N; i++)
{
printf(" %d \n",*p);
p++;
}
}

On accède à l'élément d'indice i du tableau tab grâce à
l'opérateur d'indexation [], par l'expression tab[i].
Cet opérateur d'indexation peut en fait s'appliquer à tout objet p de
type pointeur. Il est lié à l'opérateur d'indirection * par la
formule

p[i] = *(p + i)

Pointeurs et tableaux se manipulent donc exactement de même manière.
Par exemple, le programme précédent peut aussi s'écrire

#define N 5
int tab[5] = {1, 2, 6, 0, 7};
main()
{
int i;
int *p;
p = tab;
for (i = 0; i < N; i++)
printf(" %d \n", p[i]);
}
Toutefois, la manipulation de tableaux, et non de pointeurs, possède
certains inconvénients dûs au fait qu'un tableau est un pointeur
constant. Ainsi

  • on ne peut pas créer de tableaux dont la taille est une variable
    du programme,

  • on ne peut pas créer de tableaux bidimensionnels dont les
    lignes n'ont pas toutes le même nombre d'éléments.

Ces opérations deviennent possibles dès que l'on manipule des
pointeurs alloués dynamiquement. Ainsi, pour créer un tableau
d'entiers à n éléments où n est une variable du programme, on écrit


#include <stdlib.h>
main()
{
int n;
int *tab;

...
tab = (int*)malloc(n * sizeof(int));
...
free(tab);
}
Si on veut en plus que tous les éléments du tableau tab soient
initialisés à zéro, on remplace l'allocation dynamique avec malloc par

tab = (int*)calloc(n, sizeof(int));
Les éléments de tab sont manipulés avec l'opérateur d'indexation

[], exactement comme pour les tableaux.








Les deux différences principales entre un tableau et un pointeur sont

  • un pointeur doit toujours être initialisé, soit par une
    allocation dynamique, soit par affectation d'une expression adresse,
    par exemple
    p = &i ;

  • un tableau n'est pas une Lvalue ; il ne peut donc pas figurer à
    gauche d'un opérateur d'affectation. En particulier, un
    tableau ne supporte pas l'arithmétique (on ne peut pas écrire tab++;).


3.5.2  Pointeurs et tableaux à plusieurs dimensions

Un tableau à deux dimensions est, par définition, un tableau de tableaux. Il
s'agit donc en fait d'un pointeur vers un pointeur. Considérons le tableau à
deux dimensions défini par :

int tab[M][N];
tab est un pointeur, qui pointe vers un objet lui-même de type pointeur d'entier. tab a une valeur
constante égale à l'adresse du premier élément du tableau, &tab[0][0].
De même tab[i], pour i entre 0 et M-1, est un
pointeur constant vers un objet de type entier, qui est le premier
élément de la ligne d'indice i. tab[i] a donc une valeur
constante qui est égale à &tab[i][0].









Exactement comme pour les tableaux à une dimension, les pointeurs de
pointeurs ont de nombreux avantages sur les tableaux
multi-dimensionnés.



On déclare un pointeur qui pointe sur un objet de type type *
(deux dimensions) de la même manière qu'un pointeur, c'est-à-dire
type **nom-du-pointeur;


De même un pointeur qui pointe sur un objet de type type **
(équivalent à un tableau à 3 dimensions) se déclare par
type ***nom-du-pointeur;

Par exemple, pour créer avec un pointeur de pointeur une matrice à
k lignes et n colonnes à coefficients entiers, on écrit :


main()
{
int k, n;
int **tab;

tab = (int**)malloc(k * sizeof(int*));
for (i = 0; i < k; i++)
tab[i] = (int*)malloc(n * sizeof(int));
....

for (i = 0; i < k; i++)
free(tab[i]);
free(tab);
}
La première allocation dynamique réserve pour l'objet pointé par tab l'espace-mémoire correspondant à k pointeurs sur des
entiers. Ces k pointeurs correspondent aux lignes de la
matrice. Les allocations dynamiques suivantes réservent pour chaque
pointeur tab[i] l'espace-mémoire nécessaire pour stocker n entiers.




Si on désire en plus que tous les éléments du tableau soient
initialisés à zéro, il suffit de remplacer l'allocation dynamique dans
la boucle for par

tab[i] = (int*)calloc(n, sizeof(int));
Contrairement aux tableaux à deux dimensions, on peut choisir des
tailles différentes pour chacune des lignes tab[i]. Par exemple,
si l'on veut que tab[i] contienne exactement i+1 éléments, on
écrit


for (i = 0; i < k; i++)
tab[i] = (int*)malloc((i + 1) * sizeof(int));


3.5.3  Pointeurs et chaînes de caractères

On a vu précédemment qu'une chaîne de caractères était un tableau à une dimension
d'objets de type char, se terminant par le caractère nul '\0'. On peut donc manipuler toute chaîne de caractères à l'aide
d'un pointeur sur un objet de type char. On peut faire
subir à une chaîne définie par


char *chaine;
des affectations comme

chaine = "ceci est une chaine";
et toute opération valide sur les pointeurs, comme l'instruction chaine++;.
Ainsi, le programme suivant imprime le nombre de caractères d'une
chaîne (sans compter le caractère nul).

#include <stdio.h>
main()
{
int i;
char *chaine;

chaine = "chaine de caracteres";
for (i = 0; *chaine != '\0'; i++)
chaine++;
printf("nombre de caracteres = %d\n",i);
}
La fonction donnant la longueur d'une chaîne de caractères, définie
dans la librairie standard string.h, procède de manière
identique. Il s'agit de la fonction strlen dont la syntaxe est

strlen(chaine);

chaine est un pointeur sur un objet de type char. Cette fonction renvoie un entier dont la valeur est égale à
la longueur de la chaîne passée en argument (moins le caractère
'\0').
L'utilisation de pointeurs de caractère et non de tableaux permet par
exemple de créer une chaîne correspondant à la concaténation de deux
chaînes de caractères :


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{
int i;
char *chaine1, *chaine2, *res, *p;

chaine1 = "chaine ";
chaine2 = "de caracteres";
res = (char*)malloc((strlen(chaine1) + strlen(chaine2)) * sizeof(char));
p = res;
for (i = 0; i < strlen(chaine1); i++)
*p++ = chaine1[i];
for (i = 0; i < strlen(chaine2); i++)
*p++ = chaine2[i];
printf("%s\n",res);
}
On remarquera l'utilisation d'un pointeur intermédiaire p qui
est indispensable dès que l'on fait des opérations de type
incrémentation. En effet, si on avait incrémenté directement la valeur
de res, on aurait évidemment ``perdu'' la référence sur le
premier caractère de la chaîne. Par exemple,


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{
int i;
char *chaine1, *chaine2, *res;

chaine1 = "chaine ";
chaine2 = "de caracteres";
res = (char*)malloc((strlen(chaine1) + strlen(chaine2)) * sizeof(char));
for (i = 0; i < strlen(chaine1); i++)
*res++ = chaine1[i];
for (i = 0; i < strlen(chaine2); i++)
*res++ = chaine2[i];
printf("\nnombre de caracteres de res = %d\n",strlen(res));
}
imprime la valeur 0, puisque res a été modifié au cours du
programme et pointe maintenant sur le caractère nul.










3.6  Pointeurs et structures



3.6.1  Pointeur sur une structure

Contrairement aux tableaux, les objets de type structure en C sont
des Lvalues. Ils possèdent une adresse, correspondant à
l'adresse du premier élément du premier membre de la structure.
On peut donc manipuler des pointeurs sur des structures.
Ainsi, le programme suivant crée, à l'aide d'un pointeur, un tableau d'objets de type
structure.

#include <stdlib.h>

#include <stdio.h>

struct eleve
{
char nom[20];
int date;
};

typedef struct eleve *classe;

main()
{
int n, i;
classe tab;

printf("nombre d'eleves de la classe = ");
scanf("%d",&n);
tab = (classe)malloc(n * sizeof(struct eleve));

for (i =0 ; i < n; i++)
{
printf("\n saisie de l'eleve numero %d\n",i);
printf("nom de l'eleve = ");
scanf("%s",&tab[i].nom);
printf("\n date de naissance JJMMAA = ");
scanf("%d",&tab[i].date);
}
printf("\n Entrez un numero ");
scanf("%d",&i);
printf("\n Eleve numero %d:",i);
printf("\n nom = %s",tab[i].nom);
printf("\n date de naissance = %d\n",tab[i].date);
free(tab);
}
Si p est un pointeur sur une structure, on peut accéder à un
membre de la structure pointé par l'expression
(*p).membre


L'usage de parenthèses est ici indispensable car l'opérateur
d'indirection * à une priorité plus élevée que l'opérateur de
membre de structure. Cette notation peut être simplifiée grâce
à l'opérateur pointeur de membre de structure, noté ->.
L'expression précédente est strictement équivalente à
p->membre

Ainsi, dans le programme précédent, on peut remplacer tab[i].nom
et tab[i].date respectivement par (tab + i)->nom et (tab + i)->date.



3.6.2  Structures auto-référencées


On a souvent besoin en C de modèles de structure dont un des membres
est un pointeur vers une structure de même modèle. Cette
représentation permet en particulier de construire des listes
chaînées.
En effet, il est possible de représenter une liste d'éléments de même
type par un tableau (ou un pointeur). Toutefois, cette représentation,
dite contiguë, impose que la taille maximale de la liste soit
connue a priori (on a besoin du nombre d'éléments du tableau lors de
l'allocation dynamique). Pour résoudre ce problème, on utilise une
représentation chaînée : l'élément de base de la chaîne est une
structure appelée cellule qui contient la valeur d'un élément de
la liste et un pointeur sur l'élément suivant. Le dernier élément
pointe sur la liste vide NULL. La liste est alors
définie comme un pointeur sur le premier élément de la chaîne.



Pour représenter une liste d'entiers sous forme chaînée, on crée le
modèle de structure cellule qui a deux champs : un champ valeur de type int, et un champ suivant de type
pointeur sur une struct cellule. Une liste sera alors un
objet de type pointeur sur une struct cellule. Grâce au mot-clef

typedef, on peut définir le type liste, synonyme du type
pointeur sur une struct cellule.


struct cellule
{
int valeur;
struct cellule *suivant;
};

typedef struct cellule *liste;
Un des avantages de la représentation chaînée est qu'il est très
facile d'insérer un élément à un endroit quelconque de la
liste. Ainsi, pour insérer un élément en tête de liste, on utilise la
fonction suivante :

liste insere(int element, liste Q)
{
liste L;
L = (liste)malloc(sizeof(struct cellule));
L->valeur = element;
L->suivant = Q;
return(L);
}
Le programme suivant crée une liste d'entiers et l'imprime à l'écran :


#include <stdlib.h>
#include <stdio.h>

struct cellule
{
int valeur;
struct cellule *suivant;
};

typedef struct cellule *liste;

liste insere(int element, liste Q)
{
liste L;
L = (liste)malloc(sizeof(struct cellule));
L->valeur = element;
L->suivant = Q;
return(L);
}

main()
{
liste L, P;

L = insere(1,insere(2,insere(3,insere(4,NULL))));
printf("\n impression de la liste:\n");
P = L;
while (P != NULL)
{
printf("%d \t",P->valeur);
P = P->suivant;
}
}
On utilisera également une structure auto-référencée pour créer un
arbre binaire :

struct noeud
{
int valeur;
struct noeud *fils_gauche;
struct noeud *fils_droit;
};

typedef struct noeud *arbre;









This document was translated from LATEX by [Vous devez être inscrit et connecté pour voir ce lien].


http://www.umbb.org

zaki48

zaki48
Étudiant Surveillant
Étudiant Surveillant

c'est un bon sujet

merciiiii

http://chemistry4all-zaki48.blogspot.com/

SARAH

SARAH
Étudiant Super
Étudiant Super

wé c je cherche des explications vidéo pour les pointeurs merciiiiii

Contenu sponsorisé



Revenir en haut  Message [Page 1 sur 1]

Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum