• Aucun résultat trouvé

Pointeurs et tableaux

N/A
N/A
Protected

Academic year: 2022

Partager "Pointeurs et tableaux"

Copied!
33
0
0

Texte intégral

(1)

Pointeurs et tableaux

•  Une vision plus précise des « tableaux »

•  Déclaration d’un tableau « T » de 10 entiers

int T[10];!

•  La réalité des choses …

•  « T » est un pointeur (une adresse) indiquant le début d’une zone de mémoire pouvant stocker jusquà 10 entiers

(2)

Pointeurs et tableaux

•  A partir de cette déclaration de variables

int T[10];!

int *p;!

!

•  Les instructions « p = T; » et « p = &T[0]; » sont totalement équivalentes

! !…!

! !int i;!

! !p = T;!

! !i = *p;!

• i est affecté du « contenu de l’adresse ‘p’ »

• Une écriture équivalente : « i = T[0]; » ou encore à

« i = p[0]; »

(3)

Pointeurs et tableaux

•  Et les autres cellules du tableau ?

int T[10];!

int *p = T;!

!

« p+1 » est donc ladresse de « T » incrémentée de 1

•  « p+1 » est équivalent à « &T[1] » compte tenu de larithmétique des pointeurs

•  donc « * (p+1) » est équivalent à « *&T[1] » cest-à-dire « T[1] »

Généralisation: « T[i] » sécrit aussi « *(T+i) »

(4)

Pointeurs et tableaux

•  Le parcours d’un tableau de 10 éléments entiers (Mise à zéro de toutes les cellules)

•  Initialisation classique int T[10], i;!

! ! !for(i = 0; i < 10; i++) T[i] = 0;!

•  Ecriture à base d’adressage int T[10], i;!

! ! !for(i = 0; i < 10; i++) *(T+i) = 0;!

•  Ecriture exploitant la puissance des pointeurs int T[10], i, *p;!

! ! !for(i = 0, p = T; i < 10; i++) { *p = 0; p++; }!

! ! ! ! ! ! ! ! ! ! !

/* ou *p++ = 0; */!

(5)

Pointeurs et passage de paramètres

•  Rappel:

•  Le passage de paramètres dans une fonction C se fait toujours par valeur

•  Toutes les modifications faites sur les paramètres au cours de lexécution de la fonction ne sont pas préservées lors retour à la fonction appelante

Exercice:

•  Écrivez une fonction C de prototype « void echange(int, int) » qui prend deux variables entières comme paramètres et se contente de faire léchange de leur valeur respective

(6)

Pointeurs et passage de paramètres

•  Solution

void echange (int *x, int *y) {!

!int temp ;!

!temp = *x;!

!*x = *y;!

!*y = temp ;!

}!

•  A l’appel:

main ( ) {!

int i = 3, j = 2; !

echange ( &i, &j ) ; ! }!

(7)

Pointeurs et passage de paramètres

•  Le passage de paramètres par « variable » est possible en C mais il doit toujours se faire à l’aide des

adresses des paramètres

Ce qui explique:

•  On ne peut pas passer un tableau en paramètre par valeur

• Car on accède toujours à ce qu’il « pointe »

•  La présence obligatoire du « & » derrière les paramètres du

« scanf »

• Sinon à l’issue de la saisie, les saisies au clavier ne seraient jamais affectées aux variables paramètres de scanf!

•  Avantage : !

•  On ne fait qu’une copie d’un pointeur (4 ou 8 octets) au lieu de potentiellement beaucoup plus (structures…)

(8)

Pointeurs et passage de paramètres

•  Exercice

•  Écrire une fonction de recherche de la valeur maximum dans un tableau (paramètre) contenant n valeurs (paramètre). Cette fonction renverra la valeur de lindice de lélément maximum. A laide dun troisième paramètre, cette fonction permettra de récupérer dans la fonction appelante la valeur maximale.

(9)

Pointeurs et passage de paramètres

/* autre prototype : int max(int t[100], int nb_elt, int

*le_max) */!

int max(int *t, int nb_elt, int *le_max) {!

!int indice = 0, i;!

!*le_max = *t; /* équivalent *le_max = t[0]; */!

!for(i = 0; i < nb_elt; i++)!

! if(*t > *le_max) { indice = i; *le_max = *t; t++; }!

!return(indice);!

}!

!

main() { … j = max(Tab, 50, &max_de_Tab); …; }!

(10)

Arguments de la ligne de commande

•  int main(int argc, char * argv[])

•  Le premier argument représente le nombre d’arguments de la ligne de commande qui a appelé le programme.

