0% ont trouvé ce document utile (0 vote)
4 vues46 pages

Introduction à la programmation en C

Le document traite de la programmation en C et des systèmes d'exploitation, en abordant des concepts fondamentaux tels que les ordinateurs, la mémoire, et les pointeurs. Il explore également les pratiques de programmation en C, y compris la gestion de la mémoire dynamique et statique. Enfin, il met l'accent sur l'importance d'un code lisible et propre.

Transféré par

pierrotfidel16
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
4 vues46 pages

Introduction à la programmation en C

Le document traite de la programmation en C et des systèmes d'exploitation, en abordant des concepts fondamentaux tels que les ordinateurs, la mémoire, et les pointeurs. Il explore également les pratiques de programmation en C, y compris la gestion de la mémoire dynamique et statique. Enfin, il met l'accent sur l'importance d'un code lisible et propre.

Transféré par

pierrotfidel16
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Programmation en C

Martin Quinson

22 octobre 2025
1 Des machines et des OS 5
I) Présentation du module SYS1 . . . . . . . . . . . . . . . . . . . . . . . . . . 5
I.1) Objectifs : introduction à la recherche en informatique appliquée . . 6
I.2) Diérence entre théorie et pratique en informatique . . . . . . . . . . 6
I.2.a) Les ordinateurs ne manipulent pas de nombres mathéma-
tiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
I.2.b) La mémoire d'un ordinateur n'est pas uniforme (RAM) . . 7
I.2.c) Les ordinateurs ne font pas que calculer . . . . . . . . . . . 7
II) Ordinateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
II.1) Qu'est ce qu'un ordinateur ? . . . . . . . . . . . . . . . . . . . . . . . 8
II.2) Contenu d'un ordinateur . . . . . . . . . . . . . . . . . . . . . . . . . 8
II.2.a) Le processeur . . . . . . . . . . . . . . . . . . . . . . . . . . 8
II.2.b) La mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
II.2.c) Les mémoires caches . . . . . . . . . . . . . . . . . . . . . . 10
II.3) Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
III) Système d'exploitation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
III.1) Qu'est ce qu'un système d'exploitation . . . . . . . . . . . . . . . . . 11
III.2) Fonctions de l'OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
IV) Résumé de la séance "Des machines et des OS" . . . . . . . . . . . . . . . . 12

2 OS et C en pratique 13
I) Systèmes d'exploitation (suite) . . . . . . . . . . . . . . . . . . . . . . . . . 14
I.1) Design d'UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
I.1.a) Tout est un chier (ou y ressemble, au moins) . . . . . . . 14
I.1.b) Assembler les outils simples interchangeables . . . . . . . . 15
I.1.c) Préférer les textes en clair (vs. binaire) . . . . . . . . . . . 15
I.1.d) Repères historiques pour UNIX et C (pas vu en cours) . 15
I.2) Interfaces de l'OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
I.3) Sécurité des systèmes d'exploitation . . . . . . . . . . . . . . . . . . 17
II) Le langage C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
II.1) Introduction au langage C . . . . . . . . . . . . . . . . . . . . . . . . 19
II.1.a) Historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
II.1.b) Syntaxe de base du langage C . . . . . . . . . . . . . . . . 20
II.1.c) Écrire du code C . . . . . . . . . . . . . . . . . . . . . . . . 20
II.2) Pratique du langage C . . . . . . . . . . . . . . . . . . . . . . . . . . 20
III) Résumé de la séance "OS et C en pratique" . . . . . . . . . . . . . . . . . . 21

3 Mémoire statique 22
I) Identiants C, portée et durée de vie . . . . . . . . . . . . . . . . . . . . . . 22
I.1) Portée des identiants locaux et globaux . . . . . . . . . . . . . . . . 22
I.2) Paramètres et pile d'appel . . . . . . . . . . . . . . . . . . . . . . . . 23

2
3

I.3) Mot-clé static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24


II) Organisation logique de la mémoire . . . . . . . . . . . . . . . . . . . . . . . 24
II.1) Memory layout d'un processus dans l'OS . . . . . . . . . . . . . . . . 24
II.2) Espace d'adressage virtuel . . . . . . . . . . . . . . . . . . . . . . . . 25
III) Pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
III.1) Utiliser les pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
III.2) Pièges classiques avec les pointeurs . . . . . . . . . . . . . . . . . . . 27
III.3) Le pointeur NULL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
IV) Résumé de la séance "Mémoire statique" . . . . . . . . . . . . . . . . . . . . 28

4 Mémoire dynamique 29
I) Pointeurs (suite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
I.1) Arithmétique des pointeurs . . . . . . . . . . . . . . . . . . . . . . . 30
I.2) Transtypage (cast en anglais) . . . . . . . . . . . . . . . . . . . . . . 30
I.3) Pointeurs génériques : void* . . . . . . . . . . . . . . . . . . . . . . 31
II) Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
II.1) Similarités et diérences avec les pointeurs . . . . . . . . . . . . . . . 31
II.2) Les chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . 32
III) Mémoire dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
III.1) Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
III.2) L'interface malloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
III.3) Survivre à malloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
III.4) Les alternatives à malloc (hors programme) . . . . . . . . . . . . . . 35
IV) Résumé de la séance "Mémoire dynamique" . . . . . . . . . . . . . . . . . . 35

5 Maîtriser la programmation C 36
I) Pointeurs avancés : pointeurs sur fonction . . . . . . . . . . . . . . . . . . . 37
II) Constructions C pour organiser ses données . . . . . . . . . . . . . . . . . . 37
II.1) Déclarer des structures . . . . . . . . . . . . . . . . . . . . . . . . . . 37
II.2) Nommer ses types avec typedef . . . . . . . . . . . . . . . . . . . . 38
II.3) Ranger ses valeurs avec enum . . . . . . . . . . . . . . . . . . . . . . 38
III) Constructions C de bas niveau . . . . . . . . . . . . . . . . . . . . . . . . . 38
III.1) Déclarer des unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
III.2) Champs de bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
IV) Modèles d'organisation d'un code C . . . . . . . . . . . . . . . . . . . . . . . 40
IV.1) Procédural . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
IV.2) Orienté objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
IV.3) Le module point en détail . . . . . . . . . . . . . . . . . . . . . . . . 40
V) Code lisible et propre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
V.1) Code lisible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
V.1.a) Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . 41
V.1.b) Identicateurs . . . . . . . . . . . . . . . . . . . . . . . . . 42
V.1.c) Mise en page . . . . . . . . . . . . . . . . . . . . . . . . . . 42
V.2) Code propre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
V.3) Pièges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4

V.4) Tests (reporté au semestre suivant) . . . . . . . . . . . . . . . . . . . 44


VI) Compilation séparée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
VII) Conclusion sur le module . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Séance 1

Des machines et des OS


Notes d'animation
 Bilan du TP0 sur l'apprentissage du shell
 TODO : Intégrer ce qui doit l'être (piles ?) depuis [Link]
fr/capes/portail/-/blob/master/ue1/archi/[Link]

Programme du jour
I) Présentation du module SYS1 . . . . . . . . . . . . . . . . . . . . 5
I.1) Objectifs : introduction à la recherche en informatique appliquée 6
I.2) Diérence entre théorie et pratique en informatique . . . . . . . 6
I.2.a) Les ordinateurs ne manipulent pas de nombres mathé-
matiques . . . . . . . . . . . . . . . . . . . . . . . . . . 6
I.2.b) La mémoire d'un ordinateur n'est pas uniforme (RAM) 7
I.2.c) Les ordinateurs ne font pas que calculer . . . . . . . . . 7
II) Ordinateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
II.1) Qu'est ce qu'un ordinateur ? . . . . . . . . . . . . . . . . . . . . . 8
II.2) Contenu d'un ordinateur . . . . . . . . . . . . . . . . . . . . . . . 8
II.2.a) Le processeur . . . . . . . . . . . . . . . . . . . . . . . . 8
II.2.b) La mémoire . . . . . . . . . . . . . . . . . . . . . . . . . 9
II.2.c) Les mémoires caches . . . . . . . . . . . . . . . . . . . . 10
II.3) Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
III) Système d'exploitation . . . . . . . . . . . . . . . . . . . . . . . . 11
III.1) Qu'est ce qu'un système d'exploitation . . . . . . . . . . . . . . . 11
III.2) Fonctions de l'OS . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
IV) Résumé de la séance "Des machines et des OS" . . . . . . . . . 12

I) Présentation du module SYS1


 Ce cours vise à constituer une première introduction sur les ordinateurs et les systèmes
informatiques.
 Il couvre principalement le langage C et le réseau.

5
6 SÉANCE 1. DES MACHINES ET DES OS

 5 semaines : Le langage C est une excuse pour regarder comment fonctionne un


ordinateur, sans aller dans les détails électroniques.
 7 semaines : Les réseaux sont un exemple de grand système informatique qui dure
depuis 4 décennies car il était bien pensé à la base.
 Prérequis : prépa MPI, donc base du C. Au besoin, il y a un module de remise à niveau
mercredi matin.
 "Programmer en Langage C : Cours et exercices (édition noire)" par Claude Delan-
noy est un bon livre pour débutants, en complément
 MCC : un projet en binôme en semaine 4 à 6, puis un examen sur table à la n. Feuille
de pompe autorisée à l'exam

I.1) Objectifs : introduction à la recherche en informatique appliquée


 Pas de panique, ce n'est pas un cours d'ingénierie (même si c'est sympa). On veut
donner une culture générale et de combattre quelques idées fausses.
 Objectif1 est de montrer le large spectre de la recherche en informatique (luter contre
la spécialisation)
 Objectif2 : mieux comprendre les vrais systèmes informatiques pour que les problem-
solvers trouvent des problèmes sympas à résoudre. Eviter les ares de la page blanche
 Et y'a même de la recherche en génie logiciel, en particulier à Rennes (Diverse).

I.2) Diérence entre théorie et pratique en informatique


 La diérence, c'est qu'en théorie, il n'y a pas de diérence
 La théorie, c'est quand personne n'y croit sauf l'auteur. La pratique c'est quand tout
le monde y croit, sauf l'expérimentateur.

I.2.a) Les ordinateurs ne manipulent pas de nombres mathématiques


(inspiré du cours Intro to computer systems de CMU)
 Int ̸= entiers : ∃ n ∈ Int tel que x2 < 0 : 50 000 × 50 000 < 0 (pas de pb avec les
ottants)
 Float ̸= réels : (x+y)+z ̸= x+(y+z) parfois (pas pb avec les entiers)
 (1e20 + -1e20) + 3.14 = 3.14 mais 1e20 + (-1e20 + 3.14) = 0
 Int n'est pas N mais c'est un anneau (commutativité, associativité, distributivité)
 Double n'est pas R mais respecte relation d'ordre (monotonie, signe)
 La représentation en mémoire des doubles pose un problème de discrétisation. Tous
les nombres ne sont pas représentables, forcément vu que la représentation n'est pas
innie. Ce qui est étonnant, c'est que certains nombres simples à écrire en base 10 ne
peuvent pas être représentés en base 2. Par exemple : (0.1)10 = (0, 00011)2 le motif
0011 se répète inniment, ce qui fait que la valeur (0, 1)10 ne peut être qu'approximée
[Link]
 Soit dit au passage, y'a pleins d'autres nombres dans Q qui eux ne sont pas dans D,
comme par exemple 13
I). PRÉSENTATION DU MODULE SYS1 7

 Tout ceci justie une recherche active sur l'arithmétique des ordinateurs, pour limiter
au mieux les erreurs d'arrondi et d'overow. Cf par exemple l'équipe Pascaline de l'ENS
Lyon ou les travaux de Sylvie Boldo, ex-présidente du jury de l'agreg d'informatique.

I.2.b) La mémoire d'un ordinateur n'est pas uniforme (RAM)


 Pas sans limite : taille max mais aussi précision max
 Source de bugs pénibles (malloc)
 La vitesse dépend de l'endroit où sont stockées les données
 ∀ i ∈ [0 ; 2028] ∀ j ∈ [0 ; 2028]
