Allocation mémoire en C : pile, tas, zone globale, zone des constantes et zone de code

Le langage C divise la mémoire en plusieurs régions distinctes, chacune ayant un rôle spécifique.

1. La pile (Stack)

Caractéristiques
  • La pile est gérée automatiquement par le compilateur et le système d'exploitation — aucune intervention manuelle n'est requise.
  • Les données résidant sur la pile n'existent que pendant l'exécution de la fonction courante ; elles sont détruites dès que la fonction se termine.
  • La pile croît dans le sens des adresses décroissantes (du haut vers le bas). Sa taille maximale est fixée à la compilation : très rapide, mais limitée en capacité.
  • Elle suit le principe LIFO (dernier entré, premier sorti) : le dernier élément empillé est le premier à être libéré.
Contenu stocké
  • Les variables locales temporaires et les variables locales qualifiées const sont placées sur la pile.
  • Lors des appels de fonctions, les paramètres et les valeurs de retour y sont également stockés.

2. Le tas (Heap)

Caractéristiques
  • Le tas est géré manuellement par le programmeur via des fonctions d'allocation/libération.
  • Il croît dans le sens des adresses croissantes (du bas vers le haut). Sa taille est limitée par la mémoire physique ou virtuelle du système : plus lent que la pile, mais offre une grande flexibilité et un espace important.
Fonctions associées

Allocation dynamique avec malloc :

void *malloc(size_t nb_octets);
  • Le paramètre nb_octets indique la taille à allouer.
  • La valeur de retour est un pointeur de type void* pointant vers le début du bloc alloué. Ce pointeur peut être converti vers tout autre type de pointeur.

Libération de la mémoire avec free :

void free(void *pointeur);
  • Le paramètre est l'adresse de départ du bloc précédemment alloué. Tout oubli de libération entraîne une fuite de mémoire.

3. Zone globale (statique)

Description générale
  • Cette zone regroupe les variables dont la taille est connue à la compilation et dont la visibilité s'étend à toute la durée d'exécution du programme : variables globales et variables statiques.
  • Elle se divise en deux segments : .bss et .data, tous deux accessibles en lecture et en écriture.
Segment .bss
  • Les variables globales non initialisées et les variables statiques non initialisées y sont stockées.
  • Les variables globales/statiques initialisées à zéro s'y trouvent également.
  • Le segment .bss n'occupe pas d'espace dans le fichier exécutable ; son contenu est mis à zéro par le système d'exploitation au chargement.
Segment .data
  • Les variables globales initialisées (valeur non nulle) sont placées dans .data.
  • Les variables statiques initialisées (valeur non nulle) s'y trouvent aussi.
  • Contrairement au .bss, ce segment occupe de l'espace dans le fichier exécutable et est initialisé par le programme lui-même.

4. Zone des constantes

  • Les chaînes de caractères littérales et les constantes numériques sont rangées dans cette zone.
  • Les variables globales qualifiées const y résident également.
  • Pendant toute l'exécution, le contenu de cette zone est non modifiable.

5. Zone de code

  • Les instructions machine du programme sont stockées ici. Cette zone est en lecture seule : toute tentative d'écriture provoque une erreur.
  • Les chaînes littérales et les constantes définies par #define peuvent également s'y trouver selon le compilateur.

Répartition mémoire sur microcontrôleur STM32

1. Mémoire vive — RAM

  • La RAM est la mémoire interne avec laquelle le CPU échange directement les données ; on l'appelle aussi mémoire principale.
  • Elle est accessible en lecture/écriture à tout moment, très rapide, et sert de support temporaire pour les données des programmes en cours d'exécution.
  • Les données sont volatiles : à la coupure de l'alimentation, tout le contenu est perdu.

2. Mémoire morte — ROM (Flash)

  • La ROM contient des données écrites avant l'intégration dans le système. En fonctionnement, elle ne peut être que lue, contrairement à la RAM qui permet des écritures rapides.
  • Les données stockées en ROM sont stables et persitsantes même après coupure d'alimentation.

L'exemple ci-dessous s'appuie sur un chip STM32F103 avec l'environnement Keil V5. La configuration mémoire par défaut est la suivante :

  • La zone ROM démarre à l'adresse 0x08000000 avec une taille de 0x10000 octets. C'est une zone en lecture seule qui héberge la zone de code et la zone des constantes.
  • La zone RAM démarre à l'adresse 0x20000000 avec une taille de 0x5000 octets. C'est une zone en lecture/écriture qui contient la zone globale, le tas et la pile.

