Modèle de mémoire C/C++ : Zones et allocation

Le C/C++ utilise un modèle de mémoire divisé en plusieurs zones distinctes, chacune avec des caractéristiques et des règles de gestion spécifiques.

Les Zones de Mémoire

  • La Pile (Stack) : Gérée automatiquement par le compilateur. Elle stocke les paramètres de fonction, les variables locales, et les adresses de retour. Son fonctionnement est similaire à celui d'une pile LIFO (Last-In, First-Out).
  • Le Tas (Heap) : Alloué et libéré explicitement par le programmeur à l'aide de fonctions comme malloc/free ou les opérateurs new/delete. Si non libérée, la mémoire peut être récupérée par le système d'exploitation à la fin du programme. Sa gestion s'apparente à celle d'une liste chaînée.
  • La Zone Globale/Statique (Global/Static) : Contient les variables globales et les variables statiques. Les variables globales initialisées et les variables statiques initialisées résident dans une première partie de cette zone, tandis que celles non initialisées occupent une zone adjacente. Cette mémoire est libérée par le système à la fin du programme.
  • La Zone de Constantes Littérales (String Literal Pool) : Stocke les chaînes de caractères littérales et les constantes. Cette mémoire est libérée par le système à la fin du programme.
  • La Zone de Code (Code/Text Segment) : Contient le code exécutable du programme.

Exemples de Code

Exemple 1 : Déclaration et Allocation

Cet exemple illustre la localisation des variables selon les zones mémoire.


// exemple_memoire.cpp
int compteurGlobal = 0;         // Zone Globale initialisée
char *pointeurGlobalNonInit;    // Zone Globale non initialisée

int main()
{
    int variableLocale;         // Zone Pile
    char chaineStack[] = "abc"; // Zone Pile pour le tableau et sa copie
    char *pointeurStack;        // Zone Pile pour le pointeur

    char *pointeurConstante = "def"; // "def" est dans la Zone Constantes Littérales, pointeurConstante est dans la Pile

    static int compteurStatic = 0; // Zone Globale/Statique initialisée

    pointeurGlobalNonInit = (char*)malloc(10); // Allocation sur le Tas
    char *pointeurTas = (char*)malloc(20);     // Alocation sur le Tas

    strcpy(pointeurGlobalNonInit, "bonjour"); // "bonjour" peut être dans la Zone Constantes Littérales et copiée sur le Tas. Optimisation possible.

    return 0;
}

Exemple 2 : Utilisation de new et tableaux

Démonstration des pointeurs, tableaux sur la pile et chaînes de caractères constantes.


#include <iostream>

using namespace std;

int main()
{
    int *ptrEntier = new int;    // Allocation d'un entier sur le Tas
    char tableauChaine[] = "abcd"; // Tableau sur la Pile
    char *ptrChaineConst = "efgh"; // Pointeur sur la Pile, chaîne dans Zone Constantes Littérales

    // Affichage des informations sur ptrEntier
    cout << "ptrEntier: " << ptrEntier << endl; // Adresse mémoire allouée sur le Tas
    cout << "*ptrEntier: " << *ptrEntier << endl; // Valeur initiale (non définie, souvent 0)
    cout << "&ptrEntier: " << &ptrEntier << endl; // Adresse du pointeur sur la Pile
    cout << endl;

    // Affichage des informations sur tableauChaine
    cout << "tableauChaine: " << tableauChaine << endl; // Contenu "abcd"
    cout << "*tableauChaine: " << *tableauChaine << endl; // Premier caractère 'a'
    cout << "&tableauChaine: " << &tableauChaine << endl; // Adresse du tableau sur la Pile
    cout << endl;
    cout << "(tableauChaine + 1): " << (tableauChaine + 1) << endl; // Affiche à partir du 2ème caractère ("bcd")
    cout << "*(tableauChaine + 1): " << *(tableauChaine + 1) << endl; // Deuxième caractère 'b'
    cout << "&tableauChaine[1]: " << &tableauChaine[1] << endl; // Adresse du 2ème caractère
    cout << endl;

    // Affichage des informations sur ptrChaineConst
    cout << "ptrChaineConst: " << ptrChaineConst << endl; // Contenu "efgh"
    cout << "*ptrChaineConst: " << *ptrChaineConst << endl; // Premier caractère 'e'
    cout << "&ptrChaineConst: " << &ptrChaineConst << endl; // Adresse du pointeur sur la Pile
    cout << endl;
    cout << "(ptrChaineConst + 1): " << (ptrChaineConst + 1) << endl; // Affiche à partir du 2ème caractère ("fgh")
    cout << "*(ptrChaineConst + 1): " << *(ptrChaineConst + 1) << endl; // Deuxième caractère 'f'
    cout << "&ptrChaineConst[1]: " << &ptrChaineConst[1] << endl; // Adresse du 2ème caractère

    delete ptrEntier; // Libération de la mémoire allouée sur le Tas
    return 0;
}