∀ j ∈ [0 ; 2028] ∀ i ∈ [0 ; 2028]
dist[i][j] = src[i][j] dist[i][j] = src[i][j]
 La seule diérence est l'ordre des boucles : on parcourt les i avant les j ou le contraire
 L'un met 4ms et l'autre 80ms. On verra plus tard pourquoi :)
 Ce fait constitue le principal goulot d'étranglement des performances de calcul d'un
ordinateur

I.2.c) Les ordinateurs ne font pas que calculer


On trouve pleins d'autres problèmes de recherche intéressants qui ne sont pas directe-
ment liés au calcul :
 Performance dissymétrique des I/O ⇒ algo out of core (moins actif actuellement)
 Compatibilité
 Internet (TCP/IP) inventé pour ≈ 100 noeuds et fonctionne pour 4+ milliards de
IPv4
 Démarrage (BIOS, MBR, OS) fonctionne depuis les années 80 pour des matériels
très disparates
 Recherche : Génie log, équipe Diverse à Rennes
 Concurrence : quand on coordonne diérentes entités, il se pose une foule de problèmes
intéressants. Recherche : preuve de programme (ariane, voiture), modèle-checking, algo
très amusante (wait-free)
 Usage able d'un média non able : les réseaux fonctionnent avec des cables de mauvaise
qualité, et le logiciel améliore/compense cela. Idem pour les appareils photo numérique,
dont la qualité vient surtout des programmes impliqués.
Recherche : OS (domaine moins actif en OS, pour l'instant), réseau (beaucoup), traite-
ment d'image (énormément)
 Faire avec ce qu'on a : C'est pour ça que les ordis sont binaires (le premier ordinateur
russe, le setun, était ternaire, ce qui est préférable d'un point de vue théorique puisqu'il
y a une plus grande compacité entre le nombre de positions utilisées et la quantité
d'information encodable).
8 SÉANCE 1. DES MACHINES ET DES OS

II) Ordinateur
II.1) Qu'est ce qu'un ordinateur ?
 Machine à calculer universelle.
 La diérence avec un calculateur est qu'il n'y a pas à le modier physiquement pour
lui faire calculer autre chose
 En français, "ordinateur" est un vieux mot tombé en désuetude pour désigner Dieu,
celui qui met de l'ordre dans le monde.
 Depuis quand ?
 1936 : Article fondateur de Turing sur la calculabilité et la Machine de Turing
 1946 : Construction de l'ENIAC.
 Environ 350 ops, ie 2500 fois plus rapide qu'un humain
 Changer le programme prend jusqu'à 3 semaines pour tout recabler (c'est donc
plutôt un calculateur programmable)
 Pour info, les supercalculateurs modernes sont exaopiques, 1018 ops. 1 milliard
de milliard de milliard. Âge de l'univers en seconde : ≈ 4,3·1017
 1962 : Premier département d'informatique à l'université de Purdue, près de Chicago.
Fork du département de génie civil
 1886 : fondation de Tabulating Machine Company, qui fait des cartes perforées et
deviendra IBM par la suite.

II.2) Contenu d'un ordinateur


 Prise de représentation sur les diérentes parties d'un ordinateur. On attend CPU,
GPU, disque, écran, clavier, bus, . . . poussière.
 Modèle de Von Neumann (1945) susament simple pour être indémodable, mais trop
simple pour être utile (à part la distinction entre le contrôleur et l'ALU qui est non
triviale et représentative des archis modernes)

[Link]

II.2.a) Le processeur
 Composants
 Unités fonctionnelles, qui font les calculs.
II). ORDINATEUR 9

 ALU : Arithmetic/Logical Unit : logique booléenne et travail sur les entiers


 FPU : Floating Point Unit : calculs sur les nombres à virgule ottante
 Contrôleur, qui coordonne les calculs
 Zones de stockage, certaines pour les instructions, d'autres pour les données, ou
encore mixtes
 Principe général (à 3 Ghz)
 Fetch : Le contrôleur récupère l'instruction suivante du pro- L1 instructions
gramme

Cache L2

Extérieur
Contrôleur
 Decode : Le contrôleur décode l'instruction, sépare les opé- ALU
Registres
randes FPU

 Execute : L'ALU ou FPU exécute l'instruction L1 data

 Le résultat est stocké dans un registre


 Loi de Moore (ex-président d'Intel)
 Le nombre de transistors double tous les 18 mois.
 C'est une prédiction autoréalisante puisque Moore est un ex-PDG d'Intel et que
l'entreprise avait comme business plan que tous les ordinateurs sont remplacés tous
les 18 mois. 5 ans de nos jours, mais pas plus.
 1971 : 2000 transistors sur un 4004 ; 2010 : 2.6 milliards sur xéon ; 2020 : 40 milliards
AMD epyc
 Types de processeurs
 GPU : unité de calcul spécialisée dans le calcul vectoriel sur nombres ottants.
Parallélisme de masse. Contient des ALU mais vraiment pas ecace pour les calculs
génériques. Comparable à une formule 1 : très rapide si le calcul est très régulier,
nul sinon.
 RISC vs. CISC : Reduced/Complex Instruction Set CPU. Plus ou moins d'instruc-
tions (de mots au vocabulaire). Plus de mots : plus de séquences directement inscrites
dans le silicium, donc plus rapide et plus gros/complexe
 FPGA : aucune instruction inscrite dans le silicium, tout est interprété
 Little-Big Endian : ordre des bits pour écrire les nombres entiers. Les ingés Intel ont
eu des goûts bizarres, mais soit. Le nom vient des voyages de Gulliver. AMD/x86
est little, ARM est (souvent) big.

II.2.b) La mémoire
 Pyramide d'accès : On peut représenter les types de mémoire par un triangle avec les
ordres de grandeurs suivants
 Registre : <512 octets pour <1ns
 Caches : ko-Mo pour 5ns
 RAM : Go pour 10-30 ns
 Stockage de masse : To-Po pour 10ms
 Memory wall :
 Latences mises à l'échelle, pour les ordre de grandeur. Imaginons qu'un cycle fasse
une seconde
10 SÉANCE 1. DES MACHINES ET DES OS

1 cycle CPU 0,3ns 1s


cache L1 1ns 3s
cache L2 3ns 9s
cache L3 13ns 43s
RAM 120ns 6mn
SSD disk 50-150µs 2-6 jours
disque rotationel 1-10ms 1-12 mois
Internet Oslo/Madrid ou SF/NY 40ms 4 ans
Internet transcontinental 180ms 18 ans
TCP retransmit 1-3s 100-300 ans
 Le CPU va très, très très vite : 0,3ns par cycle, ça fait 10 cycles quand la lumière
parcourt 1m. Le dé est donc de nourrir le CPU de données sans pause
 C'est aussi pour ça que les registres ont une petite capacité : ils doivent être petits
en taille pour être proche des unités fonctionnelles. S'ils étaient plus gros, la lumière
mettrait plus de temps
 Le memory wall s'aggrave : Vitesse CPU croît de 55%/an et celle mémoire de
10%/an.

II.2.c) Les mémoires caches


 L'étymologie vient des trappeurs nord-américains. Ils apportaient du matériel sur un
chariot jusqu'à une sorte de camp 0, puis ils cachaient ce qu'ils ne pouvaient pas porter
pour l'expédition.
 Dans l'ordi, on a un pb de latence entre la mémoire et l'ALU, mais pas de bande
passante. On va donc rapatrier plus de données que demandées, au cas où on aurait
ensuite besoin de la donnée juste après en mémoire.
 une mémoire cache est une petite réserve de mémoire proche du coeur du CPU
RAM x
très grand tableau lecture
copie (mise en cache de 5 valeurs)

Cache L2 x
plus petit
copie (mise en cache de 2 valeurs)

Cache L1 x
très petit
copie (pas de cache)

Registre x
 La fois suivante qu'on accède aux données, on regarde d'abord si elle n'est pas déja
stockée dans un niveau de cache intermédiaire. Si oui, on a économisé la latence. Si
non, on charge la ligne de cache nécessaire, en virant une autre ligne au besoin. Plein
d'algos diérents de gestion des caches, mais LRU (least recently used eviction) marche
bien en pratique.
III). SYSTÈME D'EXPLOITATION 11

 "Coup de chance", les accès mémoire sont eectivement souvent linéaires. En fait, on
programme exprès pour, et le compilo tente de démêler les accès et les données pour
uidier.
 En HPC, on cherche même à faire des structures de données cache oblivious, càd
optimisées pour n'importe quelle taille de cache.
 C'est ce qui explique la diérence de code entre les deux programmes de copie de
matrice (4ms vs. 80ms pour une matrice 2048) : l'un utilise très ecacement les
caches, l'autre passe son temps à les invalider après chaque accès.

II.3) Conclusion
 Vous reviendrez sur le contenu d'un ordinateur (et celui d'un processeur) au second
semestre dans le module d'archi
 On parlera performance des ordinateurs dans le module de HPC l'an prochain en M1S1
 Beaucoup de recherche sur ces diérents domaines

III) Système d'exploitation


III.1) Qu'est ce qu'un système d'exploitation
 Citez moi des exemples d'OS : Linux, Windows, Mac, Android, FreeBSD, blablabla
 Combien d'OS existent ? Autant que d'élèves qui ont fait le projet du MIT ou Stanford
 Quel est le lien entre Linux et UNIX ? Et entre MacOSX et UNIX ?
 Quel est l'OS le plus utilisé sur terre ?
 Desktop : windows sans aucun doute. Serveurs : linux. Au total : Minix qui est
embarqué dans la secure zone des processeurs Intel.
 Pourquoi étudier Linux dans cette diversité ?
 Je connais bien
 C'est ouvert
 C'est fait en C, ce qui tombe bien dans ce module, avouez
 le noyau windows va bientôt s'arrêter quand le WSL3 sera encore mieux intégré.
Comme Mac OSX mais avec un Unix moderne dessous (mais on est pas vendredi).

III.2) Fonctions de l'OS


 Historiquement, c'était juste un ensemble de fonctions mises en commun, mais mainte-
nant, l'OS a une mission bien plus large
 C'est le programme entre les programmes et la machine. Objectifs :
 Protéger le matériel des applications (sous Dos, activité malveillante de la tête
de lecture pouvait détruire le disque dur)
 Protéger les applications les unes des autres (sous Dos ou M99, un bug d'un
programme = crash généralisé)
 Adapte l'interface du matériel (simplie/uniformise)
12 SÉANCE 1. DES MACHINES ET DES OS

 les pilotes traduisent les requetes API de façon spécique au matériel


 Sous Dos, les jeux spéciaient les cartes son utilisables
 Virtualise les ressources : multiplexage CPU, swap mémoire
 Le document supplémentaire en ligne sur le site donne un rapide aperçu du fonction-
nement d'un OS, et on reviendra dans tous les détails de la chose dans le module SEA
(système d'exploitation avancé) en M1S1.

IV) Résumé de la séance "Des machines et des OS"


 Principes de fonctionnement d'un ordinateur (CPU, mémoire, caches), dés (mur mé-
moire)
 M99 comme mise en pratique
 ordinateur = grand vecteur de cases mémoire contenant des nombres + un processeur
 Pas de séparation entre le code et les données (ordi = machine à calculer universelle)
 Notion d'opcode, cycle fetch/decode/execute
 Le compilateur a beaucoup à faire pour passer du C à du langage machine
 Les 3 fonctions de base d'un OS : protéger, adapter l'interface et virtualiser.
Séance 2

OS et C en pratique
Notes d'animation
 3 doc à imprimer : Pense-bête du C, TP2 (il y a des choses à préparer), Code illisible
de l'IOCCC
 TODO : Couper le pense-bête en doc de séance 2 avec les bases et doc de séance 5
avec le module point. Ce serait l'occasion de mettre des exemples des constructions de
base (de détailler la première colonne).
 TODO : Le pense-bête du C ainsi que les détails sur la syntaxe du C de ce chapitre
sont rendus inutiles par le "rappel" de cours au début du TP bonus C starter.
 Il faudrait couper les éléments de syntaxe hors du présent texte, par exemple dans
