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/freeou les opérateursnew/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;
}