Blog ENI : Toute la veille numérique !
🎁 Jusqu'au 31/12, recevez notre
offre d'abonnement à la Bibliothèque Numérique. Cliquez ici
🎁 Jusqu'au 31/12, recevez notre
offre d'abonnement à la Bibliothèque Numérique. Cliquez ici
  1. Livres et vidéos
  2. Du C au C++
  3. Les pointeurs
Extrait - Du C au C++ De la programmation procédurale à l'objet (2ième édition)
Extraits du livre
Du C au C++ De la programmation procédurale à l'objet (2ième édition)
2 avis
Revenir à la page d'achat du livre

Les pointeurs

Principe du pointeur

1. Qu’est-ce qu’un pointeur ?

a. Mémoire RAM

Tous les objets informatiques que nous avons évoqués, qu’il s’agisse des variables, des fonctions, des tableaux, des structures, etc., tous correspondent à une inscription localisée quelque part dans la mémoire principale, dénommée également mémoire vive ou RAM. En un mot, chaque objet a une "adresse" dans la mémoire principale, cette adresse est un nombre et il correspond à un emplacement de la mémoire.

La mémoire en 32 bits est construite sur la base d’un entrelacement de quatre octets en quatre octets consécutifs. À partir de l’octet qui est la plus petite unité de mémoire adressable (l’octet a 8 bits et correspond en C au type char), la mémoire est constituée d’emplacements dits "mots" de quatre octets, 32 bits, dont l’adresse est toujours un nombre multiple de quatre généralement en hexadécimal. Voici une représentation de ce principe en partant par exemple de l’adresse "100" avec une progression de 4 en 4 ici en décimal pour simplifier.

images/04ri01.png

Dans un programme, lorsqu’une variable quelconque est déclarée, une adresse mémoire lui est automatiquement affectée à la compilation. Soient par exemple trois variables dans un programme :


char C=255; 
int A=256, B=129;
 

Admettons que l’adresse de C soit 100, l’adresse de A pourrait être 104 et l’adresse de B 116 ce qui donnerait en mémoire :

images/04ri02.png

la variable B a pour valeur 129 et est à l’adresse 116

la variable A a pour valeur 256 et est à l’adresse 104

la variable C a pour valeur 255 et est à l’adresse 100

Chaque nouvel objet commence toujours sur une frontière de mot. Seul des objets de type char (1 octet) ou éventuellement short (2 octets) peuvent posséder des adresses intermédiaires d’octets. C’est pourquoi l’adresse de la variable entière A est sur la frontière de mot qui suit l’adresse de la variable de type char C. Les trois octets d’adresses 101, 102 et 103 ne sont pas utilisés.

b. Une variable pointeur

Un pointeur est une variable qui prend pour valeur des adresses mémoire. De ce fait, une variable pointeur permet d’accéder au contenu de la mémoire à partir de l’adresse qu’elle contient. En elle-même la variable pointeur est codée sur quatre octets et possède, comme toute variable, sa propre adresse mémoire....

Allocation dynamique de tableaux

1. Allouer un tableau avec un pointeur

Voici une allocation dynamique pour un entier :


int *ptr; 
    ptr=(int*)malloc(sizeof(int) );
 

ce qui est identique à


ptr=(int*)malloc(sizeof(int) * 1);
 

Si ptr contient l’adresse du premier élément d’un tableau de 1 élément, cela permet d’écrire indifféremment :


*ptr=10; 
// ou 
ptr[0]=10;
 

Les deux notations sont équivalentes.

Et pour avoir plus d’un élément dans le tableau, il suffit d’allouer davantage de mémoire, au prorata du nombre souhaité des éléments. Par exemple pour avoir un tableau de 16 entiers :


int *ptr 
    ptr=(int*)malloc(sizeof(int)*16);
 

Le parcours du tableau est habituel :


int i, 
    for (i=0; i<16; i++){ 
        ptr[i]=rand()%100;                 // initialisation 
        printf("ptr[%d]=%d",i, ptr[i]);    // affichage  
    }
 

Éventuellement la taille du tableau peut être obtenue aléatoirement :


int*ptr, taille; 
    taille=rand() + 1; //au moins un élément 
    ptr=(int*)malloc(sizeof(int)*taille);
 

Si un dépassement de la mémoire RAM est à craindre au moment de l’allocation, il est possible de contrôler le retour de la fonction malloc() qui renvoie NULL si elle n’a pas pu réaliser l’opération demandée. Par exemple :


ptr=(int*)malloc(sizeof(int)*taille); 
if ( ptr==NULL) 
    exit(EXIT_FAILURE);
 

En cas d’erreur, le programme prend fin avec EXIT_FAILURE comme valeur de retour (EXIT_FAILURE vaut 1 et indique au système d’exploitation qu’un problème s’est produit lors de l’exécution du programme).

2. Allouer une matrice avec un pointeur de pointeur