Le second est un pointeur sur un tableau de chaînes de caractères qui contiennent les arguments (un par chaîne).

•  Exemple : echo bonjour, maître

argv echo\0

bonjour, maître\0

0 \0

(11)

Allocation dynamique

•  Avant de manipuler un pointeur, il faut l’initialiser.

•  Affecter à p l’adresse d’une autre variable

•  Affecter directement une valeur à *p

•  pour cela, il faut dabord réserver à *p un espace mémoire de taille adéquate

•  l’adresse de cet espace mémoire sera la valeur de p

•  L’opération consistant à réserver explicitement un espace mémoire pour stocker l’objet pointé s’appelle l’allocation dynamique.

(12)

CHAPITRE 3

Allocation mémoire

(13)

Domaine de validité des données et allocation mémoire

•  Stockage sur la pile : la déclaration d’une variable dans une fonction réserve automatiquement la mémoire pour celle-ci dans la pile d’exécution. La taille de la pile est limitée par le système.

•  exemple :

•  int x; //alloue sizeof(int) octets sur le sommet de la pile

int t[5];

•  À la sortie de la fonction ou du bloc, la mémoire est libérée automatiquement.

(14)

allocation mémoire

•  Stockage sur le tas : la déclaration d’une variable globale ou d’une variable ‘static’ dans une fonction réserve

automatiquement la mémoire nécessaire sur le tas.

•  Cela signifie que la variable est valide du lancement

jusqu’à la fin du programme (et par exemple, conserve sa valeur lors d’appels successifs à une fonction).

#include <stdio.h>

int i;

int main() {

}

#include <stdio.h>

int foo(int x) {

static int v=0;

v++;

….

}

(15)

Domaine de validité des données et allocation mémoire

•  L’allocation dynamique se fait également sur le tas : on gère soi-même (manuellement) la durée de vie des objets

•  Permet de stocker de gros objets qu’on ne pourrait pas retourner par des fonctions, par exemple, ou dont la taille dépasse la

capacité de la pile.

Évite de monopoliser de la mémoire pour rien

•  Indispensable lorsqu’on ne connaît pas la taille des données à l’avance.

La donnée est accessible depuis tout le programme tant qu’on n’a pas libéré la mémoire explicitement.

C’est une allocation plus « coûteuse » qui utilise des appels système.

(16)

Domaine de validité

•  Domaine de validité d’une variable = partie du programme où on peut l’utiliser (sans erreur)

•  Dépend:

•  de la place où est effectuée la déclaration

•  en dehors de toute fonction

•  en début de bloc

de la présence éventuelle dun mot clé

•  extern, static, register

(17)

Domaine de validité

Déclaration Effet

Mot-clé Place Glob./Loc. Decl./Def. Validité

rien rien static static extern register

hors fonction bloc

hors fonction bloc

bloc

global local global global global local

déclaration définition définition définition définition

définition

partout bloc fichier

bloc partout

bloc

(18)

Allocation dynamique

•  Syntaxe :

void* malloc(size_t nombre_octets)

•  Retourne un pointeur de type (void*) pointant vers un objet de taille nombre-octets octets.

•  Pour initialiser des pointeurs vers des objets d’un type donné, il est conseillé de convertir le type de la sortie de la fonction malloc à l’aide d’un cast.

•  Inclure stdlib.h

(19)

Allocation dynamique

•  Possible de connaître la taille d’une donnée quelconque, donc le nombre de mots qu’elle occupe en mémoire

•  fonction sizeof( )

•  Exemple :

printf (« taille dun entier : %d \n », sizeof(int));

•  affiche 2

(20)

Allocation dynamique

•  Si on voulait allouer dynamiquement un pointeur vers un entier, on écrirait :

#include <stdlib.h>

int *p;

p=(int*) malloc (sizeof (int) ) ; Ou

p= malloc(sizeof(*p));

(21)

Allocation dynamique

#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 après initialisation = %ld \n »,p);

*p=i;

printf(« Valeur de *p= %d \n »,*p);

}

(22)

Allocation dynamique

•  Remarque :

main( ){

int i=3;

int *p; // 2 variables allouées sur la pile p=&i;

}

Les variables i et *p sont identiques (même adresse)

•  Toute modification de l’une implique la modification de l’autre.

•  Ne nécessite pas d’allocation dynamique puisque

l’espace mémoire à l’adresse &i est déjà réservé pour un entier sur la pile.

(23)

Allocation dynamique

•  La fonction malloc permet également d’allouer un espace pour plusieurs objets contigus en mémoire.

