Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Langage C
  3. Les pointeurs
Extrait - Langage C Maîtriser la programmation procédurale (avec exercices pratiques) (2e édition)
Extraits du livre
Langage C Maîtriser la programmation procédurale (avec exercices pratiques) (2e édition)
2 avis
Revenir à la page d'achat du livre

Les pointeurs

Principe du pointeur

Voici quelques questions pour vous aider à synthétiser et retenir des contenus qui nous paraissent essentiels dans cette section :

Qu’est-ce qu’un pointeur ?

Quelles sont les principales utilisations des pointeurs ?

Quels sont les différents opérateurs associés ?

Comment fonctionnent ces opérateurs ?

Qu’est-ce que la valeur NULL, à quoi sert-elle ?

Qu’est-ce qu’un void* ? Comment l’utiliser ?

Que signifie « une allocation » en programmation ?

Qu’est-ce qu’une « allocation dynamique » ?

Citez une fonction C qui permet d’allouer dynamiquement de la mémoire.

Que signifie libérer la mémoire allouée ?

Comment libérer la mémoire allouée ?

Pourquoi libérer la mémoire allouée ?

Qu’est-ce qu’une adresse mémoire non valide ?

Quel type d’erreur caractéristique provoque une adresse mémoire non valide ?

Quelles différences y a-t-il entre :

const int *ptr ; 
int const *ptr ; 
int * const ptr ; 
const int *const ptr ; 

Peut-on avoir un tableau de pointeurs ?

Comment peut-on avoir un pointeur de fonction ?

Comment utiliser un pointeur de fonction ?

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

Allocation dynamique de tableaux

Voici quelques questions pour vous aider à synthétiser et retenir des contenus qui nous paraissent essentiels dans cette section :

Peut-on créer un tableau à partir d‘un pointeur ?

Comment créer un tableau à une dimension à partir d’un pointeur ?

Comment créer un tableau à deux dimensions à partir d’un pointeur ?

Une fonction peut-elle retourner un pointeur alloué ?

Quelles différences y-a-t-il entre un tableau static et un tableau dynamique ?

Quelles sont toutes les fonctions C d’allocation mémoire ?

1. Allouer un tableau en même temps qu’une structure

L’allocation dynamique d’une structure permet en même temps d’allouer dynamiquement un tableau placé comme dernier champ de cette structure et défini par l’opérateur crochet vide [ ]. Par exemple :

typedef struct {  
    int temps;  
    float stock[];  
}Mesure; 

Structure et tableau peuvent être alloués dynamiquement en une seule fois de la façon suivante :

Mesure* mesure = malloc(sizeof(Mesure) + sizeof(float[10])); 

La structure allouée contient ici un tableau de 10 float. Autre exemple :

Mesure* mesure = malloc(sizeof(Mesure) + 16); 

La structure allouée contient alors un tableau de 4 float (sizeof(float)*4 font 16 octets).

Mise en œuvre de l’exemple :

#include <stdio.h> 
#include <stdlib.h>  
   
#define NBMAX   10   
typedef struct {  
    int temps;  
    float stock[];  
}Mesure;  
   
void pause(void);  
   
int main()  
{  
    Mesure *mesure= malloc(sizeof(Mesure)+ sizeof(float[NBMAX]));  
      
    mesure->temps = 1;  
    for (int i = 0; i < NBMAX; i++)  
        mesure->stock[i] = (float)rand() / RAND_MAX * 10;  
    for (int i = 0; i < NBMAX; i++)  
        printf("stock[%d]=%.2f\n", i, mesure->stock[i]);  
   
    pause();  
    return 0;  
}...

Pointeurs en paramètres de fonction

Voici quelques questions pour vous aider à synthétiser et retenir des contenus qui nous paraissent essentiels dans cette section :

Peut-on avoir un pointeur en paramètre de fonction ?

Qu’est-ce que ça signifie concrètement pour la fonction ?

Quel nom donne-t-on à ce type de passage de valeur à une fonction ?

Quels avantages y a-t-il à utiliser des pointeurs en paramètre de fonction ?

Une fonction peut-t-elle retourner un tableau dynamique créé à partir d’un pointeur sans utiliser de return ? Comment faire ?

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

Établir des relations entre structures

Un pointeur de structure dans une structure permet d’associer les structures et de les mettre en relation. Nous pouvons distinguer deux cas : celui d’une association entre deux structures de types différents et celui d’une association entre deux structures de même type.

Questions sur la mise en relation de structures

Voici quelques questions pour vous aider à synthétiser et retenir des contenus qui nous paraissent essentiels dans cette section :

Peut-on placer un pointeur de structure dans une structure ?

Quel intérêt ?

Quelle possibilité offre dans une structure un pointeur du même type que cette structure ?

1. Association de structures de types différents

Par exemple, dans un programme nous devons avoir d’un côté des pilotes et de l’autre des avions. Chaque pilote peut piloter un avion et chaque avion possède ou non un pilote.

Pour programmer cette situation nous définissons deux structures : une structure pour pilote et une structure pour avion. Dans la structure pilote, nous plaçons un pointeur sur une structure avion et dans la structure avion nous plaçons un pointeur sur une structure pilote. Ainsi, chaque pilote peut être associé à un avion et réciproquement chaque avion peut être relié à un pilote.

Voici une structure pilote :

#define MAXCHAR    256 
typedefstruct avion 
{ 
    struct pilote*pilote; // peut pointer sur un pilote 
    char type[MAXCHAR]; 
    intnum; 
}avion; 

Une structure avion :

typedefstruct pilote 
{ 
    struct avion*avion; // peut pointer sur un avion 
    char nom[MAXCHAR]; 
 
}pilote; 

Un constructeur pour créer dynamiquement un pilote :

pilote* ConstructPilote(char*nom, avion*a)  
{  
    pilote* p;  
    p = (pilote*)malloc(sizeof(pilote));  
    strcpy_s(p->nom, MAXCHAR, nom);  
    p->avion = a;  
    if (a != NULL)  
           a->pilote = p;  
    return...