Exemple 3 : Tailles et strlen

Comparaison des tailles obtenues avec sizeof et strlen.


#include <iostream>
#includde <cstring> // Pour strlen

using namespace std;

int main()
{
    char tableauStr[] = "hello"; // Tableau sur la Pile, incluant le '\0'
    int tableauInt[] = {1, 2, 3, 4, 5, 6}; // Tableau sur la Pile
    char *ptrStrConst = "world"; // Pointeur sur la Pile, chaîne dans Zone Constantes Littérales
    int *ptrEntierTas = new int; // Pointeur vers un entier sur le Tas

    // Affichage des tailles et longueurs
    printf("Taille de tableauStr (incluant '\\0'): %zu octets\n", sizeof(tableauStr)); // 6
    printf("Taille mémoire de tableauInt: %zu octets\n", sizeof(tableauInt)); // 24 (6 * taille d'un int)
    printf("Nombre d'éléments dans tableauInt: %zu\n", sizeof(tableauInt) / sizeof(int)); // 6
    printf("Taille du pointeur ptrStrConst: %zu octets\n", sizeof(ptrStrConst)); // 8 (taille d'un pointeur sur système 64 bits)
    printf("Longueur de la chaîne pointée par ptrStrConst: %zu\n", strlen(ptrStrConst)); // 5
    printf("Taille du pointeur ptrEntierTas: %zu octets\n", sizeof(ptrEntierTas)); // 8 (taille d'un pointeur)

    cout << "----------------" << endl;

    int *ptrDynamique = new int; // Autre allocation sur le Tas
    cout << "*ptrDynamique (initial): " << *ptrDynamique << endl; // Valeur non définie
    delete ptrDynamique; // Libération

    char *ptrNouvelleConst = "sdf f"; // Nouvelle chaîne littérale
    cout << "ptrNouvelleConst: " << ptrNouvelleConst << endl; // Affiche "sdf f"

    return 0;
}

Exemple 4 : Accès aux caractères

Différences d'accès entre un pointeur vers une chaîne littérale et un tableau de caractères.


#include <iostream>
#include <cstring>

int main()
{
    char *pointeurVersConst = "abc"; // Pointeur sur la Pile, chaîne dans Zone Constantes Littérales
    char tableauLocal[] = "abc";   // Tableau sur la Pile

    // Itération sur pointeurVersConst
    for (size_t i = 0; i < strlen(pointeurVersConst); ++i)
    {
        std::cout << "Via pointeur: " << *(pointeurVersConst + i) << std::endl;
        std::cout << "Via indice: " << pointeurVersConst[i] << std::endl;
        std::cout << "Adresse: " << &(pointeurVersConst[i]) << std::endl;
    }

    std::cout << "--- Tableau Local ---" << std::endl;

    // Itération sur tableauLocal (incluant le caractère nul)
    // Note: sizeof(tableauLocal) inclut le '\0'
    for (size_t i = 0; i < sizeof(tableauLocal); ++i)
    {
        std::cout << "Via pointeur: " << *(tableauLocal + i) << std::endl;
        std::cout << "Via indice: " << tableauLocal[i] << std::endl;
        std::cout << "Adresse: " << &(tableauLocal[i]) << std::endl;
    }

    return 0;
}

Étiquettes: C++ mémoire pile tas statique

Publié le 27 juillet à 03h41