#include <stdio.h>

#include <stdlib.h>

main( ) { int i=3;

int j=6;

int *p;

p=(int*)malloc (10000*sizeof(int));

*p=i;

*(p+1)=j;

}

(24)

Allocation dynamique

•  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é à zéro.

•  Syntaxe : void* calloc (size_t nb_obj, size_t taille_obj)

•  Ainsi,

p=(int*)calloc(N,sizeof(int));

est équivalent à

p=(int*)malloc(N*sizeof(int));

for (i=0;i<N;i++) *(p+i)=0;

(25)

Allocation dynamique

•  ATTENTION : Lorsque l’on n’a plus besoin de l’espace- mémoire alloué dynamiquement (cad quand on n’utilise plus le pointeur), il faut libérer cette place en mémoire

free(nom du pointeur);

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

(26)

Pointeurs et tableaux (suite)

•  La manipulation de tableaux et non de pointeurs possède certains inconvénients dus au fait qu’un tableau est un pointeur constant. Ainsi,

•  on ne peut pas toujours créer de tableaux dont la taille est une variable du programme (pas en C ANSI, on peut en C99)

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 quand on manipule des pointeurs alloués dynamiquement.

(27)

Pointeurs et tableaux

•  Créer un tableau d’entiers à n éléments, n variable #include <stdlib.h>

main( ){

int n;

int *tab;

...

tab=(int*)malloc(n*sizeof(int)); //ou (int*)calloc(n,sizeof(int));

...

free(tab);

}

•  Eléments de tab manipulés avec l’opérateur dindexation []

(28)

Pointeurs et tableaux

•  Tableau à deux dimensions = tableau de tableaux => pointeur vers un pointeur

(29)

Pointeurs et tableaux

•  Créer avec un pointeur de pointeur une matrice de k lignes et n colonnes à coefficients entiers

main( ){

int k,n;

int **mat;

mat=(int**)malloc(k*sizeof(int*));

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

mat[i]=(int*)malloc(n*sizeof(int));

...

for (i=0;i<k;i++) free(mat[i]);

free(mat);

}

(30)

Pointeurs et chaînes de caractères

•  chaîne de caractères = tableau à une dimension dobjets de type char, se terminant par \0 .

#include <stdio.h>

main( ) {

int i;

char * chaine;

chaine=« ceci est une chaine »;

for (i=0; *chaine!= \0 ; i++) chaine++;

printf(« nombre de caractères = %d \n »,i);

}

ATTENTION : si on fait un scanf, la mémoire n’a pas été allouée -> plantage !

(31)

Pointeurs et structures

•  On peut manipuler des pointeurs sur des structures

•  Si (*p) est une structure, on peut accéder à un champ de la structure pointée par l’expression

•  (*p).champ

•  Attention : parenthèses indispensables

•  Ecriture équivalente :

•  p - > champ

(32)

Structures auto-référencées

•  On a souvent besoin de modèles de structures dont un des membres est un pointeur vers une structure du même modèle. Cette représentation permet en particulier de

construire des listes chaînées.

•  Représentation par tableaux inadaptée (impose de connaître la taille maximale de la liste)

•  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.

(33)

Structures auto-référencées

struct cellule {

int valeur;

struct cellule * suiv;

};

typedef struct cellule *liste;

•  -> En TD, on verra les fonctions de manipulation des listes chaînées.

Références

Documents relatifs

 L'échauffement du mélange montre que cette transformation chimique libère de l'énergie thermique ( sous forme de chaleur)

Pour prouver la correction d’une fonction récursive, on procède en général par récurrence : on prouve d’abord que la sortie de la fonction est correcte pour les cas

Alors H est

Emplacement de la pile.- Pour ˆetre sˆ ur que le programme et la pile n’interf`erent pas, le microprocesseur i8086 a ´et´e con¸cu de telle fa¸con que le code et les

Exemple.- ´ Ecrivons un programme QBasic qui demande un entier naturel (de deux octets) et fait appel ` a un sous-programme en langage machine qui passe la valeur en

On s’intéresse dans tout ce problème à une suite infinie de lancers d’une pièce équilibrée autour duquel deux joueurs J et J 0 s’affrontent. Le joueur J l’emporte si

Pour supprimer un des fi chiers d’une pile, ouvrez simplement la pile et faites glisser l’élément à l’extérieur, vers l’endroit où vous voulez le déposer.. Pour supprimer

5. Sur la première pile uniquement, en utilisant le vocabulaire introduit, indiquer ce qui arrive aux électrons au niveau de chacune des électrodes en précisant celle