Vérification pratique sur STM32

Code de démonstration

#include "main.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "delay.h"
#include "uart3.h"
#include "led.h"

/* --- Zone globale --- */
        int  global_noninit;          /* variable globale non initialisee */
static  int  static_noninit;          /* variable statique non initialisee */
const   int  cglobal_noninit;         /* variable globale const non initialisee */

        int  global_init = 1;         /* variable globale initialisee */
static  int  static_init = 2;         /* variable statique initialisee */

/* --- Zone des constantes --- */
const   int  cglobal_init = 3;        /* variable globale const initialisee */

int main(void)
{
    SystemCoreClockUpdate();
    LED_GPIO_Config();
    Uart3_init();

    while (1)
    {
        /* --- Pile --- */
        int            stack_var;          /* variable locale non initialisee */
        int           *stack_ptr;          /* pointeur local non initialisee */
        int            stack_val = 3;      /* variable locale initialisee */
        char           stack_buf[10] = "hello"; /* tableau local initialise */
        const int      stack_cvar;         /* variable locale const non initialisee */
        const int      stack_cval = 3;     /* variable locale const initialisee */

        /* --- Tas --- */
        int *heap_a = (int *)malloc(sizeof(int));
        int *heap_b = (int *)malloc(sizeof(int));

        /* --- Zone globale (statique locale) --- */
        static int     s_local_noninit;
        static int     s_local_init = 2;

        /* --- Zone des constantes --- */
        char *str_ptr  = "I LOVE YOU";
        char *str_num  = "5201314";

        printf("\n[PILE] Adresses des variables locales\n");
        printf("  locale non init            : 0x%p\r\n", &stack_var);
        printf("  pointeur local non init     : 0x%p\r\n", &stack_ptr);
        printf("  locale init                 : 0x%p\r\n", &stack_val);
        printf("  tableau local init          : 0x%p\r\n",  stack_buf);
        printf("  const locale non init       : 0x%p\r\n", &stack_cvar);
        printf("  const locale init           : 0x%p\r\n", &stack_cval);

        printf("\n[TAS] Adresses allouees dynamiquement\n");
        printf("  heap_a                      : 0x%p\r\n", heap_a);
        printf("  heap_b                      : 0x%p\r\n", heap_b);

        printf("\n[ZONE GLOBALE] Adresses des variables globales/statiques\n");
        printf("  globale non init            : 0x%p\r\n", &global_noninit);
        printf("  statique non init           : 0x%p\r\n", &static_noninit);
        printf("  const globale non init      : 0x%p\r\n", &cglobal_noninit);
        printf("  globale init                : 0x%p\r\n", &global_init);
        printf("  statique init               : 0x%p\r\n", &static_init);
        printf("  statique locale non init    : 0x%p\r\n", &s_local_noninit);
        printf("  statique locale init        : 0x%p\r\n", &s_local_init);

        printf("\n[ZONE DES CONSTANTES]\n");
        printf("  const globale init          : 0x%p\r\n", &cglobal_init);
        printf("  pointeur str_ptr            : 0x%p\r\n",  str_ptr);
        printf("  pointeur str_num            : 0x%p\r\n",  str_num);

        printf("\n[ZONE DE CODE]\n");
        printf("  entree de main              : 0x%p\n", main);

        led485_flicker();
        delay_ms(1000);

        free(heap_a);
        free(heap_b);
    }
}

Résultats d'exécution

[PILE] Adresses des variables locales
  locale non init            : 0x20000654
  pointeur local non init     : 0x20000650
  locale init                 : 0x2000064c
  tableau local init          : 0x20000640
  const locale non init       : 0x2000063c
  const locale init           : 0x20000638

[TAS] Adresses allouees dynamiquement
  heap_a                      : 0x20000060
  heap_b                      : 0x20000068

[ZONE GLOBALE] Adresses des variables globales/statiques
  globale non init            : 0x20000014
  statique non init           : 0x20000018
  const globale non init      : 0x2000001c
  globale init                : 0x20000020
  statique init               : 0x20000024
  statique locale non init    : 0x20000028
  statique locale init        : 0x2000002c

[ZONE DES CONSTANTES]
  const globale init          : 0x080011a4
  pointeur str_ptr            : 0x08000e78
  pointeur str_num            : 0x08000e84

[ZONE DE CODE]
  entree de main              : 0x08000d6d

Étiquettes: C allocation mémoire STM32 RAM ROM

Publié le 13 juillet à 02h25