Pourquoi le C, histoire, plan des séances, environnement, chaîne de compilation, premier programme
Département de Mathématiques et Informatique, Faculté des Sciences et Techniques, Université Cheikh Anta Diop de Dakar
Ce que la séance construit
Le cadre du cours : pourquoi le C reste le langage des couches basses, d’où il vient et comment sa norme évolue, ce que les six séances suivantes construisent ; puis l’atelier : l’environnement Docker du cours, le chemin d’un fichier source jusqu’au processus qui s’exécute, et les outils qui observent ce processus (gcc, gdb, valgrind, AddressSanitizer, make).
__STDC_VERSION__).D’où l’on vient, où l’on va
Amont. Algorithmique : variables, conditions, boucles, sous-programmes ; un premier contact avec le C (écrire, compiler et exécuter un programme court).
Cette séance. Le cadre du cours et l’atelier : environnement Docker, chaîne de compilation, premier programme suivi ligne par ligne, outils d’observation. Le TP-1 installe l’environnement sur chaque machine.
Aval. Séance 1 : la syntaxe complète du C, instruction par instruction, avec ce que chaque ligne fait des variables ; les phases de la compilation y reviennent, avec la compilation séparée et l’édition de liens.
Le cours part d’une syntaxe supposée connue et regarde ce qu’elle devient sur la machine.
Ce que le cours apprend
Ce cours n’est pas une introduction au C. Il montre, pour chaque construction du langage, ce que la machine en fait : les octets d’une valeur, l’adresse d’une variable, la place d’un champ dans une structure, le bloc rendu par malloc, l’appel d’une fonction par son adresse. L’objectif est la compréhension des mécanismes, de l’organisation de la mémoire et des outils du programmeur système.
La méthode
Addr | +0 … +7.Références du cours
R. E. Bryant, D. R. O’Hallaron, Computer Systems: A Programmer’s Perspective, 3e éd., 2016 ; cours CSE 351 (The Hardware/Software Interface) et CSE 333 (Systems Programming) de l’université de Washington.
struct mesure {
char code;
int valeur;
char unite;
};
int main(void)
{
double a = 0.1 + 0.2;
printf("%d\n", a == 0.3);
printf("%.17f\n", a);
int x = 0x11223344;
unsigned char *o = (unsigned char *)&x;
for (int k = 0; k < 4; k++)
printf("%02x ", o[k]);
printf("\n");
printf("%zu\n", sizeof(struct mesure));
return 0;
}Résultat \(\rightarrow\)
0
0.30000000000000004
44 33 22 11
12
Où le cours répond
0, et non 1 |
Séance 2 : 0,1 n’a pas d’écriture binaire finie (IEEE 754) |
|---|---|
44 33 22 11 |
Séance 3 : octet de poids faible en premier (little-endian) |
12, et non 6 |
Séance 4 : alignement des champs, padding |
Lecture. Trois résultats que la syntaxe n’explique pas : représentation des valeurs, place en mémoire.
Le C est le langage des couches basses : celles sur lesquelles les autres langages s’exécutent.
Systèmes d’exploitation
Linux : plus de 40 millions de lignes dans l’arbre des sources du noyau (début 2025), en très grande majorité du C, avec de l’assembleur et, depuis la version 6.1 (2022), une infrastructure pour Rust, utilisée par de premiers pilotes depuis la version 6.8 (2024). Windows : noyau NT écrit pour l’essentiel en C, avec du C++ et de l’assembleur. macOS, iOS : noyau XNU en C, pilotes (IOKit) en C++. Les interfaces système POSIX (open, read, write, fork) sont définies comme des fonctions C.
Systèmes embarqués
Micrologiciels des microcontrôleurs (ARM Cortex-M, AVR, PIC), noyaux temps réel (FreeRTOS, Zephyr), objets connectés, calculateurs des véhicules (ECU) reliés par le bus CAN, appareils médicaux : le C y est choisi pour la petite taille du code produit, un temps d’exécution prévisible et l’accès direct aux registres du matériel.
Écrits en C
| bases de données | PostgreSQL, SQLite, Redis |
|---|---|
| interpréteurs | CPython (l’interpréteur Python de référence), Lua, Ruby (MRI), PHP |
| serveurs, réseau | nginx, Apache httpd, OpenSSH, curl |
| outils du système | bash, zsh, coreutils (ls, cp…), Git |
| calcul, multimédia | FFmpeg, x264, FFTW, OpenBLAS (C et assembleur) |
À ne pas confondre
Sont écrits en C++, et non en C : MySQL (pour l’essentiel), la machine virtuelle Java HotSpot, GCC depuis la version 4.8 (2013), Clang et LLVM. Les bibliothèques de référence BLAS et LAPACK sont écrites en Fortran ; OpenBLAS en est une implémentation en C et en assembleur.
Calcul pour l’IA
Les noyaux de calcul de PyTorch (ATen, c10), de TensorFlow et d’ONNX Runtime sont écrits en C++ et en CUDA, souvent derrière des API C (cuDNN, ONNX Runtime, extensions de CPython). Sur processeur, les produits de matrices passent par OpenBLAS (C et assembleur).
Cryptographie
OpenSSL, libsodium, WireGuard dans le noyau Linux (environ 4000 lignes) : du C, pour la vitesse et la portabilité ; c’est la petite taille du code de WireGuard, non le langage, qui réduit sa surface d’attaque.
La contrepartie
Le C ne vérifie ni les indices, ni les pointeurs, ni la durée de vie des blocs : environ 70 % des vulnérabilités traitées chaque année par Microsoft (bilan de 2019) et des failles graves de Chromium (2020), deux bases de code en C et en C++, sont des erreurs de sécurité mémoire (buffer overflow, use after free), au cœur de ce cours.
Terminal \(\rightarrow\)
$ python3 -c 'import sys; print(sys.version)'
3.13.16 (main, Oct 1 2026, 15:40:24) [GCC 13.3.0]
$ file -bL /usr/bin/python3 | cut -d, -f1-2
ELF 64-bit LSB executable, x86-64
$ ldd /usr/bin/python3 | grep -o 'libc.so.6 => [^ ]*'
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
Ce que le C offre
Un contrôle précis de la mémoire (où vit chaque objet, combien de temps, qui le libère), un coût prévisible pour chaque instruction, et une portabilité très large : un compilateur C existe pour presque chaque processeur.
Lecture. python3 de la machine de mesure (il n’est pas installé dans l’image du cours) est un exécutable ELF, compilé par gcc 13.3.0 à partir des sources C de CPython ; comme tout programme C de la machine, il appelle la bibliothèque C (libc.so.6), celle qu’utilisent les programmes de ce cours.
Le C est né avec UNIX, aux Bell Labs (AT&T), puis a été normalisé par l’ANSI et l’ISO.
Lecture. Ken Thompson écrit B (1969) à partir de BCPL (Martin Richards, 1967) ; Dennis Ritchie lui ajoute des types (char, int, pointeurs, puis structures) entre 1971 et 1973 : c’est le C. En 1973, le noyau d’UNIX (4e édition) est réécrit en C, jusque-là en assembleur. Le livre de Kernighan et Ritchie (1978) sert de définition jusqu’à la première norme.
| version | texte | apports |
|---|---|---|
| K&R C | livre, 1978 | définition de fait, sans norme ; paramètres déclarés hors des parenthèses |
| C89, C90 | ANSI X3.159-1989, ISO/IEC 9899:1990 | prototypes de fonctions, const, volatile, void *, bibliothèque standard normalisée |
| C99 | ISO/IEC 9899:1999 | long long, _Bool, commentaires //, déclarations parmi les instructions et dans le for, VLA, inline, <stdint.h>, initialiseurs désignés |
| C11 | ISO/IEC 9899:2011 | _Static_assert, _Generic, _Alignas ; threads (<threads.h>) et atomiques (<stdatomic.h>), optionnels ; VLA devenus optionnels |
| C17 | ISO/IEC 9899:2018 | corrections, aucune nouveauté : la norme du cours (-std=c17) |
| C23 | ISO/IEC 9899:2024 | typeof, nullptr, bool et true mots-clés, attributs ([[nodiscard]]), _BitInt(N), constantes 0b1010, #embed |
Lecture. Une norme porte le nom de l’année de sa rédaction : C17 (dite aussi C18) est publiée en 2018, C23 en octobre 2024. Les compilateurs suivent avec retard : gcc 13 connaît une partie de C23 sous le nom provisoire c2x ; gcc 15 compile par défaut en gnu23.
| option | sortie du programme |
|---|---|
| (aucune) | 201710L |
-std=c99 |
199901L |
-std=c11 |
201112L |
-std=c17 |
201710L |
-std=c2x |
202000L |
Compilateur, avec -std=c17 (extrait) \(\rightarrow\)
s3_c23.c:5:16: error: expected ';' before 'x'
s3_c23.c:6:5: error: unknown type name 'bool'
s3_c23.c:6:15: error: 'true' undeclared (first use in this function)
s3_c23.c:7:14: error: 'nullptr' undeclared (first use in this function); did you mean 'nullptr_t'?
Lecture. Par défaut : gnu17. typeof (vu comme une fonction non déclarée), bool, true, nullptr : inconnus en C17, mots-clés en c2x, où le programme affiche 2.5 1 1. C23 : 202311L (gcc 14).
L’« esprit du C » (Rationale de la norme C)
Faire confiance au programmeur ; ne pas l’empêcher de faire ce qui doit être fait ; garder un langage petit ; une seule façon de faire une opération ; être rapide, même sans garantie de portabilité.
Conséquences
Aucune vérification d’indice ni de pointeur à l’exécution ; des instructions proches de celles du processeur ; une bibliothèque standard réduite (entrées-sorties, chaînes, mémoire, mathématiques, temps) ; et des comportements indéfinis : la norme n’impose rien à un programme qui sort d’un tableau ou déborde un entier signé.
Influence
La syntaxe du C (accolades, opérateurs, if, for, while) est reprise par C++ et Objective-C, construits sur lui, puis par Java, C#, JavaScript, PHP et Go.
Énoncé
Pour chaque fragment, donner la première norme qui l’accepte (C89, C99, C11, C23).
Correction
long long et le suffixe LL.int carre();).typeof n’est qu’une extension GNU, refusée sous -std=c17.Vérification : gcc -std=c89 -pedantic-errors -c (puis c99, c11, c17, c2x) sur chaque fragment.
Chaque séance comporte un CM, un TD et un TP (la Séance 0 : un CM et le TP-1) ; chacune repart des outils de la précédente.
| séance | titre | ce que la séance construit |
|---|---|---|
| S0 | Introduction au cours | cadre, environnement Docker, chaîne de compilation, outils (TP-1) |
| S1 | Syntaxe C complète (fondamentaux) | types, variables, opérateurs, contrôle, fonctions, tableaux, entrées-sorties, compilation séparée, comportements indéfinis |
| S2 | Représentation numérique | binaire, hexadécimal, entiers non signés et complément à deux, conversions et débordements, opérations bit à bit, flottants IEEE 754 |
| S3 | Organisation de la mémoire | adresses, endianness, pointeurs, tableaux, chaînes, pile et tas, show_bytes |
| S4 | Structures et unions | champs, alignement, padding, unions, champs de bits, structures sur le tas |
| S5 | Allocation dynamique et structures chaînées | propriété d’un bloc, tableau dynamique, listes, piles, files, erreurs du tas, intérieur de malloc |
| S6 | Pointeurs de fonctions et généricité | adresse d’une fonction, tables de dispatch, rappels, qsort, conteneurs génériques |
Lecture. Les octets (S2) se lisent aux adresses (S3) ; les adresses placent les champs (S4) et les blocs du tas (S5), que des fonctions génériques manipulent (S6).
| partie du plan | où elle se trouve |
|---|---|
| I. Fondamentaux du C | S1 (et S0 pour la compilation) |
| II. Représentation | S2 |
| III. Mémoire, pointeurs, tableaux | S3 |
| IV. Structures et allocation | S4, S5 |
| V. Exécution et système | segments du processus et stack frame : S3 ; conventions d’appel, hiérarchie de cache, fichiers POSIX : hors des sept séances |
| VI. Outils et techniques | édition de liens, préprocesseur, make : S0, S1 et leurs TP ; pointeurs de fonctions : S6 ; optimisation : hors des sept séances |
| chapitres optionnels | multithreading, sockets réseau : hors des sept séances |
Lecture. Le plan initial comptait quatorze chapitres (Ch0 à Ch13), en six parties ; le cours en couvre l’essentiel en sept séances, S0 à S6.
Compétences visées
Prérequis
Variables et types de base (int, float, char) ; if, for, while ; fonctions et passage de paramètres ; premiers pointeurs (déclaration, *p) ; compiler avec gcc. Les bases sont supposées acquises : la Séance 1 les reprend en détail, sur la machine.
Tous les TP se font dans un conteneur Docker : le même système et les mêmes outils sur chaque machine.
Lecture. Windows, macOS et Linux exécutent la même image : Ubuntu 24.04 et les outils du cours. Le dossier de travail de l’hôte est monté sous /workspace : un fichier écrit d’un côté est lu de l’autre.
| outils | rôle |
|---|---|
| gcc 13.3.0, g++ | compiler (options du cours) |
| AddressSanitizer | erreurs mémoire (-fsanitize=address) |
| gdb 15.1, gdb-dashboard | exécuter pas à pas, lire les variables |
| valgrind 3.22.0 | erreurs mémoire, fuites |
| make 4.3, cmake 3.28 | automatiser la compilation |
| objdump, nm, hexdump, file | lire un binaire, un fichier |
| strace, ltrace | suivre les appels système, de bibliothèque |
| vim, nano, git | éditer, suivre les versions |
Image
elbachirtoure/ c-advanced-course:v2.0, sur ubuntu:24.04, environ 1,1 Gio installée. Versions relevées dans une image reconstruite à partir du Dockerfile du cours ; help-tp liste les commandes.
Mac à puce Apple
L’image v2.0 est publiée pour amd64 et arm64 : sur un Mac à puce Apple, le conteneur tourne en arm64, et code machine et adresses diffèrent de ceux du cours.
Terminal \(\rightarrow\)
-it |
terminal interactif |
|---|---|
--rm |
conteneur supprimé à la sortie (exit) |
--security-opt |
autorise setarch -R et gdb à désactiver l’aléa des adresses |
-v |
dossier courant de l’hôte monté sous /workspace |
PowerShell (Windows) : -v "${PWD}:/workspace".
Sortie d’erreur sans –security-opt \(\rightarrow\)
$ setarch -R ./adresse
setarch: failed to set personality to (null): Operation not permitted
$ gdb -q -nx -batch -ex run ./adresse
warning: Error disabling address space randomization: Operation not permitted
Résultat avec –security-opt \(\rightarrow\)
$ setarch -R ./adresse
x = 2026, adresse de x : 0x7fffffffec04
$ setarch -R ./adresse
x = 2026, adresse de x : 0x7fffffffec04
Lecture. Le profil de sécurité par défaut de Docker interdit l’appel système qui désactive l’aléa des adresses (personality) : setarch -R échoue, gdb avertit. Avec l’option, une variable locale garde la même adresse d’une exécution à l’autre ; les séances suivantes s’en servent.
Pour chaque séance, trois temps et des documents distincts.
CM
Les notions, projetées pas à pas : code, état de la mémoire, sortie du programme exécuté. Un CM de lecture, rédigé, reste sur le site du cours (ebtoure.sn).
TD
Exercices sur papier : lire un dump hexadécimal, suivre des pointeurs, calculer des adresses, dire ce qu’affiche un programme.
TP
Sur machine, dans le conteneur : compiler, observer, provoquer et corriger des erreurs sous gdb, valgrind et AddressSanitizer.
Lecture. La Séance 0 n’a pas de TD : son TP, le TP-1, installe et vérifie l’environnement (Docker, image, éditeur, gcc, make, gdb, valgrind, AddressSanitizer).
Une commande gcc bonjour.c -o bonjour enchaîne quatre transformations du fichier source.
| phase | programme | fichiers | arrêt |
|---|---|---|---|
| préprocesseur | cc1 |
.c \(\rightarrow\) .i |
-E |
| compilation | cc1 |
.i \(\rightarrow\) .s |
-S |
| assemblage | as |
.s \(\rightarrow\) .o |
-c |
| édition de liens | collect2, ld |
.o \(\rightarrow\) exécutable |
— |
Terminal \(\rightarrow\)
$ gcc -### -std=c17 bonjour.c -o bonjour 2>&1 | grep '^ ' | awk '{ if ($1 == "as") print; else print $1 }'
/usr/libexec/gcc/x86_64-linux-gnu/13/cc1
as --64 -o /tmp/cc7H9w5w.o /tmp/ccFwmVXE.s
/usr/libexec/gcc/x86_64-linux-gnu/13/collect2
Lecture. gcc est un pilote : gcc -### affiche les programmes qu’il lancerait, sans les lancer. cc1 réunit préprocesseur et compilateur ; as assemble ; collect2 appelle l’éditeur de liens ld. Sans -E, -S ni -c, le .s et le .o sont créés dans /tmp, puis effacés.
Dossier après chaque commande ; nature : premier champ de file ; rouge : fichier produit, orange : fichier lu.
Sortie \(\rightarrow\)
Bonjour, Dakar !
Énoncé
Programmes complets, compilés par gcc -std=c17 -Wall -Wextra : quelle étape s’arrête, sur quel message ?
Correction
afficher_total'~», puiscollect2: error: ld returned 1 exit status`.SIGFPE, statut 136 \(= 128 + 8\).Terminal \(\rightarrow\)
$ ./p4; echo "statut $?"
Floating point exception
statut 136
Un premier programme, compilé avec les options du cours, suivi ligne après ligne.
Une rangée par ligne exécutée (les huit dernières) ; valeurs relevées sous gdb ; ·· : pas encore de valeur ; — : variable hors de portée.
Sortie \(\rightarrow\)
1 ticket(s) : 250 FCFA2 ticket(s) : 500 FCFA
3 ticket(s) : 750 FCFAfin
Lecture. prix \(=250\) (FCFA, un ticket de bus) ; total n’a pas encore de valeur, n n’existe pas encore.
Lecture. total \(=0\).
Lecture. Entrée dans la boucle : n = 1, puis le test n <= 3, vrai.
Lecture. total + prix est calculé, puis rangé dans total : 250.
Lecture. printf remplace %d par n puis par total : une ligne de sortie.
Lecture. Fin du tour : n++ porte n à 2 ; le test est vrai, un tour de plus.
Lecture. total + prix est calculé, puis rangé dans total : 500.
Lecture. printf remplace %d par n puis par total : une ligne de sortie.
Lecture. Fin du tour : n++ porte n à 3 ; le test est vrai, un tour de plus.
Lecture. total + prix est calculé, puis rangé dans total : 750.
Lecture. printf remplace %d par n puis par total : une ligne de sortie.
Lecture. n++ porte n à 4 ; le test 4 \(\le\) 3 est faux : sortie de la boucle, n cesse d’exister.
Lecture. Après la boucle, total garde sa valeur ; printf affiche la dernière ligne.
Terminal \(\rightarrow\)
$ gcc s6_avert.c -o s6_avert
$ gcc -std=c17 -Wall -Wextra s6_avert.c -o s6_avert
s6_avert.c: In function 'carre':
s6_avert.c:5:9: warning: unused variable 'inutile' [-Wunused-variable]
5 | int inutile;
| ^~~~~~~
s6_avert.c:8:1: warning: control reaches end of non-void function [-Wreturn-type]
8 | }
| ^
Lecture. Sans option, gcc ne dit rien. Avec -Wall -Wextra : une variable inutilisée, et une fonction qui peut finir sans return (si x <= 0, valeur rendue indéterminée). Options du cours : -std=c17 (norme), -Wall -Wextra, -O0 (sans optimisation), -g (pour gdb).
| commande | effet |
|---|---|
break 8 |
arrêt avant la ligne 8 |
run |
lance le programme |
print n |
valeur de n |
next |
exécute une ligne |
continue |
jusqu’au prochain arrêt |
info locals |
variables locales |
backtrace |
pile des appels |
v_tickets.c, compilé avec -g ; dix commandes, dans cet ordre : break 8, run, print n, print total, next, print total, continue, print n, info locals, backtrace.
Dans le conteneur, gdb-dashboard s’affiche à chaque arrêt : dashboard -enabled off le masque.
Terminal \(\rightarrow\)
Breakpoint 1 at 0x118c: file v_tickets.c, line 8.
Breakpoint 1, main () at v_tickets.c:8
8 total = total + prix;
$1 = 1
$2 = 0
9 printf("%d ticket(s) : %d FCFA\n", n, total);
$3 = 250
1 ticket(s) : 250 FCFA
Breakpoint 1, main () at v_tickets.c:8
8 total = total + prix;
$4 = 2
n = 2
prix = 250
total = 250
#0 main () at v_tickets.c:8
Lecture. Arrêt avant la ligne 8, à chaque tour ; au 2e arrêt, n vaut 2 et total 250 : la ligne 8 n’est pas encore exécutée.
Résultat \(\rightarrow\)
somme = 46
Sortie d’erreur sous valgrind (extrait) \(\rightarrow\)
Invalid read of size 4
at main (s7_hors.c:11)
Address 0x4a7e050 is 0 bytes after a block of size 16 alloc'd
ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Sortie d’erreur avec -fsanitize=address (extrait) \(\rightarrow\)
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000020
READ of size 4 at 0x502000000020 thread T0
#0 main s7_hors.c:11
SUMMARY: AddressSanitizer: heap-buffer-overflow s7_hors.c:11 in main
Lecture. La seconde boucle lit notes[4], après le bloc de 16 octets : comportement indéfini. Sans outil, 46, qui semble juste. valgrind (sans recompiler) signale la lecture ; AddressSanitizer (-fsanitize=address) arrête le programme ligne 11.
Makefile
CC = gcc
CFLAGS = -std=c17 -Wall -Wextra -O0 -g
all: bonjour
bonjour: bonjour.c
$(CC) $(CFLAGS) -o bonjour bonjour.c
clean:
rm -f bonjour
.PHONY: all clean
Terminal \(\rightarrow\)
$ make
gcc -std=c17 -Wall -Wextra -O0 -g -o bonjour bonjour.c
$ make
make: Nothing to be done for 'all'.
$ touch bonjour.c
$ make
gcc -std=c17 -Wall -Wextra -O0 -g -o bonjour bonjour.c
$ make clean
rm -f bonjour
Lecture. Une règle dit cible: dépendances, puis la commande, précédée d’une tabulation (\(\rightarrow\)). make compare les dates : bonjour plus récent que bonjour.c, rien à refaire ; après touch, la source est plus récente, la commande est relancée. .PHONY : all et clean ne sont pas des fichiers.
Énoncé
jour et stock à la fin de chaque tour.stock part de 10.Correction
| tour | jour |
stock |
stock > 0 |
|---|---|---|---|
| 1 | 1 | 7 | vrai |
| 2 | 2 | 2 | vrai |
| 3 | 3 | \(-3\) | faux |
stock \(=-3\), le test est faux.0 > 0 est faux après le 2e tour, « rupture : jour 2 ».Résultat \(\rightarrow\)
jour 1 : 7
jour 2 : 2
jour 3 : -3
rupture : jour 3
Énoncé
total au 3e tour d’une boucle.return.printf est défini dans un .o ou attendu d’ailleurs.Correction
-g, puis break, run, continue, print total.gcc -Wall : avertissement -Wreturn-type, « control reaches end of non-void function ».valgrind ./prog (sans recompiler), ou -fsanitize=address (à la compilation).make, qui compare les dates des cibles et de leurs dépendances.nm fichier.o : U printf, utilisé mais non défini.setarch -R ./prog, dans un conteneur lancé avec --security-opt seccomp=unconfined.À retenir
-std=c17, __STDC_VERSION__ \(=\) 201710L).gcc enchaîne préprocesseur (.i), compilateur (.s), assembleur (.o) et éditeur de liens (exécutable) ; chaque phase a ses erreurs, et certaines erreurs n’apparaissent qu’à l’exécution.-std=c17 -Wall -Wextra -O0 -g ; un avertissement se lit et se corrige.-v "$(pwd)":/workspace et --security-opt seccomp=unconfined.Séance 1 : syntaxe C complète (fondamentaux)
La syntaxe du C, instruction par instruction, avec ce que chaque ligne fait des variables : types et tailles sur la machine du cours, conversions implicites, opérateurs et priorités, contrôle, fonctions et passage par valeur, tableaux et chaînes, printf et scanf ; puis les phases de la compilation, reprises avec la compilation séparée et l’édition de liens, et les comportements que la norme ne définit pas.
À préparer
Le TP-1 : Docker installé, conteneur du cours lancé sur le dossier de travail, verifie_env.sh sans échec, un premier programme compilé avec les options du cours, une session gdb, un rapport de valgrind lu.
__STDC_VERSION__) ; ISO/IEC 9899:2024 (C23) ; Rationale for International Standard — C, rév. 5.10, 2003.Mesures de la séance : x86-64, Linux 6.18, gcc 13.3.0 (-std=c17 -Wall -Wextra -O0 -g), glibc 2.39, gdb 15.1, valgrind 3.22.0, make 4.3 ; environnement : image reconstruite à partir du Dockerfile du cours.
Programmation C avancée — Séance 0 Dr. El Hadji Bassirou TOURÉ • DMI/FST/UCAD
Lecture. Le préprocesseur remplace
#include <stdio.h>par le texte de l’en-tête (déclaration deprintfcomprise) etVILLEpar"Dakar": 549 lignes, plus aucune directive.Lecture. Le compilateur traduit le C en assembleur x86-64 : les chaînes deviennent des
.string, l’appel devientcall printf@PLT.Lecture. L’assembleur code les instructions en binaire : fichier objet ELF de 1544 octets ;
mainy est défini (T),printfutilisé mais absent (U).Lecture. L’éditeur de liens ajoute le code de démarrage et relie
printfà la glibc partagée (printf@GLIBC_2.2.5) : exécutable PIE (chargeable à toute adresse) de 15968 octets.Lecture. Au lancement, le chargeur dynamique charge
libc.so.6et y trouveprintf; le code de démarrage appellemain: une ligne de sortie.