un doc séparé, et probablement tuer la refcard vu qu'elle n'est pas très pratique
 TODO : Convertir l'exercice de tortue dans le TP pour faire du svg à la place de
postscript
 Logistique ENS :
 Il faut élire les délégués si ce n'est toujours pas fait, et il faut naliser les inscriptions
pédagogiques

Programme du jour
I) Systèmes d'exploitation (suite) . . . . . . . . . . . . . . . . . . . 14
I.1) Design d'UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
I.1.a) Tout est un chier (ou y ressemble, au moins) . . . . . 14
I.1.b) Assembler les outils simples interchangeables . . . . . . 15
I.1.c) Préférer les textes en clair (vs. binaire) . . . . . . . . . 15
I.1.d) Repères historiques pour UNIX et C (pas vu en cours) 15
I.2) Interfaces de l'OS . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
I.3) Sécurité des systèmes d'exploitation . . . . . . . . . . . . . . . . 17
II) Le langage C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
II.1) Introduction au langage C . . . . . . . . . . . . . . . . . . . . . . 19
II.1.a) Historique . . . . . . . . . . . . . . . . . . . . . . . . . 19
II.1.b) Syntaxe de base du langage C . . . . . . . . . . . . . . 20
II.1.c) Écrire du code C . . . . . . . . . . . . . . . . . . . . . . 20
II.2) Pratique du langage C . . . . . . . . . . . . . . . . . . . . . . . . 20
III) Résumé de la séance "OS et C en pratique" . . . . . . . . . . . 21

13
14 SÉANCE 2. OS ET C EN PRATIQUE

I) Systèmes d'exploitation (suite)


I.1) Design d'UNIX
 UNIX est un OS dominant depuis 1970, grâce à qques bonnes idées de design
 C'est aussi parce que C et UNIX ont été inventés ensemble, par les mêmes personnes.
Un éventuel remplaçant devrait peut-être remplacer les deux à la fois.
 Ça ne sera pas Windows/C++, ni MacOSX Objective-C
 Peut-être que ça sera Rust (qui reprend du C, en bien plus extrêmement moderne) et
Redox (qui vise à implémenter les dernières avancées en OS, au besoin en s'éloignant
un peu d'UNIX  mais pas tant que ça).
 Ou bien Hare (un C moderne hardcore sans oritures) / Helios (un OS jouet pour
l'instant, mais qui sait ?), faits par la même personne.
 Dicile à dire car inventer un design d'OS est une tâche gargantuesque.
 Mais on s'égare, revenons déjà au design d'UNIX avant de vouloir le dépasser

I.1.a) Tout est un chier (ou y ressemble, au moins)


 Utilisateur : stdin /stdout /stderr
 Dans tout le cours, on utilisera la même représentation simple ci-dessous. Les ma-
chines (ordinateurs) sont des boites, les programmes qui s'exécutent dedans sont des
ellipses, et les descripteurs de chier sont des petits ronds à la surface de l'ellipse.
0: stdin
1: stdout
App 2: stderr

descripteurs
machine de fichier
processus
 Autres programmes : socket réseau, tubes
 Le noyau : /proc et /sys
 Le matériel : /dev
 Au passage, une chose agréable sous Unix est que la place des choses sur disque est
standardisé (FHS  Filesystem Hierarchy Standard), et je ne comprend pas qu'Apple
mette autant d'eorts à ne plus le respecter.
 Notez que le réseau n'est pas parfaitement intégré vu que c'est arrivé en 1983 seulement.
 Plan 9 est un UNIX distribué, mais Unix est good enough. Plan 9 (et Inferno)
sont abandonnés maintenant. Les concepteurs ne voulaient pas que ça devienne
un produit comme UNIX. Ceci étant dit, il demeure que Plan 9 contenait de très
bonnes idées qui n'ont pas encore été reprises ailleurs. Ceux qui cherchent des idées
novatrices (ainsi que les collectionneurs bizarres) devraient se pencher sur le cas de
cet OS.
I). SYSTÈMES D'EXPLOITATION (SUITE) 15

 Plan 9 is not a product, it is an experimental investigation into a dierent way of


computing. The developers started from several basic assumptions : that CPUs
are very cheap but that we don't really know how to combine them eectively ;
that good networking is very important ; that an intelligent user interface is
a Right Decision ; that existing systems are not the correct way to do things,
and in particular that today's workstations are not the way to go. [from http:
//[Link]/site/[Link]]

I.1.b) Assembler les outils simples interchangeables


 Ça aide à l'établissement d'un écosystème aux briques évolutives
 Mécanismes dans le shell :
 Redirection des E/S avec des tubes : <, >, , 2>, |
 grep, sed, cat
 voir les pages de manuel
 C'est dommage que la même chose n'ait jamais vu le jour pour l'UI, malgré les nom-
breuses tentatives
 DOM-X windows a tourné au cauchemard de sécurité
 AppleScript avait l'air sympa mais ca n'a pas marché
 Gnome pousse l'usage du javascript (et c'est donc une mauvaise idée)

I.1.c) Préférer les textes en clair (vs. binaire)


 Ça participe à simplier l'assemblage de briques hétérogènes
 Interopérabilité et debug.
 Plus généralement, UNIX applique souvent le KISS
 C'est pour la cong (vs. système de registres de windows) et pour les logs sur disque.
 Mais c'est aussi pour les protocoles du web (http and co)
 Dans l'histoire récente, cet aspect tend à disparaitre avec l'avènement de System D qui
fait le contraire pour des raisons de performance
 Grosse crise dans Debian vers 2015, à deux doigts de la scission. Je n'adore pas ce
qui est advenu

I.1.d) Repères historiques pour UNIX et C (pas vu en cours)


 Ils ont été inventés ensemble, par les mêmes personnes
 UNIX
 1965 : ambitieux projet MULTICS aux Bell Labs
 1969 : Abandon de MULTICS, le projet UNICS commence
 1970 : début ociel du projet UNIX
 1973 : réécriture en C
 1982 : AT&T perd son procès anti-monopole. UNIX est donné aux universités, dont
Berkeley, et à diverses entreprises qui vont commercialiser leurs versions.
16 SÉANCE 2. OS ET C EN PRATIQUE

 1983 : TCP/IP arrive, bien après le design d'UNIX. Le résultat ressemble à une
verrue.
 80-90 : UNIX War. c'est BSD contre System V (V=5 pas la lettre). Développer des
applis portables entre toutes ces diérences subtiles devient dicile
 90-00 : normalisation. Les diérentes normes POSIX posent un socle commun
 00-10 : n des UNIX propriétaires, abandonnés les uns après les autres par leurs
entreprises. AIX (IBM), IRIX, HP-UX. Microsoft Windows règne sans partage. La
légende dit que Microsoft s'assure de la survie d'Apple (en les nançant secrètement)
Pour éviter le procès antitrust.
 10-20 : internet et les webapps font passer windows au second plan. On parle peut-
être moins des OS installés.
 Langage C :
 1967 : BCPL aux Bell Labs
 1968 : B (simplié mais toujours très illisible)
 1971 : C version K&R. Brian Kernighan et Dennis Ritchies (+ Thompson pour
UNIX)
 1983 : C++ Bjarne Stroustrup
 1989 : ANSI C
 1990 puis 1999 puis 2011 : versions de ISO C
 Le C++ a beaucoup plus de variantes, l'institut de normalisation en ajoute toujours
plus. C++11, C++14, C++17, C++20. Et c'est pas ni, ce qui n'est pas forcément
une bonne nouvelle car le langage devient vraiment énorme, plein de bloat

I.2) Interfaces de l'OS


 Interface graphique vs. shell : même usage. Le mieux est celui que l'on connait le mieux.
 Pour moi c'est le shell car interactions plus riches (paramètres), et c'est utilisable à
distance (accès ssh)
 Je rêve d'une interface mélangeant les deux, comme caja-terminal si ce dernier fonc-
tionnait vraiment
 Interface de programmation : En C la plupart du temps, même s'il faudrait arrêter ça
et passer au Rust maintenant. Mais Rust est un langage exigeant donc dicile d'accès
 Importance de la notion de standardisation : avant POSIX, nombreuses variantes
d'Unix incompatibles, ce qui rendait l'écriture de prog impossible. La standardisation
consiste à graver la syntaxe et des morceaux de sémantique des API dans le marbre.
Ca a du bon et du mauvais
1. Le shell en pratique
 Syntaxe fondamentale : cmd options paramètres
 commandes de base : ls, cd, gcc, emacs
 CtrlC CtrlZ & bg fg : lancer emacs.
 Historique des commandes, édition de commande, Complétion,
 Tubage de commandes
I). SYSTÈMES D'EXPLOITATION (SUITE) 17

 Interaction avec les processus : ps aux, kill 42, killall -STOP firefox
 micro scripts de conversion d'images ou de concaténation de chiers mp3 : convert
-rotate 90
 Lien vers les exos : [Link]

I.3) Sécurité des systèmes d'exploitation


1. Les systèmes réels ont des failles
 Parfois exprès pour ne pas brider l'implémentation
 while (1) fork()
 while (1) {int*a = malloc(512); *a = '1'; }
 while true ; do mkdir toto ; cd toto ; done
 Y'a pleins de undened behavior en langage C aussi. Après un tel comporte-
ment, tout est possible (overow sur les entiers => parfois 2000 < −2000)
 Souvent pas exprès. Très nombreux problèmes de sécurité
 dans les applis, motivation pour le Rust, donc. Faites du Rust je vous dis
 mais aussi dans le matériel : meltdown
 C'est une fuite de données (canal caché) car mise en commun des caches
entre les processus.
 Attaque RowHammer ( != Meltdown) : Lecture de la mémoire des autres
depuis un script javascript s'exécutant dans le sandbox du navigateur
 Pour s'en protéger, Linux vide les caches à chaque changement de contexte.
Perte de presque 30% de performances.
2. Solutions classiques en sécurité
 Réponses techniques :
 les mécanismes de protection mis en place par l'OS
 Utilisateurs UNIX, droit d'accès (rwx r-x r-x : 755) et délégation de droit
(sudo et setuid)
 répertoires : r=ls ; w=création éléments ; x=cd
 chmod et ls -lh
 Windows ore des Acces Control List plus ns
 Réponses sociales :
 quota disque ⇝ publication des stats d'usage de chacun
 Parfois, c'est la seule solution. Il est interdit juridiquement de reverse-engineerer
certains matériels.
 Portail Skylander sur Wii = simple lecteur RFID, mais service juridique
interdit de regarder comment ça marche. Grosses menaces.
 Wii turing complete, donc le constructeur ne peut pas les brider pour n'exé-
cuter que du code maison. Mais l'installation de certains jeux eace tout
code non certié Nintendo, y compris les créations personnelles.
3. Quelques propriétés de sécurité
 condentialité : accessible aux seuls autorisés.
18 SÉANCE 2. OS ET C EN PRATIQUE

 basé sur le secret et les maths. chirement asymétrique


 condentialité persistante (forward secrecy) : si quelqu'un sauvegarde le canal
chiré puis nit par trouver la clé dans le futur, il peut toujours pas lire.
Génération de nouvelles clés à intervalles réguliers, et échange des nouvelles
clés sous la protection des précédentes. Donc on peut pas remonter le ux. On
veut aussi du secret futur (par exemple si PGP est cassé dans 15 ans)
 intégrité : pas de modif indésirée
 authentication : garantie utilisateur est qui il prétend.
 Attention à la biométrie car changer de mdp quand quelqu'un sait le reproduire
est plus facile que changer d'oeil ou d'empreinte digitale.
 Un ministre allemand a perdu l'exclusivité de ses empreintes suite à une photo
HD en conférence de presse.
 Anonymat : / !\ pseudonymat pas susant ; TOR : on noie son ot dans la masse
 Sécurité Instagram comparable à la sécurité des gnus traversant la rivière des
crocodiles. Chacun espère que quelqu'un d'autre se fera bouer, et yolo
 non-répudiation : celui qui a envoyé ça ne peut pas nier l'avoir fait. Banque,