Qu’est-ce qu’une matrice ? Une matrice est un tableau de tableaux d’objets d’un type donné. Par exemple une matrice d’entiers :


int mat[4][7]
 

mat est un tableau de 4 tableaux de 7 entiers. Du fait de l’équivalence tableau-pointeur, la matrice peut être remplacée par un pointeur de tableau, c’est-à-dire par un pointeur de pointeur, ce qui s’écrit :


int**mat;
 

Le premier niveau de pointeur sert à allouer un tableau de pointeurs. C’est le tableau des lignes et il a la taille du nombre de lignes souhaité. Par exemple...

Pointeurs en paramètres de fonction

1. Passage par référence

Les paramètres de fonctions sont des variables locales à la fonction initialisées avec des valeurs au moment de l’appel de la fonction. Lors d’un passage de tableau, le paramètre est un pointeur qui prend comme valeur l’adresse du premier élément qui est aussi l’adresse de tout le bloc mémoire du tableau. Cette section a pour objet l’étude du passage d’adresse mémoire pour n’importe quel type de variable et pas seulement les tableaux. C’est ce que l’on appelle un "passage par référence" et il s’agit de la référence à une variable via son adresse mémoire. Nous préciserons également quelques finesses quant au passage des tableaux.

a. Cas général d’une variable quelconque

La fonction modif() a un paramètre auquel elle affecte la valeur 50 :


void modif(int e1) 
{ 
    e1=50; 
}
 

dans le main() une variable v est initialisée avec la valeur 10. La fonction modif() est appelée et la valeur de v est affectée à son paramètre ce qui donne :


#include <stdio.h> 
#include <stdlib.h> 
 
int main() 
{ 
int v=10; 
    modif(v); 
    printf("v=%d \n", v); 
    return 0; 
}
 

Qu’imprime la fonction printf() ?

... La variable v, locale au contexte d’appel, vaut toujours 10.

Le paramètre de la fonction est une autre variable, locale à la fonction, à laquelle une valeur est affectée au moment de l’appel. Toutes les modifications faites sur cette variable locale à la fonction dans le corps de la fonction modifient uniquement cette variable locale à la fonction.

En C le passage des arguments se fait toujours par valeur et c’est uniquement la valeur des variables qui est affectée aux paramètres des fonctions. Ce n’est pas la variable elle-même qui deviendrait paramètre de la fonction.

Toutefois, si l’on utilise un pointeur comme paramètre de fonction et que l’on passe comme valeur pour le paramètre l’adresse mémoire de la variable, il devient possible de modifier la valeur de cette variable en passant par son adresse mémoire....

Fichiers (type FILE*)

1. Notions de base

a. Le type FILE*

Dans un programme C un fichier est toujours une structure de type FILE manipulée via son adresse par un pointeur FILE*. Pour utiliser un fichier dans un programme, ou une fonction du programme, la première chose à faire est de déclarer un FILE* :


#include <stdio.h> 
#include <stdlib.h> 
 
int main() 
{ 
FILE*f;  
    (...) 
    return 0; 
}
 

b. Ouverture et fermeture d’un fichier

Dans la librairie standard stdio.h, la fonction :


FILE* fopen( const char* name ,const char* mode);
 

permet d’ouvrir un fichier existant ou de créer un fichier. Cette fonction ouvre le fichier dont le nom comprenant le chemin d’accès est spécifié au premier paramètre et selon le mode donné par le second paramètre. Les deux sont des chaînes de caractères. Il y a six modes possibles : r, w, a, r+, w+, a+ et le caractère "b" ajouté ensuite permet de sélectionner une entrée/sortie en mode binaire.

Voici un tableau récapitulatif des différents modes possibles :

Mode

Accès

Positionnement en écriture

Si le fichier existe

Si le fichier n’existe pas

r

Lecture

au début

ouverture

erreur

w

a

Écriture

Écriture

au début

à la fin

initialisation

ouverture

création

création

r+

w+

a+

Lecture

et

écriture

au début

au début

à la fin

ouverture

initialisation

ouverture

erreur

création

création

Suffixe b : à ajouter pour les entrées/sorties binaires.

Attention, l’initialisation d’un fichier existant signifie qu’il est ramené à une taille nulle, c’est-à-dire que toutes les données qu’il contient sont perdues.

Pour refermer un fichier précédemment ouvert, la librairie stdio.h propose la fonction :


int   fclose(FILE*);
 

Cette fonction referme le fichier indiqué via le pointeur au paramètre p. Elle retourne EOF sur erreur et zéro sinon. EOF est une valeur sentinelle qui indique la fin du fichier.

Exemple d’ouverture/fermeture de fichier en mode binaire :


#include <stdio.h> 
#include <stdlib.h> 
 
int main() 
{ 
FILE*f; 
 
    if (f=fopen("test.bin","rb")){ ...