---
title: "Partie 2 : Les langages comme C/C++ ou Zig permettent de contrôler la mémoire mais à quel prix ?"
description: "C évite le besoin d'avoir un garbage collector et donne un contrôle manuel de la mémoire, mais ouvre la porte à des bugs dangereux."
url: "https://mayorana.ch/fr/blog/c-low-level-cost-fr"
language: "fr"
date: "2025-08-08"
author: "Mohamed Bennekrouf"
tags:
  - "rust"
  - "c"
  - "memory"
  - "dangling-pointer"
  - "undefined-behavior"
en_version: "https://mayorana.ch/en/blog/c-low-level-cost.md"
---

# Partie 2 : Les langages comme C/C++ ou Zig permettent de contrôler la mémoire mais à quel prix ?

Avec C, il n'y a pas de runtime, pas de GC.

```c
char* msg = malloc(100);
strcpy(msg, "hello");
free(msg);
printf("%s", msg); // ❌ Use after free
```

<div class="svg-container" style="margin:2rem 0;">
<svg class="cmem-fig" viewBox="0 0 800 220" width="100%" style="height:auto;max-width:780px;display:block;margin:0 auto;" role="img" aria-label="Une séquence use-after-free : allocation, écriture, libération, puis une quatrième étape lit la mémoire libérée et déclenche un comportement indéfini">
<style>
.cmem-fig{--bg:#f8fafc;--box:#ffffff;--tx:#1e293b;--mut:#64748b;--ln:#cbd5e1;--ac:#FF6B00}
:root.dark .cmem-fig,[data-theme="dark"] .cmem-fig{--bg:#0f172a;--box:#1e293b;--tx:#f8fafc;--mut:#94a3b8;--ln:#475569}
.cmem-fig text{font-family:ui-sans-serif,system-ui,sans-serif;fill:var(--tx)}
.cmem-fig .title{font-size:13px;font-weight:700}
.cmem-fig .body{font-size:12px;font-weight:600}
.cmem-fig .cap{font-size:11px;fill:var(--mut)}
.cmem-fig .box{fill:var(--box);stroke:var(--ln);stroke-width:1.5}
.cmem-fig .acbox{fill:var(--ac);stroke:var(--ac)}
.cmem-fig .ln{stroke:var(--ln);stroke-width:1.5;fill:none}
</style>
<!-- defs -->
<defs>
<marker id="cmem-arrow-fr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="var(--ln)"></path>
</marker>
</defs>
<!-- step 1 -->
<rect class="box" x="10" y="70" width="170" height="80" rx="8"></rect>
<text x="95" y="100" text-anchor="middle" class="title">malloc(100)</text>
<text x="95" y="120" text-anchor="middle" class="cap">mémoire allouée</text>
<text x="95" y="135" text-anchor="middle" class="cap">msg → pointeur valide</text>
<!-- step 2 -->
<rect class="box" x="210" y="70" width="170" height="80" rx="8"></rect>
<text x="295" y="100" text-anchor="middle" class="title">strcpy(msg, ..)</text>
<text x="295" y="120" text-anchor="middle" class="cap">écrit "hello"</text>
<text x="295" y="135" text-anchor="middle" class="cap">dans le bloc alloué</text>
<!-- step 3 -->
<rect class="box" x="410" y="70" width="170" height="80" rx="8"></rect>
<text x="495" y="100" text-anchor="middle" class="title">free(msg)</text>
<text x="495" y="120" text-anchor="middle" class="cap">mémoire libérée</text>
<text x="495" y="135" text-anchor="middle" class="cap">msg est maintenant pendant</text>
<!-- step 4 accent -->
<rect class="acbox" x="610" y="70" width="180" height="80" rx="8"></rect>
<text x="700" y="100" text-anchor="middle" class="title" fill="#ffffff">printf(msg)</text>
<text x="700" y="120" text-anchor="middle" class="cap" fill="#ffffff">lit mémoire libérée</text>
<text x="700" y="135" text-anchor="middle" class="cap" fill="#ffffff">comportement indéfini</text>
<!-- arrows -->
<path class="ln" d="M180,110 L210,110" marker-end="url(#cmem-arrow-fr)"></path>
<path class="ln" d="M380,110 L410,110" marker-end="url(#cmem-arrow-fr)"></path>
<path class="ln" d="M580,110 L610,110" marker-end="url(#cmem-arrow-fr)"></path>
<!-- caption -->
<text x="400" y="195" text-anchor="middle" class="cap">Rien en C n'empêche l'étape 4 de lire une mémoire qui ne lui appartient plus</text>
</svg>
</div>

## Pièges courants

| Problème | Code | Risque |
|----------|------|--------|
| Use-after-free | `printf("%s", msg);` | Undefined behavior |
| Double free | `free(msg); free(msg);` | Heap corruption |
| Buffer overflow | `char buf[4]; strcpy(buf, "long");` | Memory corruption |
| Memory leak | `malloc(...)` sans `free` | Crashes lents |

## Modèle de mémoire Manuelle

Tu dois :
- Allouer la mémoire
- Tracker l'ownership  
- La libérer manuellement
- Éviter d'accéder à la mémoire freed ou invalide

## Conséquences réelles

Voici quelques bugs connus.

### Heartbleed (OpenSSL)
```c
// Version simplifiée du bug
char* buffer = malloc(payload_length);
memcpy(buffer, payload, payload_length); // Pas de bounds check !
// L'attaquant pouvait lire au-delà du buffer
```

**Impact :** Plus de 500 000 serveurs ont exposé leurs clés privées et mots de passe.

### CVE-2021-44228 (Équivalent Log4Shell en C)
```c
char* user_input = get_user_data();
sprintf(log_buffer, "User: %s", user_input); // Buffer overflow possible
```

**Le Problème :** Pas de bounds checking automatique signifie que les attaquants peuvent :
- Crasher ton programme
- Exécuter du code arbitraire
- Voler des données sensibles

## Sécurité de la mémoire niveau statique (dans le code statique, avant l'exécution / runtime)

**Vulnérabilités de sécurité par catégorie :**
- **70%** des bugs sécurité Microsoft : problèmes de memory safety
- **65%** des vulnérabilités Chrome : memory corruption  
- **~50%** des patches sécurité Android : liés à la mémoire

## Un poid pour le développeur

### Chaque allocation nécessite un tracking
```c
typedef struct {
    char* data;
    size_t size;
} Buffer;

Buffer* create_buffer(size_t size) {
    Buffer* buf = malloc(sizeof(Buffer));
    if (!buf) return NULL;
    
    buf->data = malloc(size);
    if (!buf->data) {
        free(buf);  // Il faut se rappeler de nettoyer !
        return NULL;
    }
    
    buf->size = size;
    return buf;
}

void destroy_buffer(Buffer* buf) {
    if (buf) {
        free(buf->data);  // Il faut free dans le bon ordre
        free(buf);
    }
}
```

**Saturation mental :** Chaque fonction doit considérer :
- Qui possède ce pointer ?
- Quand doit-il être liberé ?
- Est-il encore valide ?

### Debugging des problèmes mémoire
```bash
$ valgrind ./my_program
==12345== Invalid read of size 4
==12345==    at 0x40084B: main (test.c:10)
==12345==  Address 0x5204044 is 0 bytes after a block of size 4 alloc'd
==12345==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
```

**Un des problèmes majeurs :** Les bugs sont découverts trop tard, au runtime, pas au moment de la compilation.

## Trade-off performance vs sécurité

### Caractéristiques performance de C
```c
// Zero overhead - accès mémoire direct
int sum = 0;
for (int i = 0; i < 1000000; i++) {
    sum += array[i];  // Pas de bounds checking
}
```

**Vitesse :** ✅ Performance maximale  
**Sécurité :** ❌ Une erreur = vulnérabilité de sécurité

### Contrôle de l'empreinte mémoire
```c
// Contrôle précis de l'emprunte mémoire
struct Point {
    float x, y, z;     // Exactement 12 bytes
} __attribute__((packed));

Point* points = malloc(1000 * sizeof(Point)); // Allocation prévisible
```

**Contrôle :** ✅ Contrôle complet de l'emprunte mémoire
**Risque :** ❌ Gestion manuelle des lifetimes

## Les outils aident, mais ne suffisent pas

### Static Analysis
```c
// clang-static-analyzer peut attraper certains problèmes
char* ptr = malloc(10);
free(ptr);
*ptr = 'x';  // ⚠️ Warning: use after free
```

### Runtime Detection
```c
// AddressSanitizer (ASan) attrape les bugs au runtime
$ gcc -fsanitize=address program.c
$ ./a.out
=================================================================
==12345==ERROR: AddressSanitizer: heap-use-after-free
```

### La Limitation
- **Outils static :** Ratent les cas complexes, faux positifs
- **Outils runtime :** Ne détectent les bugs qui s'exécutent que pendant les tests
- **Code review :** Erreur humaine, chronophage

<div class="svg-container" style="margin:2rem 0;">
<svg class="cmemb-fig" viewBox="0 0 800 270" width="100%" style="height:auto;max-width:780px;display:block;margin:0 auto;" role="img" aria-label="L'analyse statique, les sanitizers et la revue de code ne couvrent chacun qu'une partie de l'espace des bugs, si bien qu'un résidu de bugs mémoire atteint quand même la production">
<!-- style -->
<style>
.cmemb-fig{--bg:#f8fafc;--box:#ffffff;--tx:#1e293b;--mut:#64748b;--ln:#cbd5e1;--ac:#FF6B00;--bad:#e11d48}
:root.dark .cmemb-fig,[data-theme="dark"] .cmemb-fig{--bg:#0f172a;--box:#1e293b;--tx:#f8fafc;--mut:#94a3b8;--ln:#475569}
.cmemb-fig .box{fill:var(--box);stroke:var(--ln);stroke-width:1.5}
.cmemb-fig .boxbad{fill:var(--box);stroke:var(--bad);stroke-width:2}
.cmemb-fig .ti{fill:var(--tx);font:700 13px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.cmemb-fig .tx{fill:var(--tx);font:600 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.cmemb-fig .mut{fill:var(--mut);font:11px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.cmemb-fig .bad{fill:var(--bad);font:700 13px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.cmemb-fig .ac{fill:var(--ac);font:600 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.cmemb-fig .ln{stroke:var(--ln);stroke-width:1.5;fill:none}
</style>
<!-- defs -->
<defs>
<marker id="cmemb-arrow-fr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0L10,5L0,10z" fill="var(--ln)"/></marker>
</defs>
<!-- title -->
<text x="400" y="24" class="ti">Chaque filet de sécurité en C a son trou ailleurs</text>
<!-- static analysis -->
<rect x="25" y="40" width="235" height="100" rx="7" class="box"/>
<text x="142" y="64" class="tx">Analyse statique</text>
<text x="142" y="88" class="mut">lit le code, ne l'exécute pas</text>
<text x="142" y="108" class="mut">rate les pointeurs dont le</text>
<text x="142" y="126" class="mut">chemin est indécidable — et bruite</text>
<!-- sanitizers -->
<rect x="282" y="40" width="235" height="100" rx="7" class="box"/>
<text x="399" y="64" class="tx">ASan / Valgrind</text>
<text x="399" y="88" class="mut">exécute le vrai programme</text>
<text x="399" y="108" class="mut">mais ne voit que les lignes</text>
<text x="399" y="126" class="mut">réellement couvertes par les tests</text>
<!-- code review -->
<rect x="539" y="40" width="235" height="100" rx="7" class="box"/>
<text x="656" y="64" class="tx">Revue de code</text>
<text x="656" y="88" class="mut">comprend l'intention</text>
<text x="656" y="108" class="mut">mais un pointeur libéré peut être</text>
<text x="656" y="126" class="mut">à des fichiers de son usage</text>
<!-- Y-merge -->
<path d="M142,140 L142,164 L399,164" class="ln"/>
<path d="M399,140 L399,164" class="ln"/>
<path d="M656,140 L656,164 L399,164" class="ln"/>
<path d="M399,164 L399,184" class="ln" marker-end="url(#cmemb-arrow-fr)"/>
<!-- residue -->
<rect x="230" y="184" width="340" height="46" rx="6" class="boxbad"/>
<text x="400" y="204" class="bad">l'union des trois ≠ tout l'espace</text>
<text x="400" y="221" class="mut">ce qu'aucun des trois n'a vu part en production</text>
<!-- footer -->
<text x="400" y="256" class="ac">Rust supprime la catégorie au lieu de traquer les cas — aucun de ces bugs ne compile</text>
</svg>
</div>

## Pourquoi C est toujours utilisé malgré les risques

### Exigences de programmation système
- **Systèmes d'exploitation :** Besoin d'accès direct au hardware
- **Systèmes embarqués :** Contraintes mémoire, pas de place pour un runtime
- **Code critique en performance :** Chaque nanoseconde compte

### Legacy et écosystème
- **Bases de code massives :** Décennies de code C en production
- **Écosystème de librairies :** La plupart des librairies système écrites en C
- **Connaissance développeur :** Générations de programmeurs C

## Le problème Fondamental

C te donne deux mauvais choix :

**Option 1 : Gestion manuelle de la mémoire**
```c
char* data = malloc(size);
// ... logique complexe ...
if (error) {
    free(data);  // Il faut se souvenir du cleanup dans TOUS les chemins
    return -1;
}
// ... plus de logique ...
free(data);  // Facile d'oublier ou de double-free
```

**Option 2 : [Garbage collector](https://mayorana.ch/fr/blog/why-garbage-collector-fr)**
- Ajouter une librairie GC comme Boehm GC
- Perdre la prévisibilité des performances
- Toujours possible d'avoir des fuites mémoires

## Points clés
✅ **Performance prévisible - pas de pauses GC**  
✅ **Contrôle complet de l'emprunte mémoire**  
✅ **Overhead runtime minimal**  
❌ **Unsafe par défaut - une erreur = vulnérabilité**  
❌ **Responsabilité élevé pour les développeurs**  
❌ **La plupart des bugs sécurité viennent des problèmes mémoire**  
❌ **Les outils détectent les bugs après qu'ils soient écrits, pas avant**

---

**Le Défi :** Nous voulons la performance de C/C++ sans ses inconvénients.

**La Question :** Et si le compilateur pouvait prévenir les bugs mémoire au moment de la compilation ?

**Suivant :** "Voir la partie 3 de cette article"