contrat.
 Centralisé (notaire), basé sur la possession d'un secret (carte bleu ou clé PGP),
ou décentralisée (contrat bitcoin)

4. Règles à suivre

 Protéger ses données (c'est la loi)


 Il faut orir une protection raisonnable au regard des données protégées. Cer-
tains systèmes ne sont pas connectés à internet (mais stuxnet a fonctionné
quand même)
 Hadopi : charge de preuve inversée sur le défaut de sécurisation (c'est à l'accusé
de prouver qu'il a fait le nécessaire, alors que dans le reste de la loi, c'est à
l'accusateur de prouver le méfait)
 Ne pas contourner les protection (c'est la loi)
 Avoir la possibilité technique ne donne pas le droit
 c'est comme avec le sac des petites vieilles dans la rue
 La loi actuelle ne plaisante pas
 Loi programmation militaire : internet = circonstance agravante, passage en
antiterrorisme
 jurisprudence blue touf : notion d'eraction n'existe pas. Interdit d'entrer
quand tu sais que t'es pas sensé être là, même si c'est ouvert
 Vous êtes informaticiens, vous ne pourrez plus plaider l'incompétence technique,
alors faites attention. Surtout vues la législation et la jurisprudence françaises
II). LE LANGAGE C 19

II) Le langage C
II.1) Introduction au langage C
 Si vous venez de prépa MPI, vous avez probablement déjà vu le contenu de cette partie
(qui prenait 45mn de cours avant). À l'ENS Rennes, tout ceci est maintenant vu dans
le module de rattrapage MPI, et les sous-sections suivantes ne sont pas vues en cours.
 Intérêt historique et culturel
 Très répandu (tous les OS, 50% de Debian)
 Beaucoup de langages s'en inspirent (C++, Java, C#, Python, Ruby, Rust, PHP)
 Permet de faire des programmes ecaces (mais c'est pas automatique : on peut tout
à fait écrire un code C inecace)
 Intérêt pédagogique
 Comprendre le C permet de comprendre la machine
 C'est le langage évolué le plus proche de l'architecture
 Devenu un modèle opérationel concret de la machine, par co-design : c'est la lin-
gua franca entre les programmeurs les plus bas niveau et les ingénieurs concevant
les ordinateurs, donc le matériel se conforme au modèle puisque c'est ce à quoi
les programmeurs s'attendent.
 Comprendre C et machine pour mieux programmer en <insert language name>
 Les VM cachent plus ou moins bien les détails (CAML vs. Perl)
 Faut connaître les pointeurs C pour bien comprendre les références Python/Java
 Avantages et inconvénients du C
 (+) C'est un langage très simple, très KISS. Syntaxe en une seule page A4
 (-) AUCUN garde fou, les outils font une conance aveugle aux humains, c'est fati-
guant
 (+) On contrôle tout (optimisation) (-) il faut tout contrôler
 (+) On peut comprendre les méandres (-) 1001 façons de se tirer dans le pied
 Presque backward compatible avec 1970 : (+) stable et pérène (-) qques héritages
regrettables
 Pas de bibliothèque standard : (-) il faut tout refaire soi-même
 ⇒ C'est un langage à connaître, mais à n'utiliser qu'au besoin

II.1.a) Historique
 Inventé dans les années 1970 par Brian Kernighan et Dennis Ritchie, du laboratoire
Bells de AT&T, pour abstraire un peu l'écriture de programmes de l'ordinateur cible.
Compilateur pour traduire le code source des humains en langage machine des ordis ⇝
ne pas tout réécrire à chaque fois.
 Mais ça reste un langage très proche de la machine (compilos très limités à l'époque)
 La première machine cible est le PDP-11 : 24ko de mémoire ⇝ KISS
 La mémoire est un tableau d'octets sans structure
20 SÉANCE 2. OS ET C EN PRATIQUE

 (le nom est une mauvaise blague car c'est une variante du langage B de laboratoires
Bells)

II.1.b) Syntaxe de base du langage C


 On suppose la syntaxe connue, donc on ne s'attarde pas du tout.
 Au besoin le TP préquel "C starter" permet à pour ceux qui n'en ont jamais fait de
voir les bases en autonomie.
 D'autant que la syntaxe est très classique, très proche de ce qu'on trouve en C++
ou en Java (forcément), et aussi du Python (sauf la façon de délimiter les blocs)
 Il faut déclarer les variables à l'avance, qui sont typées pour aider le compilo (pas pour
la sémantique).
 int et double pour tout faire (booléen : entiers  vrai ̸= 0)
 Préprocesseur : les lignes commençant par # passent dans une machine à cherche-
remplace améliorée
 Il n'y a rien dans le langage et quasi pas de bibliothèque standard, et tout est à faire à
la main
 conçu en 1970, c'est à l'humain de s'adapter aux outils
 messages d'erreur parfois cryptique (mais moins pire avec clang)

II.1.c) Écrire du code C


 Workow attendu
 On écrit son source dans un chier toto.c (avec geany)
 On compile ce chier (ie, traduction en binaire exécutable) gcc toto.c -o binaire
 On exécute ce binaire ./binaire
 Compiler ? C'est quoi en vrai ?
 Phase 1 : préprocesseur (dene, include, ifdef)
 Phase 2 : compilation = traduction en assembleur
 Phase 3 : édition de liens = puzzle des ptits bouts d'assembleur (si plusieurs chiers)

II.2) Pratique du langage C


 Premières bêtises possibles :
 Exécuter le source : impossible, le C est pas interprété (le CPU ne comprend que le
langage machine  cf. M99)
 Compiler sans resauvegarder (un IDE aide, mais il faut savoir faire sans  e.g. à
l'agreg)
 Exécuter sans recompiler (idem)
 Ne pas lire les erreurs ou warning (clang plus compréhensible)
 Erreurs classiques qu'il faut savoir reconnaître
 syntax error : facile
III). RÉSUMÉ DE LA SÉANCE "OS ET C EN PRATIQUE" 21

 missing main function : car oui, il faut un main. On ne peut pas écrire de code hors
des fonctions comme en python
 redénition d'une fonction
 segfault : c'est le grand problème
 Comment réagir : don't panic ; lire le message d'erreur ; ne pas lire le second message
d'erreur (le compilo est aux fraises)
 Il faut faire preuve d'auto discipline et utiliser les rares outils à notre disposition.
 -Wall -Wextra -Werror -g -fsanitize=undefined
 Écrire du C moderne (éviter certaines choses autorisées par le compilo  place des
déclarations)
 gdb et valgrind pour chasser les bugs (y'a un TP spécique)
 KISS (simplicité algorithmique), car un algorithme embrouillé donne rarement un
beau code
 Le code doit être lisible car c'est vous-même qui allez le lire le plus souvent. Et puis,
on n'a pas le niveau de l'IOCCC (Intl Obfuscated C Code Contest). On regardera
ensemble les 3 premières entrées de la liste suivantes (en fonction du temps dispo),
sur le code distribué.
 2012 endoh1 (simulation uide) avec les entrées pour-out, column et clock
 2000 dhyang (quine achant le visage de Saitou Hajime puis aku soku san, ie sin
swift slay)
 2004 arachnid (simulateur labyrinthe déplacement : adws)
 2014 deak (calcul de fractale)
 2014 mado (mario)
 2015 mills2 : un programme de compression très très court
 2015 mills1 : un appy bird ascii

III) Résumé de la séance "OS et C en pratique"


 Le langage C est un bon langage pour comprendre la machine
 Il ore une "abstraction" très représentative de la réalité sous-jacente
 De toute façon, on est obligé de comprendre la machine pour parvenir à programmer
en C
 Le C est un langage préhistorique
 Faut tout faire soit-même, et ça pique un peu
 C est un langage préhistorique, mais les outils aident. IOCCC
 Les chiers en C. fopen, fprintf, fscanf.
 Sécurité informatique. Faut protéger ses infrastructures ; la capacité technique ne donne
pas le droit.
Séance 3

Mémoire statique
 2 docs à imprimer : Exemples de code + TD mémoire statique (choses à préparer)
Programme du jour
I) Identiants C, portée et durée de vie . . . . . . . . . . . . . . . 22
I.1) Portée des identiants locaux et globaux . . . . . . . . . . . . . . 22
I.2) Paramètres et pile d'appel . . . . . . . . . . . . . . . . . . . . . . 23
I.3) Mot-clé static . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
II) Organisation logique de la mémoire . . . . . . . . . . . . . . . . 24
II.1) Memory layout d'un processus dans l'OS . . . . . . . . . . . . . 24
II.2) Espace d'adressage virtuel . . . . . . . . . . . . . . . . . . . . . . 25
III) Pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
III.1) Utiliser les pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . 26
III.2) Pièges classiques avec les pointeurs . . . . . . . . . . . . . . . . . 27
III.3) Le pointeur NULL . . . . . . . . . . . . . . . . . . . . . . . . . . 27
IV) Résumé de la séance "Mémoire statique" . . . . . . . . . . . . . 28

I) Identiants C, portée et durée de vie


I.1) Portée des identiants locaux et globaux
 Pas de diérence entre variables et fonctions (application un peu extrême des idées de
Von Neumann)
 Les identiants sont globaux ou locaux. Cela leur donne une visibilité ou portée (scope)
et une durée de vie spécique.
 Les variables locales sont dans une fonction.
 Scope : visible depuis le bloc appelant
 Durée de vie : jusqu'à la n du bloc
 Faire des fonctions locales n'est pas très C-ish, même si les compilos modernes
acceptent.
 Identiants globaux (variables et fonctions)
 Scope : partout ; Durée de vie : toujours (jusqu'à la n du programme)

22
I). IDENTIFIANTS C, PORTÉE ET DURÉE DE VIE 23

 Étude du programme 1 sur la visibilité, sur le code imprimé


 Oui, on peut faire des locales à un sous-bloc, aussi. (l.9) a:0 b:0
 Non, faire des variables de même nom qui se masquent ainsi(l.13) a:? b:0
n'est pas une bonne idée. (l.19) a:10 b:10
 Pour comprendre, il faut imaginer des variables qui s'em-(l.24) a:10 b:0
pilent, et indicer a par la ligne de déclaration (a2 pour la(l.26) a:10 b:15
globale) (l.28) a:10 b:0

I.2) Paramètres et pile d'appel

 Quel est l'achage du programme 2 (fonctions et paramètres) ?


 Cela inverse les valeurs de a et b car les paramètres ligne 4 sont inversés. haha, très
drôle.
 En fait, non. C'est pas drôle, il ne faut pas écrire des pièges à soi-même comme ça.
 La pile d'appel est un endroit de la mémoire où sont créés des cadres de pile, un
contexte d'exécution pour chaque appel de fonction (récursif ou non). C'est là que
vivent les variables locales. Créé à la demande lors de l'appel, puis détruit lors du
return.
 Les paramètres sont des variables locales (que la bienséance seule interdit de modier
même si le programme 3 montre que le compilo laisse faire)
 En C, (tous) les paramètres sont passés par copie.
 Le M99 ne fonctionne pas exactement comme ça, puisque les paramètres sont consom-
més. Il faudrait améliorer le M99 pour corriger.
 Étudions le programme 4a

max
a 12
b 42
main main main
x 12 x 12 x 12
y 42 y 42 y 42
z ?? z ?? z 42

Stack Stack Stack


 Attention, le passage par valeur pose parfois des pièges, comme on va voir dans le
programme 4b
 On calcule bien 36 dans la fonction, mais cette zone mémoire est eacée en sortie
de fonction
24 SÉANCE 3. MÉMOIRE STATIQUE

triple triple
a 12 a 36
main main main main
x 12 x 12 x 12 x 12

Stack Stack Stack Stack

I.3) Mot-clé static


 Quel est l'achage du programme 5 ?
 La colonne des a est remplacée par des zéros ; l'incrément de a en n de fonction est
sans eet puisque la variable est détruite après chaque appel
 Le mot-clé static a deux signications :
 Associé à une variable locale, cela augmente sa durée de vie tandis que sa portée est
