Chargement...

Notions d’adresses et de mémoire

Comprendre les adresses mémoire : octets, disposition et références par pointeurs.

En C++, les données sont stockées dans la mémoire (RAM) de l’ordinateur. Chaque variable possède sa propre adresse en mémoire. Comprendre les adresses mémoire est essentiel pour maîtriser les fonctionnalités puissantes du C++ comme les pointeurs. Dans cet article, nous verrons la structure de la mémoire, le concept d’adresse et comment les variables sont stockées en mémoire.


1. Qu’est-ce que la mémoire ?

La mémoire (RAM) est l’espace où les données et les instructions sont temporairement stockées pendant l’exécution d’un programme. Lorsque le programme se termine, les données présentes dans la mémoire sont effacées. Le compilateur C++ réserve un espace mémoire pour chaque variable et lui attribue une adresse.

Par exemple, dans le code suivant, chaque variable possède une adresse unique :


#include <iostream>
using namespace std;

int main() {
    int age = 25;
    double pi = 3.14;

    cout << "Adresse de age : " << &age << endl;
    cout << "Adresse de pi : " << &pi << endl;

    return 0;
}

Exemple de sortie :


Adresse de age : 0x61ff0c
Adresse de pi : 0x61ff08

Chaque adresse représente une position physique dans la mémoire et est généralement affichée au format **hexadécimal**.


2. L’opérateur & (Adresse)

L’opérateur & renvoie l’adresse d’une variable — c’est-à-dire l’emplacement mémoire où sa valeur est stockée.


int nombre = 100;
cout << "Adresse de nombre : " << &nombre << endl;
cout << "Valeur de nombre : " << nombre << endl;

L’adresse est un emplacement, tandis que la valeur est la donnée stockée à cet endroit. Cette distinction est essentielle pour comprendre le concept de pointeur.


3. Structure de la mémoire (Stack et Heap)

Lorsqu’un programme C++ s’exécute, la mémoire est généralement divisée en deux zones principales :

ZoneDescriptionUtilisation
Stack (pile) Contient les variables automatiques (locales). L’espace est alloué à l’appel d’une fonction et libéré à sa fin. Variables à durée de vie automatique (int, double, etc.)
Heap (tas) Espace réservé dynamiquement. Géré manuellement avec new et delete. Structures de données dynamiques (objets créés avec new)

Exemple :


int x = 10;          // Stocké dans la pile
int *p = new int(5); // Stocké dans le tas

Le pointeur p lui-même se trouve dans la pile, mais la valeur vers laquelle il pointe se trouve dans le tas. Cette distinction est importante pour éviter les fuites de mémoire (memory leaks).


4. L’opérateur sizeof

L’opérateur sizeof renvoie la taille (en octets) qu’occupe une variable ou un type de donnée dans la mémoire.


#include <iostream>
using namespace std;

int main() {
    cout << "Taille de int : " << sizeof(int) << " octets" << endl;
    cout << "Taille de double : " << sizeof(double) << " octets" << endl;
    cout << "Taille de char : " << sizeof(char) << " octets" << endl;
    return 0;
}

Valeurs typiques sur la plupart des systèmes :


5. Visualisation des variables en mémoire

Exemple :


int a = 5;
int b = 10;

Représentation en mémoire :


Adresse     Valeur
0x61ff0c → 5   (a)
0x61ff08 → 10  (b)

Chaque variable possède une adresse distincte. Ces adresses peuvent augmenter ou diminuer selon la disposition mémoire.


6. Introduction aux pointeurs

Un pointeur est une variable spéciale qui stocke l’adresse d’une autre variable. L’opérateur * (astérisque) est utilisé pour déclarer un pointeur.


#include <iostream>
using namespace std;

int main() {
    int nombre = 42;
    int *ptr = &nombre; // L’adresse de nombre est assignée à ptr

    cout << "Valeur de nombre : " << nombre << endl;
    cout << "Valeur pointée par ptr : " << *ptr << endl;
    cout << "Adresse contenue dans ptr : " << ptr << endl;

    return 0;
}

Dans cet exemple, ptr contient l’adresse de nombre. L’expression *ptr permet d’accéder à la valeur stockée à cette adresse.


7. Pointeur nul et utilisation sûre

Un pointeur ne doit jamais pointer vers une adresse invalide. S’il n’est pas encore initialisé, il doit recevoir la valeur nullptr.


int *p = nullptr;

if (p == nullptr) {
    cout << "Le pointeur ne pointe vers aucune adresse." << endl;
}

Cette vérification empêche les erreurs d’exécution et les segmentation faults.


8. Schéma mémoire avec pointeur

Exemple :


int x = 10;
int *p = &x;

Représentation mémoire :


Variable | Adresse  | Valeur
x        | 0x61ff0c | 10
p        | 0x61ff08 | 0x61ff0c

Le pointeur p stocke l’adresse de x. En utilisant *p, on accède à la valeur de x.


9. Gestion de la mémoire et sécurité

En C++, la gestion directe de la mémoire offre une grande flexibilité, mais aussi une grande responsabilité. Toute mémoire allouée dynamiquement doit être libérée avec delete.


int *p = new int(100); // Allocation dans le tas
cout << *p << endl;
delete p;              // Libération de la mémoire
p = nullptr;           // Évite les pointeurs pendants

Une fuite de mémoire se produit lorsque la mémoire n’est pas correctement libérée, ce qui peut ralentir le programme au fil du temps. Ainsi, chaque new doit être accompagné d’un delete.


10. TL;DR

  • Chaque variable a une adresse mémoire (& pour l’obtenir).
  • Stack → mémoire automatique ; Heap → mémoire dynamique.
  • sizeof indique la taille d’un type.
  • int *p déclare un pointeur ; *p accède à la valeur pointée.
  • nullptr doit être utilisé comme valeur d’initialisation sûre.
  • La mémoire allouée avec new doit être libérée avec delete.
  • Tous les exemples peuvent être exécutés dans Visual Studio 2022 ou GCC 11+.

Articles connexes