inchangée. Cf. programme 6, qui fait ce à quoi on s'attend : la fonction nextInt
énumère les nombres entiers
 Associé à une variable globale, cela réduit sa visibilité au chier courant. Sa durée
de vie est inchangée. C'est l'équivalent Java ou C++ de private

portée durée de vie


Fonction projet ∞
Variable globale projet ∞
globale static ≈ chier ∞
locale static bloc ∞
locale bloc bloc

 Plus précisément, une globale statique est limitée à l'unité de compilation, c'est à dire
au .o constitué.
 Il est important ici de comprendre que la portée est dénie à la compilation, tandis que
la durée de vie est à l'exécution.
 Ces nouvelles connaissances posent la question de où sont stockées les locales statiques
en mémoire.
 on dirait une globale : initialisé une seule fois et persistant entre les appels
 Il faut parler système pour comprendre ce mystère

II) Organisation logique de la mémoire


II.1) Memory layout d'un processus dans l'OS
 Les locales static sont troublantes
 On dirait une globale (initialisé une seule fois, presistante) mais déguisée en locale
(visible ici seulement)
II). ORGANISATION LOGIQUE DE LA MÉMOIRE 25

 Pour comprendre, il faut savoir comment est organisée la mémoire du processus.


 (un processus est un programme en cours d'exécution dans l'ordinateur)
 Il y a trois segments mémoire, de 0 à 232 en 32bits
 Data : Là où se trouvent le code compilé + les variables globales. C'est ce qu'on
avait en M99
 On reparle du tas bientôt : c'est là où on fait les malloc pour ceux qui connaissent.
Il n'y en a pas en M99 car le tas est une fonctionnalité de l'OS et non du matériel.
 Pile : Contient les cadres de piles comme vu plus haut, et comme dans la M99 (c'est
aussi une caractéristique de l'OS et non du matériel, mais celle ci existe en M99)
 Il y a un trou (une zone inutilisée) entre la pile et le tas, qui peuvent grandir tous
les deux. Si collision, alors le processus a épuisé la mémoire disponible.
 Notez que c'est une simplication par rapport à la réalité, qui contient plus de
segments (bibliothèques) dont on reparle l'an prochain en SEA

Data Heap Stack



Code+Globals Dynamic Memory Stack Frames
0 4Gb

 C'est de l'adressage virtuel, car l'OS fait de la médiation sur les cases mémoires lues.
 A chaque accès mémoire, le programme lit une adresse virtuelle sans savoir où les
données sont physiquement en mémoire. Le CPU a une table associative (TLB 
translation lookaside buer) qui à chaque couple (pid, adresse virtuelle) associe
une adresse physique.
 Il faut faire ecace pour les performances générales donc fait par matériel : MMU
= zone du CPU.
 De plus la TLB groupe la mémoire par pages, pas des adresses individuelles
 C'est fondamental pour assurer les fonctions de l'OS vues la semaine dernière :
protection des applis ; virtualisation mémoire ; uniformisation des accès mémoire
depuis l'appli
 Cela explique les locales static : à la compilation, ce sont des locales, mais le compi-
lateur les place dans le segment Data, pas dans les cadres de pile.

II.2) Espace d'adressage virtuel


 C'est un autre nom pour la mémoire d'un processus. Elle peut donc être vue comme
un grand tableau de cases mémoire successives. Chaque case fait un octet sur tous les
systèmes (hard et OS) que je connais.
 Adresse mémoire : numéro de la case dont on parle, tout simplement. En adressage
virtuel, hein.
 Pourquoi la pile commence à 4Go ? Car je fais mes dessins en 32bits et que MAXINT=232 =4Go
sur cette architecture.
 En fait, sous linux, la pile commence à 3Go car le dernier Go de l'espace d'adressage
est un mapping direct d'une zone de l'OS pour rendre les context switch entre l'appli
et l'OS plus ecaces. Mais c'est hors programme.
26 SÉANCE 3. MÉMOIRE STATIQUE

 Et si l'ordinateur n'a pas tant de mémoire ? ⇝ virtualisation : des pages swappées sur
disque au besoin
 Contenu d'une cellule
 Avec 8 bits on code 256 valeurs. Par exemple [0, 255] ou [−127, 128]
 Pour manipuler des plus grandes données, on agrège plusieurs cases (lues et inter-
prétées ensemble)
 2 cellules : 216 = 65536 ; 4 cellules : 232 ≈ 4 · 1010 ; 8 cellules : 264 ≈ 1.8 · 1019

 Organisation interne
 Il n'y a aucune méta-donnée, ce qui est cohérent avec le langage C : si tu l'as pas
fait, y'en a pas.
 On ne sait donc pas interpréter les données qui se trouvent dans la mémoire
 comme en M99 : on sait pas si 110 est LDA 10, ou si c'est la valeur numérique
110.
 En C, on ne sait même pas si c'est une valeur ou un morceau (l'une des cases)
d'une valeur

III) Pointeur
 C'est un mot qui fait peur quand on apprend le C, et c'est une source d'erreur très
importante. Peut-être la principale
 Mais à la base, c'est juste une variable numérique contenant une adresse mémoire
 En 32bits, il faut 4 cases pour stocker une adresse

pointer p
0x404
0x405
0x406
0x407
0x408
0x409
0x40A
0x40B

0x410
0x411
0x412
0x413
0x414
0x415
0x416
0x417
0x418
0x419
0x41A
0x41B
0x41C
0x41D
0x41E
0x41F
0x420
0x421
0x422
0x423

... 0x414 ...

 Pour savoir interpréter une case pointée, il faut connaître le type de donnée stockée à
cet endroit
char* pc; a int* pi; 42
 On peut pointer vers une case contenant un pointeur. On obtient par exemple int**

III.1) Utiliser les pointeurs


 La valeur numérique des pointeurs n'a aucune valeur sémantique (valeur 0x414 sans
importance).
 Ce qui compte, c'est ce vers quoi ça pointe (c'est un pointeur vers là où la variable
cpt est stoquée)
 C'est à quoi sert l'opérateur & qui se lit adresse de
III). POINTEUR 27

i p
int i=42; int* p=&i; 42
 On a déjà rencontré ce & : dans les paramètres de la fonction scanf. Cela explique
comment elle peut modier les variables dans la fonction appelante :
 Et c'est pour ça que la fonction a besoin du %d : c'est pour savoir comment interpréter
ce pointeur.
 Oui, les pointeurs permettent de modier la mémoire hors du scope. Mémoire magma
informe.

scanf
int main() {
int a;
&
scanf("%d", a); main
} a ?

Stack
 On peut maintenant corriger le code de la fonction triple sur la feuille
 Le paramètre est de type int*, on lit et modie avec *a, on appelle avec &a

III.2) Pièges classiques avec les pointeurs


 #1 : L'étoile * a une sémantique extrêmement lourde.
 une étoile en trop ou pas assez dans un programme, et c'est le SEGFAULT (mort
du processus)
 #2 : L'étoile a deux sens très diérents
 =int *p= declares a pointer variable* p which is a pointer to an integer value
 =*p= is then the pointed value, interpreted according to the pointer type
 (that's actually three meanings when counting ×, the multiplication)

 =int *p ; p=12 ;= selects where it points in memory


 =int *p ; *p=12 ;= changes the memory in the pointed area

 Pascal was a bit more reasonable : INTEGER ^p vs. p^ (pas le même ordre au moins)
 In Java, there is no pointers, but reference to objects are close to that concept

III.3) Le pointeur NULL


 Utile pour dire non initialisé, ou invalide (comme dans la fonction fopen)
 Par convention, c'est la valeur numérique 0 (même ça, ça n'est pas dans le langage. Un
simple dene)
 Pour s'assurer que l'OS fait bien le SEGFAULT attendu, on a quelques pages sans
permission de r/w au début
28 SÉANCE 3. MÉMOIRE STATIQUE

IV) Résumé de la séance "Mémoire statique"


 Identiants locaux / globaux
 globales statiques : portée réduite
 locales statiques : durée de vie augmentée
 Pile d'appel, les paramètres sont des variables locales
 Organisation mémoire du processus : les trois segments Data / Tas / Pile (explique les
locales statiques)
 Espace d'adressage, la mémoire est un grand tableau
 Pointeur = adresse d'une case
 Permet de modier n'importe où en mémoire. C'est la force du C ; c'est la raison
d'arrêter le C.
 Les pièges des pointeurs : une étoile en trop et c'est le drame ; l'étoile a 3 signications
 Le pointeur NULL
Séance 4

Mémoire dynamique
 1 doc à imprimer : TP debug (choses à préparer)
 Logistique ENS :
 Portes ouvertes lycée : il faut déjà savoir lesquelles ont lieu quand.
Le département rembourse un (seul) aller-retour de train par lycée pour ça. Parlez-
vous entre anciens collègues.
 Connectez-vous à [Link] pour créer un compte
 TODO : Faire un doc à imprimer pour gagner du temps en séance ? Pas sûr que ce
soit utile : ça passe dans le temps imparti en speedant, et c'est l'occasion de faire des
schémas mémoire à coté du code copié pour l'occasion
 TODO : Décaler les pointeurs de fonction à la séance suivante, car le temps est un peu
court quand même, et c'est une notion avancée

Programme du jour
I) Pointeurs (suite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
I.1) Arithmétique des pointeurs . . . . . . . . . . . . . . . . . . . . . 30
I.2) Transtypage (cast en anglais) . . . . . . . . . . . . . . . . . . . . 30
I.3) Pointeurs génériques : void* . . . . . . . . . . . . . . . . . . . . 31
II) Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
II.1) Similarités et diérences avec les pointeurs . . . . . . . . . . . . 31
II.2) Les chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . 32
III) Mémoire dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . 33
III.1) Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
III.2) L'interface malloc . . . . . . . . . . . . . . . . . . . . . . . . . . 33
III.3) Survivre à malloc . . . . . . . . . . . . . . . . . . . . . . . . . . 34
III.4) Les alternatives à malloc (hors programme) . . . . . . . . . . . . 35
IV) Résumé de la séance "Mémoire dynamique" . . . . . . . . . . . 35

29
30 SÉANCE 4. MÉMOIRE DYNAMIQUE

I) Pointeurs (suite)
I.1) Arithmétique des pointeurs
 Addition : pointeur + entier : valide.
 C'est un décalage en case, pas en octets.
 C'est déroutant, mais c'est parce que les pointeurs ressemblent aux tableaux en C
p[i] ≜ *(p+i)
 Donc after = before + sizeof(int)*3 car ce sont des entiers.
pointer p
int *pi=0x400;

0x400
0x401
0x402
0x403
0x404
0x405
0x406
0x407
0x408
0x409
0x40A
0x40B
0x40C
0x40D
0x40E
0x40F
0x410
0x411
0x412
0x413
0x414
0x415
0x416
0x417
pi=pi+3;
printf("pi:%x\n",pi);
before after
 Soustraction : pointeur - entier : valide
 C'est la même chose, mais en décalant vers la gauche.
 Pointeur - pointeur : Valide s'ils pointent sur la même chose. C'est le calcul du nombre
de cases entre eux
 Toutes les autres opérations entre pointeurs et entiers sont invalides (division, multipli-
cation, etc)

I.2) Transtypage (cast en anglais)


 c'est l'art de convertir un type dans un autre. Par exemple int a = (int)b .
 Quel que soit le type de b, le compilateur fait de son mieux pour en faire un entier
 On retrouve des transtypages dans à peu près tous les langages.
 Deux types très dierents de transtypages en C.
 Dans tous les cas, le type en C ne dénote pas de la sémantique, mais taille et représen-
tation en mémoire
1. Transtypage de scalaires
 un scalaire est une valeur normale : un entier, une lettre, un double.
 Lors d'un transtypage de scalaire, on change la valeur.
 double d = 5.7; 5.7
int i = (int)d; 5
 Cela change la représentation en mémoire.
 Dans l'exemple, double représentés en IEEE 754 (partout), sur 8 octets ; entier
sur 4 octets en 32bits.
 Cela peut induire une perte irréversible de précision
 Dans l'exemple, on ne retrouvera jamais le 0.4 depuis la variable i
2. Transtypage de pointeurs
II). TABLEAUX 31

 Mémoire inchangée (donc valeur inchangée), mais change la sémantique.


 Càd, change la façon d'interpréter les pointeurs (arithmétique)
int a; after
int* pi=&a;
char* pc=pi; a pi pc
pi++;
pc++;
before
 Dans l'exemple : pi se décale de 4 octets car il pointe sur des entiers ; pc se décale
d'un seul octet.
 C'est cohérent avec l'idée que les pointeurs peuvent être utilisés comme des ta-
bleaux en C

I.3) Pointeurs génériques : void*


 Ce sont des pointeurs dont on ne connaît pas le type.
 Exemple : les paramètres de printf ou scanf
 Exemple : manipulation directe de la mémoire memcmp, memcpy (si pas overlap),
memmove (si overlap)
 A l'origine on ne pouvait pas faire d'arithmétique des pointeurs sur ces pointeurs, et
c'est assez logique
 Mais gcc l'autorise quand même, en supposant que sizeof(void)=1 car c'est trop
pratique
 C'est une extension GNU, donc clang le fait aussi mais pas icc ni visualC
 Oui, la taille du vide n'est pas nulle, mais le vide est de taille 1 :)

II) Tableaux
II.1) Similarités et diérences avec les pointeurs
 Tableaux et pointeurs se ressemblent beaucoup en C, sans être la même chose : conver-
sions automatiques
 C'est une source de complexité très avancée. Les plus curieux liront le tuto "la vérité
sur les pointeurs et les tableaux en C" (sur la page du cours) pour comprendre. Les
concepteurs du langage ont un regret pour ce point précis : ils ont fait trop compliqué.
 Exemples de code pour comprendre les points communs et diérences
 int* p;
 int tab[3];

 p = tab;

 p += 2;

 tab[1] = 5; 5
32 SÉANCE 4. MÉMOIRE DYNAMIQUE

 p[-1] = 6; 6 (aucune vérication des bornes en


C, jamais)
 Dans cet exemple, tab = p n'aurait aucun sens : tab n'est pas une case mémoire mo-
diable.
 Il faut voir tab comme une valeur numérique, comme 42. On ne change pas la valeur
de 42.
 Chaque fois que le compilateur voit tab, il écrit une valeur numérique, l'adresse du
début de tab dans le segment Data qu'il construit (ce qui est joyeux à calculer avec
l'ASLR address space layout randomization, mais c'est vraiment hors sujet).
 C'est aussi pour ça qu'on ne peut pas écrire tab1 = tab2 Le compilateur ne voit que
des adresses. Ce serait comme lui dire d'exécuter l'aectation 32 = 14 . Nonsense.
 Pour bien comprendre la diérence entre pointeur et tableau, rééchissons à la taille
mémoire occupée.
 p est un pointeur ⇝ 4 octets en 32bits ; tab est un tableau de 3 cases ⇝ 12 octets
en 32 bits
 Mais quand sizeof compte la place occupée par le tableau, il compte toutes les
cases à la fois. Donc un int t[3] occupe 12 octets (3 cases de 4 octets chaque).
 D'ailleurs, on compte les cases avec sizeof(tab)/sizeof(tab[0]) .
 Donc les pointeurs et les tableaux sont diérents.
 Sauf dans les types de paramètres : fun(char *p) ≜ fun(char p[3]) ≜ fun(char
p[])
 Là, les pointeurs et les tableaux sont rigoureusement identiques, et la taille du ta-
bleau est ignorée.
 Plus précisément, le tableau est dégradé en pointeur et on ne peut plus utiliser la
ruse précédente pour connaître la taille du tableau, car le pointeur n'a plus la taille
du tableau pointé.
 C'est historique, mon ami.
 Les pointeurs de tableaux ne sont pas des tableaux de tableaux (ni des pointeurs de
pointeurs)
 si int tab[3] = { 0, 1, 2}; alors &tab est de type int (*)[3], ce qui n'est pas
int **
 mais c'est compliqué, vous irez voir le doc sur le site si ça vous intéresse vraiment
 La fonction main accepte les deux prototypes, entre autres.

II.2) Les chaînes de caractères


 Il n'y a pas de type String, on fait des tableaux de caractères.
 Pas de métadonnées où ranger la taille. Convention : chaînes terminées par le caractère
zéro, noté '\0'
 La convention du langage Pascal était de préxer la taille dans un entier short au début
de la chaîne, mais ça limite la taille de la plus grande chaîne (65535 lettres max) et ça
III). MÉMOIRE DYNAMIQUE 33

consomme un octet de plus par chaîne.


 Fonctions à connaître : strlen, strcpy, strcat, strcmp
 Vous comprenez maintenant pourquoi il n'y a pas besoin d'esperluette pour lire une
chaîne de caractères avec scanf, et aussi pourquoi on risque les dépassements mémoire
à faire cela.
 Une initialisation devrait être char str[]={'h', 'e', 'l', 'l', 'o', 0}; mais
c'est un peu lourd, donc on peut faire char str[] = "hello"; . Attention à bien
garder la place pour le 0 nal si on précise la taille.

III) Mémoire dynamique


III.1) Motivation
 Les tableaux sont de taille statique en C (canal historique)
 L'écriture int n; scanf("%d", &n); int tab[n]; est interdite aux concours
 Donc il faut connaître la taille de tous les tableaux à la compilation
 C'est très pénible, on voudrait pouvoir faire des tableaux de taille connue seulement
à l'exécution
 En fait, les tableaux de taille variable sont autorisés dès C99, mais on a quand même
besoin de la solution ci-dessous dans d'autres contextes plus complexes, comme des
structures imbriquées
 Solution : on va le faire à la main, en utilisant le tas :
 On demande des blocs mémoire quand on en a besoin
 On les rend après usage

Data Heap Stack



Code+Globals Dynamic Memory Stack Frames
0 4Gb

III.2) L'interface malloc


 c'est l'approche standard. c'est une bibliothèque ; emacs plus hardcore et fait sans ça,
brk() direc
 #include <stdlib.h>
 void* malloc(int size) : réserve un bloc de size octets en mémoire et retourne
adresse début
 void free(void* p) : libère (rend) un bloc précédemment alloué
 void* realloc(void* p, int size) : modie la taille d'un bloc ; /!\ cela peut le
déplacer
 Exemple simple.
 void *A=malloc(12); A
 void *B=malloc(5); A B
34 SÉANCE 4. MÉMOIRE DYNAMIQUE

 free(A); B
 void *C=malloc(6); C B
 C=realloc(C,13); B C

III.3) Survivre à malloc


 Comme d'habitude en C, il n'y a aucun garde-fou et la moindre erreur mène au SEG-
FAULT
 Solution 1 : bonnes pratiques pour éviter les problèmes (et métaphore du notaire :
mémoire = terrain)
 Règle #1 : on n'accède qu'à des zones réservées
 usage avant malloc : on achète le terrain avant de construire, svp
 usage après free : on arrête d'utiliser ce qu'on a vendu, svp
 symptômes sinon : SEGFAULT si chanceux, corruption mémoire quelque part
sinon
 On ne peut pas être chanceux sur un use after free
 Règle #2 : à chaque malloc correspond un free
 s'il manque un free, alors c'est une fuite mémoire (memory leak)
 Le système pense que la mémoire est encore prise et ne peut la réutiliser
 Quand y'a qu'un leak, ça va. C'est quand il y en a beaucoup que ça pose
problème.
 Ralentissement (swapping), puis malloc retourne NULL (en vrai, le malloc de
la glibc ne le fait pas), puis l'OOM killer de linux intervient
 Double free du même malloc : on vend deux fois
 Si le bloc n'avait pas été réalloué, c'est une no-op
 S'il avait été réalloué [dans un autre module], ça le libère dans le dos de l'autre.
Pas cool.
 int *A = malloc(12); free(A);
 int *B = malloc(12); free(A); ⇝ libère B.
 Les mallocs modernes se suicident quand ils détectent ça. Car oui, y'a plusieurs
implem de malloc, et même de la recherche en R&D là dedans. La détection
se base sur les métadonnées autour. Implémenter son propre malloc est un
exercice instructif, même s'il est dur de prendre les vrais de vitesse
 Solution 2 : il faut connaître les outils pour soigner le mal :
 valgrind pour détecter les erreurs à leur source, et pas seulement les défaillances : un
outil formidable, simple d'accès et voit 90% des problèmes (seul le tas est surveillé,
pas la pile)
 gdb pour explorer l'état de la mémoire : un outil formidable. Vieux mais parfaitement
robuste. Pratique comme un tank soviétique.
 C'est l'occasion d'introduire un peu de vocabulaire classique
 faute : bêtise du programmeur
IV). RÉSUMÉ DE LA SÉANCE "MÉMOIRE DYNAMIQUE" 35

 erreur : comportement incorrect (ie, comportement proscrit, ou qui ne fait pas


partie de la spec)
 défaillance : comportement observé ̸= comportement attendu (là, ça se voit)

III.4) Les alternatives à malloc (hors programme)


 Malloc est une interface implémentée par une bibliothèque. On peut donc faire sa propre
implem (pour bien comprendre comment ça fonctionne, ou pour l'adapter à son cas
spécique) et/ou on peut faire complétement sans. emacs par exemple a été écrit avant
que malloc existe et fait donc sans.
 brk() est un appel système permettant de décaler la frontière de n du tas, ce qui
revient à demander des pages contiguës au noyau pour y mettre sa mémoire.
 mmap() est un appel système permettant de demander des pages virtuelles supplémen-
taires. On mappe de la mémoire virtuelle sur de la mémoire physique. L'intérêt est
que c'est pas forcément contigu, ce qui donne plus de liberté (le tas risque d'entrer en
collision avec d'autres choses préallouées s'il grandit beaucoup, même s'il peut y avoir
de la mémoire virtuelle non-attribuée). D'ailleurs la pile n'est pas forcément contiguë
dans les systèmes modernes, pour la même raison

IV) Résumé de la séance "Mémoire dynamique"


 Transtypage : scalaire et pointeurs
 Arithmétique des pointeurs, Pointeurs et tableaux : c'est similaire en r-value (sans être
identique) et très diérent en l-value
 Chaînes de caractères
 Pointeurs void* et pointeurs invalides ou pendouillants (dandling)
 Fonctions malloc/free/realloc
 Pour survivre, il faut de bonnes pratiques pour éviter les pbs
 On n'accède qu'à des zones réservées (ni use-before-malloc, ni use-after-free)
 A chaque malloc correspond un free (ni memleak, ni double-free)
 Il faut connaître les outils pour soigner le mal
 valgrind détecte les erreurs à la source
 gdb permet d'explorer la mémoire en cours d'exécution
Séance 5

Maîtriser la programmation C
 1 doc à imprimer : Doc5 (mais pas le TP à rendre qui sera distribué en salle)
 TODO : Décaler la date du rendu à la veille du TP suivant, pas le soir du TP
 Note d'animation : Je n'ai pas eu le temps de faire la partie sur les tests en 2324, qui
n'est pas forcément si intéressante de toute façon. Peut-être qu'on pourrait vouloir faire
une partie sur git, aussi
 Note d'animation : j'ai fait la partie sur la motivation de code propre une séance, puis
insisté sur comment faire au début d'un autre séance correspondant au milieu de la
période de projet

Programme du jour
I) Pointeurs avancés : pointeurs sur fonction . . . . . . . . . . . . 37
II) Constructions C pour organiser ses données . . . . . . . . . . . 37
II.1) Déclarer des structures . . . . . . . . . . . . . . . . . . . . . . . . 37
II.2) Nommer ses types avec typedef . . . . . . . . . . . . . . . . . . 38
II.3) Ranger ses valeurs avec enum . . . . . . . . . . . . . . . . . . . . 38
III) Constructions C de bas niveau . . . . . . . . . . . . . . . . . . . 38
III.1) Déclarer des unions . . . . . . . . . . . . . . . . . . . . . . . . . 38
III.2) Champs de bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
IV) Modèles d'organisation d'un code C . . . . . . . . . . . . . . . . 40
IV.1) Procédural . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
IV.2) Orienté objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
IV.3) Le module point en détail . . . . . . . . . . . . . . . . . . . . . . 40
V) Code lisible et propre . . . . . . . . . . . . . . . . . . . . . . . . . 41
V.1) Code lisible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
V.1.a) Commentaires . . . . . . . . . . . . . . . . . . . . . . . 41
V.1.b) Identicateurs . . . . . . . . . . . . . . . . . . . . . . . 42
V.1.c) Mise en page . . . . . . . . . . . . . . . . . . . . . . . . 42
V.2) Code propre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
V.3) Pièges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
V.4) Tests (reporté au semestre suivant) . . . . . . . . . . . . . . . . . 44
VI) Compilation séparée . . . . . . . . . . . . . . . . . . . . . . . . . . 45
VII) Conclusion sur le module . . . . . . . . . . . . . . . . . . . . . . . 46

36
I). POINTEURS AVANCÉS : POINTEURS SUR FONCTION 37

I) Pointeurs avancés : pointeurs sur fonction


 C'est utile pour passer des callbacks (préciser un comportement à exécuter quand un
évènement arrive) ou pour faire du dynamic dispatch (associer un code à exécuter à
un type de structure, pour faire un pas de plus vers la POO en C. Pas forcément une
bonne idée : C++ préférable)
 Dénir une variable de type pointeur : il faut des parenthèses.
 int (*fun)(void); // :
 La variable est nommée fun ; son type est int(*)(void).
 Si on oublie les parenthèses, int* est prioritaire, et le résultat n'a pas de sens.
 Aecter une valeur à un pointeur sur fonction : pas besoin d'esperluette
 int mafonction(){ return 42; } // Déclaration d'une fonction
 fun =&mafonction; // La variable fun contient maintenant un pointeur vers mafonction.
 fun = mafonction; // exactement pareil : le & est optionnel
 car aucun autre sens possible pour cette expression : une fonction en rvalue ne peut
être qu'une prise d'adresse, donc le compilateur est permissif
 Invoquer la fonction pointée : naturellement
 x = (*fun)() // La variable x vaut maintenant 42
 x = fun() // Exactement comme avant, même si c'est très moche. C'est autorisé
car non-ambigu.

II) Constructions C pour organiser ses données


 En C, on est libre de tout, y compris de coder proprement
 Il faut tout faire, car le compilateur n'aidera en rien (il ne se plaint pas des fautes de
goût)

II.1) Déclarer des structures


 On va vite car c'est au programme de prépa MPI de nos jours
 Base de la propreté en informatique : ranger ensemble ce qui va ensemble. En C, c'est
des structures
 Voir le code de la structure point sur la feuille de pompe. /!\ Attention au ' ;' nal.
C'est qu'on peut dénir une variable de ce type entre l'accolade fermante et le point-
virgule
 Exemple d'usage avec la notation pointée
 Initialisation en place : struct point p2 = {2, 4.2, 3.7};
 Usage en paramètre ou en retour de fonction ⇝ copie complète, comme d'habitude
 Possibilité de faire des structures imbriquées, voire récursives.
 Pas d'opérateurs globaux. memset et memcpy pour mettre à zéro ou copier l'un dans
l'autre.
38 SÉANCE 5. MAÎTRISER LA PROGRAMMATION C

II.2) Nommer ses types avec typedef


 Nommer ses types est une bonne idée pour rendre le code plus lisible.
 L'habitude est de terminer le nom des types par _t comme dans point_t
 La sémantique du typedef est : Le dernier mot de la ligne devient un synonyme de
tout le reste de la ligne. Cette règle admet des exceptions quand on dénit un type de
fonction ou de pointeur sur fonction, puisque le nom de l'identicateur est avant les
parenthèses
 Cf l'exemple ligne 4 de point.h

II.3) Ranger ses valeurs avec enum


Énumérations en C
1 #include <stdio.h>
2
3 enum couleur { pique, coeur, carreau, trefle };
4 typedef enum couleur couleur_t;
5
6 const char* couleur_str(couleur_t c) {
7 const char* res = "couleur invalide";
8 switch (c) {
9 case pique: res = "pique"; break;
10 case coeur: res = "coeur"; break;
11 case carreau: res = "carreau"; break;
12 case trefle: res = "trefle"; break;
13 }
14 return res;
15 }
16
17 int main() {
18 couleur_t val = pique;
19 val += 2;
20 printf("La valeur est %s (%d)\n",
21 couleur_str(val), val);
22 return 0;
23 }
 enum permet de dénir un type n'admettant que certaines valeurs nommées
 L'exemple présenté sur les couleurs de carte est très classique.
 Cela va naturellement avec les switch cases : Si on fait des switch sur une valeur
énumérée (eg le type de la valeur dans l'union), alors on a envie de faire un enum.
 On ne peut pas retrouver le nom de la valeur à runtime : c'est mappé sur des entiers à
la compilation.
 Le cas default devient optionnel car le compilateur nous prévient si on oublie l'un des
cas dans le switch (par exemple quand on ajoute des cas).
 Par contre, ça peut encore rater si on fait de l'arithmétique sur ces valeurs

III) Constructions C de bas niveau


III.1) Déclarer des unions
 C'est exactement comme une structure, sauf que tous les champs d'un union sont au
même endroit en mémoire
III). CONSTRUCTIONS C DE BAS NIVEAU 39

Les unions en C
1 #include <stdio.h>
2
3 union entier {
4 int i;
5 char c[sizeof(int)];
6 };
7
int main() {

e.c[0]

e.c[1]

e.c[2]

e.c[3]
8
9 union entier e;
10 e.i = 42;
11 42
12 for (int i=0; i < sizeof(int); i++) e.i
13 printf("%d ", e.c[i]);
14 printf("\n");
15 e.i = -42;
16 for (int i=0; i < sizeof(int); i++)
17 printf("%x ", e.c[i]);
18 printf("\n");
19 return 0;
20 }
 Le premier exemple ache 42 0 0 0 sur mon ordinateur, little endian 64bits.
 Le second exemple ache d6    sur mon ordinateur, car les nombres
négatifs sont codés en complément à 2. Cad 42 + -42 = 0 ; 0xd6 = 214 ; 214 + 42 =
256 ce qui fait une retenue pour mettre tous les 0xf f à zéro
 On s'embête à ça pour encoder une valeur de plus
 sizeof de l'ensemble : 4 (comme un int)
 C'est donc assez pratique pour aller explorer la mémoire à l'échelle de l'octet
 Autre usage des unions C : Faire une variable générique, comme en python. Mais il
faut stocker le type de l'objet à coté de sa valeur. Typiquement juste au début de la
structure*

III.2) Champs de bits


 Il est impossible en C de faire une variable scalaire dont la taille soit inférieure à un
octet. Le plus petit type est char sur un octet.
 Au sein d'une structure, on peut déclarer un champ de bits. C'est un type entier dont
on précise la taille.

1 struct A { struct B { 6
2 int bool1; int bool1 : 1; 7
3 int bool2; int bool2 : 1; 8
4 }; }; 9

 La structure A pèse deux fois plus lourd que la structure B : A :8 B :4 sur ma machine.
Ça ne fait pas un seul octet pour autant, car le compilateur utilise un entier pour porter
le tout.
 Le type de base du champ de bits (celui de bool1 par exemple) peut être int, signed
int ou unsigned int. Cela change l'interprétation des bits du champ
 Le plus grand champ de bits que je peux créer sur mon ordinateur a une largeur de
32 bits. C'est un 64bits, sizeof(int)=4 ici. Les big ints ne sont pas oerts par le
40 SÉANCE 5. MAÎTRISER LA PROGRAMMATION C

compilateur (sauf uint128_t). Il faut utiliser une bibliothèque.


 Si l'un des éléments n'a pas de nom mais seulement une largeur, c'est du remplissage.
Mais c'est dangereux de faire ça car la norme n'impose pas au compilateur de faire
quelque chose de logique en mémoire. Il est libre de représenter les champs de bits
comme il veut.
 Impossible de faire un pointeur vers un champ de bits, bien sûr

IV) Modèles d'organisation d'un code C


IV.1) Procédural
 Programmer procédural en C
 C'est la façon historique
 On organise des modules où toutes les fonctions qui vont ensemble sont dans le
même chier
 L'état est dans des globales cachées (déclarées globales static dans un module)
 L'interface est dans un chier d'entête
 cette approche est has been pour de bonnes raisons (mauvaise encapsulation, impos-
sible d'avoir deux instances du module, état caché dicile à tester)

IV.2) Orienté objet


 Programmer orienté objet en C
 État placé dans une structure. Fonctions prennent un pointeur vers l'instance en
premier argument
 Pas si éloigné du python avec son self, si on regarde pas de trop près
 On peut faire de l'encapsulation et du dispatch (avec des pointeurs sur fonction)
assez facilement
 On peut aussi faire de l'héritage, mais faut savoir arrêter et passer au C++ (ou
Rust !)
 L'habitude en C est de faire de l'OO à la sauce C
 La gestion mémoire reste à la charge de l'appelant, qui peut soit faire malloc soit
les garder sur la pile.
 La diérence est de savoir si on fait une fonction new qui malloc, ou bien init qui
met la bonne valeur dans les champs
 Vous allez sans doute voir les deux variantes dans les codes C que vous allez lire

IV.3) Le module point en détail


 Alias de type avec typedef pour raccourcir par rapport au très valide struct point*
 Constructeur, destructeur, copy constructeur ; Des fonctions de manipulation
 Le contenu de la structure et l'implem des méthodes est caché, dans un chier à part
V). CODE LISIBLE ET PROPRE 41

 Le compilo n'a pas besoin de connaître la structure car on manipule un pointeur


vers de taille connue
 Le chier d'entête donne le prototype de ces méthodes
 Inclus dans le chier d'implem pour vérier que le prototype correspond à la vraie
dénition
 Inclus dans les chiers appelants pour annoncer le prototype au compilo
 Attention, il faut protéger contre la double inclusion (crétin de compilo)

V) Code lisible et propre


 L'IOCCC est l'exemple à ne pas suivre.
 La programmation n'est pas une question de technologie. Il s'agit d'exprimer vos idées
de façon précise et ecace. [[Link]
 Dans une large mesure, l'acte de coder est un acte d'organisation. Refactorisation.
Simplier. Comprendre comment supprimer les manipulations superues ici et là. [http:
//[Link]/[Link]]

V.1) Code lisible


V.1.a) Commentaires
 Mauvais commentaires :
 Les commentaires ne doivent pas paraphraser le code. D'autant que les commentaires
sont rarement mis à jour en même temps que le code, donc risque d'obsolescence.
 Si le code est très mauvais, il faut le réécrire, pas le commenter
 Les commentaires doivent expliquer le code
 donner l'intention du code, les présupposés et les invariants
 Expliquer l'objectif d'un chier donné, pourquoi on a groupé ici ce qui est dedans
 Il faut aussi indiquer la licence et le copyright en haut du chier
 surprises lors de l'implémentation et tentatives précédentes (par exemple sur les
perfs)
 expliciter les choix sous-jacents, par exemple sur la valeur des constantes
 reconnaître les problèmes en donnant des pistes d'amélioration
 TODO, HACK, FIXME (qqch cassé), XXX (gros problème)
 Se mettre à la place du lecteur
 Si le code fait qqch étonnant, il faut documenter la raison de ce choix
 Un commentaire de chier doit donner une vue d'ensemble, expliquer la raison de
ce découpage du projet.
 Résumer des blocs de code un peu longs
 Les commentaires doivent être compacts et informatifs, sans ambiguité
 Des exemples de paramètres / résultats sont précieux
42 SÉANCE 5. MAÎTRISER LA PROGRAMMATION C

V.1.b) Identicateurs
 Les identicateurs doivent être bien choisis. S'il faut expliciter dans les commentaires
ce que fait un paramètre, peut-être qu'il faudrait plutôt le renommer.
 Les booléens devraient avoir is ou has dans leur nom pour être explicite. disable_x=0
est moins lisible que use_x=1
 Une fonction est un outil pour l'utilisateur. Il faut donc que son nom parle à l'utilisa-
teur, pas seulement à son auteur. GrahamAlgorithm est un nom moins explicite que
EnveloppeConvexe

V.1.c) Mise en page


 La mise en page est importante pour la lisibilité
 Indentation consistante. Utilisez clang-format pour cela
 Faites des petits blocs comme on fait des paragraphes.
 Réduisez le scope des variables
 Documentez le bloc

V.2) Code propre


 Si le code est compliqué, il n'est pas digne de conance et il faut le remplacer par qqch
de plus simple si possible. On parle de changer la logique.
 Le découpage du projet en un ensemble bien pensé de fonctions et modules
 Les ingénieurs informaticiens n'emboutissent pas du métal, mais ils forment des
concepts-outils
 On a tous une taille d'espace de stockage dans le cerveau et on ne peut pas ren-
trer plus gros. On veut donc comprendre chaque brique en entier, puis comprendre
l'assemblage des briques prises comme éléments atomiques
 Dupliquer les choses est la pire idée.
 Moto classique de programmeur : DRY/SPOT. Don't repeat yourself / Single point
of truth
 DRY = ne jamais dupliquer du code. Si le code doit être corrigé ou modié, il faudra
le faire dans chacune des copies
 Ne jamais aggraver avec CtrlC/CtrlV ; toujours chercher à mettre le code en
facteur commun pour réduire la duplication de code.
 Le pire, c'est un code qui a été créé par CtrlC/CtrlV puis les copies ont divergé
légèrement, de façon dicile à comprendre mais incompatibles entre elles. L'enfer
à relire / comprendre / faire évoluer
 SPOT = on se débrouille pour que les valeurs aient des noms. Par exemple, au lieu
d'écrire 640 partout où on a besoin de la taille de l'écran, on fait une constante
nommée en début de programme avec cette valeur, et on l'utilise ensuite.
 Avantage1 : le code est plus lisible ainsi, on comprend le pourquoi des calculs
numériques.
V). CODE LISIBLE ET PROPRE 43

 Avantage2 : s'il faut modier la taille de l'écran, on va avoir un moment très


dicile si les constantes numériques n'ont pas été nommées correctement
 Anecdote : Quand nvidia a voulu faire un driver open source pour rentrer dans
le monde linux, mais sans perdre la main sur le code (s'assurer qu'ils étaient
les seuls à pouvoir le faire évoluer), ils ont diusé une version obfusquée où les
constantes étaient inlinées (nom remplacé par la valeur) et les premiers calculs
déjà faits pour cacher les valeurs. Cela faisait un code profondément dicile
à lire/modier. Ils ont longtemps prétendu qu'il s'agissait eectivement de la
"preferred form for modication" (en référence à ce que la GPL impose de diuser
en guise de code source), jusqu'à ce que la communauté développe un autre pilote
par rétroengineering.
 L'objectif est de regrouper ensemble les traitements qui vont ensemble sans fragmenter
les fonctionnalités partout dans code (shotgun design)
 Simplier le ow exécutif
 C'est pour ça qu'on n'utilise plus de labels et goto en C (même si le compilo nous
laisserait faire)
 On évite les pièges idiots et les surprises pour lecteurs inattentifs
 Préférer if (a < b) à la version à l'envers if (b > a)
 S'il y a if/then, la condition du if ne doit pas avoir de négation
 Préférez if (!cond) return; à un niveau d'indentation supplémentaire
 Évitez les indentations trop profondes, qui demandent d'avoir beaucoup de code en
tête
 Simplier les expressions, en nommant les variables intermédiaires
 Eliminer les choses inutiles. Si supprimer quelque chose rend le code plus compact et
facile à lire, il faut le faire
 Les variables et fonctions inutilisées sont vraiment à désherber
 Supprimer les variables temporaires, les variables de ot (done)
 Inliner les fonctions utilisées à un seul endroit (sauf si le découpage aide à la lecture)
 De toute façon, le compilateur va redécouper notre code en fonction de l'architecture.
Rien ne sert d'essayer de l'aider.
 Les variables constantes sont préférables : le lecteur peut avoir conance dans leur valeur
sans vérier. Mutability seen as an antipattern
 Éviter l'overengineering en n'écrivant que le code immédiatement nécessaire
 Pas besoin de couvrir un cas qui ne peut pas se produire dans ce projet
 Il faut supprimer du code devenu inutile, comme on désherbe, car un petit codebase
est moins complexe
 Il faut utiliser les bibliothèques existantes au lieu de réinventer la roue

V.3) Pièges
 Optimisation = mauvaise idée, ca complique et faut bien comprendre pour être sûr que
ça gagne. Donc on n'optimise que un code déjà écrit, et après proling. Jamais à priori.
44 SÉANCE 5. MAÎTRISER LA PROGRAMMATION C

 Rule 1 : Optimization : don't do it


 Rule 2 (experts only) : Optimization : don't do it yet.
 Premature optimization is the root of all evil. Knuth.
 Code golf : vouloir le faire en moins de caractères. Cette performance n'aide pas le
lecteur
 On ne veut pas réduire le nombre de lignes, mais le temps pour qu'un lecteur com-
prenne
 Garder la première version. Il est souvent nécessaire de refactorer son code pour qu'il
devienne lisible.
 On refactore SimGrid depuis 20 ans, et c'est pour ça qu'on arrive à faire autant
 Refactorer, c'est comme désherber son jardin ou remettre ses idées au propre
 Mais on risque de casser des trucs en changeant. D'où l'intérêt de tester.

V.4) Tests (reporté au semestre suivant)


 Un code bien testé est facile à modier, par exemple pour le refactorer et le rendre plus
propre
 En plus, un code facile à tester est souvent un code plus simple à comprendre car il
n'a pas d'état caché
 Mais attention, les tests sont du code eux aussi. Il faut qu'ils soient lisibles et de
qualité. Bien nommés, documentés, etc.
 Vocabulaire :
 Erreur : comportement incorrect ; Faute : cause de l'erreur
 Tests en boite blanche (écrit après lecture du code source), boite noire (écrit à
l'aveugle)
 Tests unitaire (= par fonction) ; tests d'intégration (tout le projet en même temps)
 Tests fonctionnels, tests de performance, tests de régression (éviter que les bugs
reviennent lors des évolutions futures)
 Mocking de composants (abstraction du composant pour tester le reste en isolation),
stub (développement d'un étayage temporaire pour faire fonctionner le reste le temps
que le vrai code soit développé)
 Alpha testing (en cours de dev), beta testing (validation après dev mais avant release)
 Générer les bons tests est dicile, demande de l'expérience voire une expertise spécique
 Stratégie pour aider les développeurs débutants : Right + BICEPS
 Est-ce que le résultat est correct ? Right
 B : boundary. Conditions aux limites, la base du test en boite blanche.
 Valeurs à la limite de l'intervalle possible pour chasser les o-by-one, ou bien
au delà pour chasser les dépassements de capacité.
 Valeurs mal formées. Ou pas de valeur du tout dans la liste.
 Duplicata, Ordre invalide,
 I : inverse relationship. Si j'ajoute un élément, je le retrouve.
 C : cross-check. Vérier le résultat du test avec une autre méthode, même si
VI). COMPILATION SÉPARÉE 45

l'autre méthode est trop inecace pour être utilisée en prod.


 E : Force error conditions. Écrire des torture tests pour le code, comme le Chaos
Monkey d'Amazon. Dicile de faire des torture tests permettant non seulement
de détecter mais en plus de diagnostiquer / comprendre le pb. Souvent, il y a
trop de hasard et de bruit pour comprendre la cause.
 P : performance, à tester aussi.
 S : Specic language. Faire un petit langage dédié (DSL) documentant ce que
le test fait dans un formalisme compact peut être utile. Rééchir à ce langage
demande de rééchir à ce qu'il faut tester, et ensuite il est plus facile de tester
tous les cas de façon combinatoire.
 Test Driven Development : stratégie très ecace pour écrire le code qui a l'interface
dont on a besoin, en écrivant d'abord les tests qui ressemblent au code appelant. Les
outils permettent de générer les stubs de code à écrire ensuite.
 Design by contract vs. defensive programming. Il faut vérier les présupposés, mais pas
tout le temps car cela prend du temps. En plus c'est du code écrit, et on pourrait se
tromper dans les vérications de présupposés, ou bien il faudra les maintenir.
 Quelques framework de tests :
 En C, j'ai fait un petit framework simple pour vous
 En C++, nous utiliserons Catch2
 Beaucoup de recherche en tests
 QuickCheck : assertions are written about logical properties that a function should
fulll. Then QuickCheck attempts to generate a test case that falsies such asser-
tions.
 fuzzing : faire varier les données en entrée en respectant une syntaxe
 model checking : faire varier l'ordre des opérations concurrentes

VI) Compilation séparée

 Un seul chier de 500 000 lignes, c'est dur à lire, naviguer ; long à compiler ; dur à
collaborer
 Mais 5 chiers séparés, c'est dur à compiler. Il faut savoir quoi recompiler quand. Tran-
sitif inclusions.
 Il ne faut pas essayer de compiler à la main, mais il faut utiliser un outil pour ça
 make est parfait pour compiler : il a un arbre et gère tout bien. Mais dur d'écrire un
Makefile sans oublier la moindre dépendance. Cela demande de lire tous les sources
pour ne rien oublier, c'est un travail de robot, pas d'humain.
 cmake converti un chier [Link] à peu près lisible en Makefile très bien fait.
Mais interdit à l'agreg, donc il faut maîtriser les deux formalismes
46 SÉANCE 5. MAÎTRISER LA PROGRAMMATION C

VII) Conclusion sur le module


 L'objectif était surtout de vous faire comprendre comment fonctionne la machine au
plus bas niveau, quand on a même pas de libc pour nous aider
 Mais vous avez maintenant une bonne compréhension du langage C, en prime
 Il y a énormément de ressources sur C et UNIX pour aller plus loin. Le plus dicile est
de trouver une ressource qui vous convient. La liste suivante est volontairement courte,
pour vous permettre d'aller droit au but.
 Les livres de Claude Delannoy sont très bien pour apprendre à programmer, en C
comme en C++.
 "Programmer en Langage C : Cours et exercices (édition noire)" est un bon livre
pour apprendre, si vous n'avez pas tout compris
 "Le guide complet du langage C (édition blanche)" est un bon guide de référence,
maintenant que vous savez programmer.
 Apprendre à programmer en C sur OpenClassroom. Particulièrement intéressant
pour les débutants, mais moins bien que le livre pour débutants de Delannoy je crois.
[Link]
 Modern C par Jens Gustedt. Particulièrement intéressant pour les programmeurs
conrmés, mieux que le Delannoy correspondant. [Link]
[Link]/modern-c/
 Polycopié de C de l'ENSIMAG [Link]
 Les wikibooks Programmation C et C programming . Il s'agit de livres de réfé-
rence incomplets, c'est assez frustrant. N'hésitez pas à les améliorer si vous pouvez.
[Link]
 Voici quelques liens supplémentaires, plus amusants qu'indispensables :
 Clean code Présentation amusante sur pourquoi et comment programmer pro-
prement. [Link]
 Wat , une présentation sarcastique et hors sujet sur les horreurs du javascript.
Juste pour rire. [Link]
 Après ce demi-module, on peut partir dans plusieurs directions
 le réseau, à partir de la semaine prochaine, pour découvrir un gros système cohérent,
cf. le poly spécique
 le C++, le semestre prochain, pour avoir de l'aide du compilateur pour organiser de
gros codes
 l'architecture, le semestre prochain, pour mieux comprendre comment fonctionne un
vrai processeur
 le système, l'an prochain, pour voir comment fonctionne l'OS, et comment sont
implémentés les OS
 le module HPC, pour parler des performances des ordinateurs et l'impact de l'infor-
matique sur la vraie vie

Vous aimerez peut-être aussi