0% ont trouvé ce document utile (0 vote)
2 vues33 pages

Résumer

Le document présente une synthèse exhaustive sur l'architecture des ordinateurs, définissant un ordinateur comme une machine programmable qui exécute des instructions pour traiter des données. Il aborde les différentes catégories de systèmes informatiques, les composants fondamentaux tels que le CPU, la mémoire et les périphériques, ainsi que les architectures Von Neumann et Harvard. De plus, il explore des concepts clés comme le fonctionnement interne du CPU, la hiérarchie de la mémoire, l'encodage numérique et la machine de Turing, tout en soulignant les implications théoriques et pratiques de ces éléments dans le domaine de l'informatique.

Transféré par

emiedupuise123
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 DOCX, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
2 vues33 pages

Résumer

Le document présente une synthèse exhaustive sur l'architecture des ordinateurs, définissant un ordinateur comme une machine programmable qui exécute des instructions pour traiter des données. Il aborde les différentes catégories de systèmes informatiques, les composants fondamentaux tels que le CPU, la mémoire et les périphériques, ainsi que les architectures Von Neumann et Harvard. De plus, il explore des concepts clés comme le fonctionnement interne du CPU, la hiérarchie de la mémoire, l'encodage numérique et la machine de Turing, tout en soulignant les implications théoriques et pratiques de ces éléments dans le domaine de l'informatique.

Transféré par

emiedupuise123
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 DOCX, PDF, TXT ou lisez en ligne sur Scribd

RÉSUMÉ DÉTAILLÉ – ARCHITECTURE DES ORDINATEURS

1. QU’EST-CE QU’UN ORDINATEUR ?

Un ordinateur est une machine électronique programmable capable


d’exécuter une suite d’instructions (programme) stockées en mémoire
afin de traiter des données.

Il fonctionne uniquement avec des signaux électriques représentant


des bits (0 ou 1). Toute information (nombre, texte, image, son,
instruction machine) est encodée sous forme binaire.

Un ordinateur transforme donc :

Programme (abstrait, écrit en C++ par exemple)


→ Instructions machine (binaire)
→ Signaux électriques
→ Actions physiques (affichage, moteur, communication)

1.1 Grandes catégories de systèmes

1) Microprocesseur (CPU seul)

Un microprocesseur :

 Exécute les instructions

 Ne contient PAS de mémoire interne importante

 Nécessite :

o RAM externe

o Mémoire de stockage

o Contrôleurs I/O

o Alimentation régulée

On le retrouve dans :

 PC

 Serveurs

 Téléphones (SoC plus complexe)


Caractéristique principale : usage général.

2) Microcontrôleur (MCU)

Un microcontrôleur intègre sur une seule puce :

 CPU

 RAM

 Mémoire Flash

 Périphériques (ADC, timers, UART, SPI, I²C)

Avantages :

 Faible consommation

 Faible coût

 Taille compacte

 Très prédictible en temps réel

Exemple : ATmega328P (Arduino Uno)

3) Systèmes embarqués

Un système embarqué est un système informatique conçu pour


une tâche précise.

Exemples :

 ABS automobile

 Lave-vaisselle

 Robot industriel

 Contrôleur de moteur

Caractéristiques :

 Optimisé pour une fonction

 Ressources limitées

 Forte contrainte temps réel


1.2 Les 3 composantes fondamentales

Tout système informatique contient :

1) CPU (Unité centrale)

Rôle :

 Récupère instruction en mémoire

 La décode

 L’exécute

Il contient :

 Registres

 ALU

 Unité de contrôle

 Compteur de programme (PC)

2) Mémoire

Stocke :

 Programme

 Données

Types :

 Registres (ultra rapide)

 Cache

 RAM

 Flash

 SSD

3) Périphériques

Interface avec le monde réel :


Entrées :

 Capteurs

 Boutons

 Réseau

Sorties :

 LED

 Moteur

 Écran

4) Les bus

Trois bus principaux :

Bus d’adresses

Permet au CPU de sélectionner une case mémoire.

Bus de données

Transporte les données.

Bus de contrôle

Indique :

 Lecture

 Écriture

 Interruption

1.3 Architectures : Von Neumann vs Harvard

Architecture Von Neumann

 Une seule mémoire pour code et données

 Un seul bus mémoire

Avantages :

 Flexible
 Code auto-modifiable

Inconvénient :

 Goulot d’étranglement (CPU ne peut pas lire instruction ET


donnée simultanément)

Architecture Harvard

 Mémoire code séparée

 Mémoire données séparée

 Bus séparés

Avantages :

 Accès parallèle

 Plus rapide

 Plus sécuritaire

Inconvénient :

 Moins flexible

Harvard modifiée (AVR)

 Flash → programme

 SRAM → données

 Instructions spéciales (LPM) pour lire Flash

2. BINAIRE ET HEXADÉCIMAL

Bits et octets

1 bit = 0 ou 1
8 bits = 1 octet

1 octet = 2⁸ = 256 valeurs possibles


Conversion binaire → décimal

Exemple :
10110100₂

= 1×2⁷ + 0×2⁶ + 1×2⁵ + 1×2⁴ + 0×2³ + 1×2² + 0×2¹ + 0×2⁰


= 128 + 32 + 16 + 4
= 180

Hexadécimal

Base 16 :
0–9, A–F

1 chiffre hex = 4 bits

1011₂ = B
0100₂ = 4

10110100₂ = B4₁₆

Pourquoi utiliser l’hex ?

 Compact

 Lisible

 Correspond directement aux bits

Exemple :
Adresse 32 bits :
0x0800FFFC

3. CPU – FONCTIONNEMENT INTERNE

3.1 Cycle Fetch-Decode-Execute

Chaque instruction passe par :

1. Fetch → lire instruction mémoire

2. Decode → interpréter
3. Execute → effectuer opération

4. Write-back → écrire résultat

Le compteur de programme (PC) contient l’adresse de la prochaine


instruction. (Est un pointeur)

3.2 Jeu d’instructions (ISA)

Définit :

 Instructions disponibles

 Format binaire

RISC

 Instructions simples

 Exécution rapide

 Exemple : ARM, AVR

CISC

 Instructions complexes

 Exemple : x86

3.3 Registres

 Mémoire interne ultra-rapide

 AVR : 32 registres 8 bits

Pourquoi importants ?
Accès RAM = plusieurs cycles
Accès registre = 1 cycle

Toute opération doit passer par registres.

3.4 ALU

Effectue :

 Addition
 Soustraction

 AND

 OR

 XOR

 Décalages

Met à jour les flags :

Z → zéro
C → retenue
N → négatif
V → overflow

3.5 Horloge

Fréquence = nombre de cycles par seconde

Arduino Uno = 16 MHz


PC moderne = >5 GHz

Une instruction peut prendre plusieurs cycles.

3.6 Pipeline

Découpe instruction en étapes :

Fetch
Decode
Execute
Memory
Write-back

Permet exécution parallèle partielle.

Améliore débit mais complexifie gestion branchements.

3.7 Stack et mémoire programme

Stack (pile)
Contient :

 Variables locales

 Adresse de retour

 Contexte

Croît vers le bas.

Heap (tas)

Allocation dynamique :
malloc()
free()

Danger :
Stack et heap peuvent entrer en collision.

4. MÉMOIRE

4.1 Hiérarchie mémoire

Du plus rapide au plus lent :

Registres
Cache
RAM
SSD

Compromis :
Vitesse ↔ Capacité ↔ Coût

AVR :

 Pas de cache

 Comportement déterministe

4.2 Espaces mémoire AVR

Mémoire données :

 0x0000–0x001F → registres
 0x0020–0x005F → I/O

 0x0100–0x08FF → SRAM

Mémoire programme :

 Flash séparée

5. ENCODAGE NUMÉRIQUE

5.1 Endianness (Boutisme)

Little-endian

Octet faible en premier


Intel, ARM, AVR

Big-endian

Octet fort en premier


TCP/IP

Reconstruction portable :

Little-endian :

uint16_t v = buffer[0] | (buffer[1] << 8);

Big-endian :

uint16_t v = (buffer[0] << 8) | buffer[1];

5.2 IEEE 754 – Float

Structure 32 bits :

1 bit signe
8 bits exposant
23 bits mantisse

Valeur :
(-1)^s × 1.m × 2^(exp - 127)
Valeurs spéciales :

Exp = 255, mantisse = 0 → infini


Exp = 255, mantisse ≠ 0 → NaN

Float ≈ 7 chiffres significatifs

Ne jamais comparer avec ==


Toujours utiliser epsilon.

5.3 ASCII et UTF-8

ASCII :

 7 bits

 128 caractères

UTF-8 :

 1 à 4 octets

 Compatible ASCII

Attention :
Nombre de caractères ≠ nombre d’octets en UTF-8

6. PÉRIPHÉRIQUES

6.1 Principe général

Le CPU interagit avec périphériques via registres de contrôle.

Lire/écrire dans ces registres configure et contrôle le périphérique.

6.2 Protocoles

UART :

 Asynchrone

 Bits start/stop

SPI :

 Synchrone
 Rapide

I²C :

 Bus 2 fils

 Synchrone

 Adressable

6.3 Memory-mapped I/O

Registres périphériques placés dans espace mémoire.

Écriture à une adresse spéciale = commande matériel.

AVR :

 Hybrid :

o IN/OUT rapides

o Memory-mapped

7. Machine de Turing – Fondement théorique de l’informatique

La machine de Turing, proposée par Alan Turing en 1936, est un


modèle mathématique abstrait servant à définir formellement ce
qu’est un calcul.

Elle ne représente pas un ordinateur réel, mais un modèle théorique


permettant de comprendre les limites fondamentales du calcul.

7.1 Structure d’une machine de Turing

Elle est composée de :

1) Un ruban infini

 Divisé en cases

 Chaque case contient un symbole (0, 1 ou symbole vide)

 Représente la mémoire
Contrairement à la RAM moderne (accès aléatoire), la machine de
Turing accède au ruban de manière séquentielle.

2) Une tête de lecture/écriture

Elle peut :

 Lire le symbole courant

 Écrire un symbole

 Se déplacer à gauche ou à droite

Elle représente le processeur (CPU).

3) Un ensemble fini d’états

La machine possède :

 Un nombre fini d’états internes

 Une table de transition

Chaque règle indique :

SI (état courant, symbole lu)


ALORS (écrire symbole, déplacer tête, changer d’état)

Exemple simple de transition

État q0, lit 1 →

 écrire 0

 aller à droite

 passer à état q1

C’est l’équivalent d’une instruction machine.

Pourquoi c’est fondamental ?

Alan Turing a démontré que :


Toute fonction calculable peut être exécutée par une machine de
Turing.

C’est la thèse de Church-Turing.

Cela signifie :

 Tout algorithme programmable

 Tout logiciel moderne

 Tout microcontrôleur

 Tout PC

Peut être modélisé comme une machine de Turing.

Équivalence avec un ordinateur moderne

Ordinateur
Machine de Turing
réel

Ruban RAM

Tête de lecture CPU

États internes Registres

Table de transition Programme

Déplacement Incrémentation
gauche/droite du PC

La différence majeure :

 Machine de Turing → accès séquentiel

 Ordinateur moderne → accès aléatoire (RAM)

Cela améliore énormément la performance, mais ne change pas la


puissance théorique.

Machine de Turing universelle

Turing a aussi défini une machine capable de simuler toutes les autres
machines de Turing.
C’est le concept fondamental du :

➡ Ordinateur programmable universel

Cela signifie :

 Le matériel ne change pas

 Seul le programme change

Un Arduino, un PC ou un superordinateur sont tous universels.

Limites du calcul – Problème de l’arrêt

Turing a démontré qu’il est impossible d’écrire un programme capable


de déterminer, pour tout autre programme, s’il va s’arrêter ou non.

C’est le problème de l’arrêt (Halting Problem).

Conséquence :
Il existe des problèmes mathématiquement indécidables.

Cela montre que :


Même les ordinateurs parfaits ont des limites fondamentales.

Complexité et ressources

Même si un problème est calculable (Turing-complet), il peut être :

 Trop long à calculer

 Nécessiter trop de mémoire

Exemple :
Certains problèmes exponentiels deviennent impossibles à résoudre en
pratique.

Donc :
Calculable ≠ faisable efficacement.

Pourquoi c’est important en architecture ?

La machine de Turing permet de comprendre que :

1. Tous les processeurs peuvent exécuter les mêmes algorithmes.


2. Les différences d’architecture influencent seulement :

o La performance

o La consommation

o L’efficacité

Mais pas la puissance théorique.

Lien avec les systèmes embarqués

Même un microcontrôleur 8 bits comme l’AVR est Turing-complet si :

 Il a suffisamment de mémoire

 Il peut exécuter des boucles

Cela signifie qu’en théorie :


Un Arduino peut exécuter n’importe quel algorithme, mais plus
lentement qu’un PC.

8. Machine est little-endian et big-endian

bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0

0 1 0 1 0 1 1 0

bit0 = bit le moins significatif (LSB)

bit7 = bit le plus significatif (MSB)

0x12345678

0x12 = octet de poids fort (MSB)

0x78 = octet de poids faible (LSB)

Le poids faible correspond à la partie droite du nombre.

Exemple little-endian :

La machine est little-endian car…

La valeur est : 0x12345678


En mémoire, on observe :

[0] = 0x78

[1] = 0x56

[2] = 0x34

[3] = 0x12

C’est la définition du little-endian : L’octet le moins significatif est


stocké en premier en mémoire.

Exemple Big-endian:

La valeur est : 0x12345678

En mémoire, on observe :

[0] = 0x12

[1] = 0x34

[2] = 0x56

[3] = 0x78

Big-endian signifie l’octet de poids fort (Most Significant Byte – MSB)


est stocké en premier en mémoire.

Laboratoire :
a) Que signifie #include <iostream> ?
La directive #include <iostream> indique au compilateur d’inclure la
bibliothèque standard d’entrée/sortie du C++.
La bibliothèque iostream permet d’utiliser des objets comme std::cout
(affichage à l’écran) et std::cin (lecture au clavier).
Sans cette ligne, le compilateur ne reconnaîtrait pas std::cout.

b) À quoi sert return 0; à la fin de main ?


L’instruction return 0; indique que le programme s’est terminé correctement.
La valeur 0 signifie qu’il n’y a eu aucune erreur lors de l’exécution.
Si une autre valeur est retournée (ex. return 1;), cela peut indiquer une erreur.
c) Est-ce qu’il est possible d’écrire la ligne std::cout … sans la mettre dans la
fonction main ? Pourquoi ?
Non, ce n’est pas possible. En C++, toutes les instructions exécutables doivent se
trouver à l’intérieur d’une fonction. La fonction main est le point d’entrée du
programme : c’est elle qui démarre l’exécution. Si on écrit std::cout en dehors
d’une fonction, le programme ne compilera pas, car les instructions ne peuvent
pas être placées directement dans la zone globale.

d) Quelle est la différence entre int16_t et uint16_t ?


int16_t est un entier signé sur 16 bits.
uint16_t est un entier non signé sur 16 bits.
La différence principale est donc que int16_t accepte les nombres négatifs,
tandis que uint16_t accepte seulement des nombres positifs.

e) Pourquoi utilise-t-on <cstdint> plutôt que simplement int ?


La bibliothèque <cstdint> permet d’utiliser des types de taille fixe comme
int16_t, uint16_t, int32_t, etc.
Le type int n’a pas une taille garantie : selon le système, il peut être 16 bits, 32
bits ou même 64 bits.
En utilisant <cstdint>, on s’assure que le type a exactement le nombre de bits
voulu.

f) Que signifie le f après 23.5f ?


Le f indique que la valeur est de type float (nombre réel en simple précision).
Sans le f, le nombre 23.5 est considéré par défaut comme un double (double
précision). Donc 23.5f signifie que la constante est explicitement un float.

g) Pourquoi faut-il caster octet en (int) pour l’affichage en hexadécimal ?

Le type uint8_t est généralement défini comme un alias de unsigned char.


Or, lorsqu’on affiche un unsigned char avec std::cout, il est interprété comme
un caractère ASCII et non comme un nombre.

Donc, si on écrit :

std::cout << std::hex << octet;

le programme risque d’afficher un caractère (par exemple ´´´´) au lieu de la valeur


numérique en hexadécimal.

En faisant un cast :

(int)octet
on force la conversion vers un entier (int).
Ainsi, std::cout l’affiche comme une valeur numérique (par exemple b4) et
non comme un caractère.

h) Quelle est la taille réelle en mémoire obtenue avec sizeof ?

struct DonneesAxe {
int16_t position; // centièmes de degré
int16_t vitesse; // dixièmes de degré/seconde
uint16_t courant; // milliampères
};

Chaque membre est un entier de 16 bits.

 int16_t = 2 octets
 int16_t = 2 octets
 uint16_t = 2 octets

Total théorique : 2 + 2 + 2 = 6 octets

i) La taille de DonneesBras est-elle exactement sizeof(uint8_t) + 6 *


sizeof(DonneesAxe) ? Pourquoi ?

struct DonneesAxe {
int16_t position;
int16_t vitesse;
uint16_t courant;
};

struct DonneesBras {
uint8_t sequence; // numéro de trame
DonneesAxe axes[6]; // tableau de 6 axes
};

Calcul théorique :
 uint8_t sequence = 1 octet
 DonneesAxe = 6 octets
 6 axes = 6 × 6 = 36 octets
Total théorique :
1 + 36 = 37 octets
Non, car elle donne 38 octets à cause du padding.

0 1 2 3 4 5 6 7
⬜ ⬛ ⬜ ⬜ ⬜ ⬜ ⬜ ⬜ char + Padding + axe 1
⬜⬜⬜⬜⬜⬜ axe 2
⬜⬜⬜⬜⬜⬜ axe 3
⬜⬜⬜⬜⬜⬜ axe 4
⬜⬜⬜⬜⬜⬜ axe 5
⬜⬜⬜⬜⬜⬜ axe 6

j) Dessinez la disposition mémoire de chaque structure en indiquant les octets


de padding et Quelle version est la plus compacte ? Pourquoi ? Est-ce toujours
la plus performante ?

Le padding, ce sont des octets vides ajoutés automatiquement par le compilateur


dans une structure.

Pourquoi le processeur veut ça ?


Parce que :
 Accès aligné = 1 seul cycle mémoire
 Accès non aligné = plusieurs cycles
Donc sans padding :
 plus lent
 parfois même interdit sur certains processeurs embarqués

L’alignement veut dire :


👉 certains types doivent commencer à des adresses spécifiques.

Par exemple :

Doit commencer à une


Type Taille
adresse multiple de

uint8_t 1 1 (n’importe où)

uint16_t 2 2
Doit commencer à une
Type Taille
adresse multiple de

uint32_t 4 4

uint64_t 8 8

Exemple :

struct Test {
uint8_t a; // adresse 0
uint32_t b; // ?
};

Sans padding :
0:a
1:b
2:b
3:b
4:b
Mais 1 n’est pas multiple de 4 ❌

Donc le compilateur ajoute :

0:a
1 : padding
2 : padding
3 : padding
4:b
5:b
6:b
7:b
Maintenant b commence à 4 ✔

struct VersionA {
uint8_t a; // 1 octet
uint32_t b; // 4 octets
uint8_t c; // 1 octet
};

Octet 0 : a
Octets 1-3 : padding
Octets 4-7 : b
Octet 8 : c
Octets 9-11 : padding

 a occupe 1 octet
 3 octets de padding sont ajoutés pour aligner b sur un multiple de 4
 b occupe 4 octets
 c occupe 1 octet
 3 octets de padding sont ajoutés pour que la structure ait une taille multiple de 4
➡ Taille totale : 12 octets

struct VersionB {
uint8_t a; // 1 octet
uint8_t c; // 1 octet
uint32_t b; // 4 octets
};

Octet 0 : a
Octet 1 : c
Octets 2-3 : padding
Octets 4-7 : b

 a et c occupent 2 octets
 2 octets de padding sont ajoutés pour aligner b
 b occupe 4 octets
 La taille totale est déjà multiple de 4
➡ Taille totale : 8 octets
#pragma pack(push, 1)
struct VersionC {
uint32_t b;
uint8_t a;
uint8_t c;
};
#pragma pack(pop)

Explication :
Le #pragma pack(1) désactive le padding.
Les membres sont placés directement les uns après les autres.
➡ Taille totale : 6 octets

La version la plus compacte est VersionC (6 octets), car le #pragma pack(1)


supprime complètement le padding.

Le padding est performant pour augmenter vitesse. Annuler le padding sauve de la


mémoire mais ralenti programme.

k) Que se passe-t-il si on oublie delete[] ? (Exercice 3.4)


Si on oublie delete[], la mémoire allouée avec new[] n’est jamais libérée.
➡ Cela crée une fuite mémoire (memory leak).
o new → delete
o new[] → delete[]

1. Pourquoi doit-on caster en char* lors de l'utilisation de \`read()\` et \`write()\`


avec des données binaires ?

R : Pour éviter les erreurs de compilation

Les fonctions :

std::istream::read(char* s, std::streamsize n);


std::ostream::write(const char* s, std::streamsize n);
attendent un pointeur vers char.

Si tu as par exemple :
std::vector<uint8_t> buffer;
alors :
[Link]()
retourne un uint8_t*, pas un char*.

Le cast (ou \`reinterpret_cast\`) indique qu'on veut lire/écrire les octets bruts
de n'importe quel type.

2. Qu'affichera ce code ?

uint16_t valeur = 300;


uint8_t petit = valeur;
std::cout << (int)petit;

R : 44 car 8 bits ne peuvent contenir que des valeurs entre 0 et 255.

uint8_t a = 250;
uint8_t b = 10;
uint8_t c = a + b;
std::cout << (int)c;

R:4
-++
+++

3. Pourquoi doit-on caster uint8_t en int pour l'afficher en hexadécimal avec


std::cout ?

R : On caste uint8_t en int parce que uint8_t est traité comme un unsigned
char, et std::cout affiche les char comme des caractères ASCII, pas comme
des nombres.
4. Ce code produit potentiellement des résultats incorrects.
Pourquoi ?
struct Paquet {
uint8_t type;
uint32_t donnees;
};
uint8_t buffer[5] = {0x01, 0x12, 0x34, 0x56, 0x78};
Paquet* p = reinterpret_cast(buffer);
std::cout << std::hex << p->donnees;

Il faut ecrire : Paquet* p = reinterpret_cast<Paquet*>(buffer);

R : Ce code peut produire des résultats incorrects à cause du padding : le


compilateur peut ajouter des octets invisibles entre type et donnees pour
aligner le uint32_t sur 4 octets. Le reinterpret_cast lit au-delà des données
valides du buffer. Il faut #pragma pack(push, 1) ou équivalent pour une
structure de 5 octets.
Pourquoi faut “compacter” la structure ?
Parce que ton buffer contient exactement 5 octets, mais ta structure peut en
prendre 8 en mémoire.

Ici, pas besoin de nommer explicitement le "pragma pack", simplement


comprendre qu'il faut indiquer au compilateur que la structure doit être
"compactée".

5. Quelle est la différence fondamentale entre une conversion de type et une


réinterprétation de bits ?
Une conversion de type transforme une valeur vers un autre type (ex. int vers
float, la valeur est recalculée). Une réinterprétation de bits ne change pas les
bits en mémoire, elle change seulement la façon dont ils sont interprétés par le
programme. La réinterprétation est dangereuse car elle ne garantit pas un
résultat cohérent.

6. Qu'est-ce qu'un "file descriptor" (descripteur de fichier) ?


R : Un entier identifiant un fichier ouvert
7. Décrivez les trois étapes du cycle fetch-decode-execute.

Fetch (recherche) : Récupère l'instruction depuis la mémoire (ou cache)


Decode (décodage) : Il analyse l’instruction pour déterminer quelle opération doit
être effectuée et sur quelles données. (Sélectionne registres, configure ALU)
Execute (exécution) : Exécute l'opération dans l'ALU ou autre unité fonctionnelle.
Le compteur d'instructions (Program Counter) est incrémenté pour pointer vers
l'instruction suivante.

8. Décrivez la hiérarchie mémoire d'un ordinateur moderne, du plus rapide au


plus lent.
Registres → Cache (L1, L2, L...) → RAM → Stockage (SSD/HDD). Plus une
mémoire est rapide, plus elle est chère et petite. Cette hiérarchie optimise le
compromis entre vitesse, capacité et coût.

9. Quel type devrait-on utiliser pour stocker une valeur monétaire précise (ex:
prix en dollars) ?
R : On utilise un entier (ex. stocker 10,99 $ comme 1099 cents) pour éviter les
erreurs d’arrondi des nombres flottants, car float et double ne représentent pas
exactement les décimales en base 2.

10. Les chiffres ASCII ('0' à '9') sont-ils encodés de manière séquentielle ?
Pourquoi est-ce utile ?
C’est utile parce qu’on peut facilement convertir un caractère en entier avec
une simple opération comme : int chiffre = caractere - '0';

11. Pourquoi une chaîne UTF-8 ne compte-t-elle pas nécessairement le même


nombre d'octets que le nombre de caractères ?

R : Parce que UTF-8 est un encodage à longueur variable : un caractère


peut occuper de 1 à 4 octets selon le symbole (ex. lettres accentuées, emojis,
caractères asiatiques). UTF-8 est dominant pour tous les pays et toutes les
langues sur Internet, il est utilisé dans la plupart des standards, est souvent le
seul encodage autorisé et est pris en charge par tous les systèmes
d’exploitation et langages de programmation modernes.

12. Vous devez lire une structure de données de 5 octets depuis un fichier binaire
créé par un autre système. Votre structure C++ fait 8 octets à cause du
padding. Quelle approche devriez-vous utiliser ?
R : Forcer la structure à être compacte (sans padding) avec une directive de
compilation, OU lire les champs individuellement octet par octet plutôt que la
structure complète. Le padding automatique est utile pour la performance
mais incompatible avec les formats binaires externes définis.

13. Si ptr est un pointeur vers uint32_t et a la valeur 0x1000, quelle sera la valeur
de ptr + 3 ?
R : 0x100C
Parce que ptr pointe vers un uint32_t (4 octets).
Donc ptr + 3 avance de 3 × 4 octets = 12 octets → 0x1000 + 0xC = 0x100C.
Comme ptr pointe vers un uint32_t : 1 déplacement = 4 octets
Donc :
ptr + 3 = 3 × 4 octets = 12 octets
12 en hexadécimal = 0xC

14. Identifiez le problème dans ce code :


uint8_t* creer_buffer()
{
uint8_t buffer[100];
// ... initialisation ...
return buffer;
}
R : Le problème est que buffer est une variable locale créée sur la pile. Quand
la fonction se termine, cette mémoire est détruite, donc le pointeur retourné
pointe vers une zone invalide. Utiliser ce pointeur plus tard peut causer un
comportement imprévisible. Il faudrait allouer dynamiquement avec new[] ou
utiliser un std::vector. Les variables locales sont généralement allouées sur
la pile (stack), une zone mémoire gérée automatiquement qui grandit et
rétrécit au fil des appels de fonctions.

int* creer()
{
static int x = 5;
return &x;
}
R : code valide

std::vector<uint8_t> f()
{
std::vector<uint8_t> v(100);
return v;
}
R : code valide

15. Pourquoi utilise-t-on delete[] et non delete pour libérer un tableau alloué
avec new[] ?

R : Parce que new[] crée un tableau complet d’éléments, donc delete[] est
nécessaire pour libérer correctement tous les éléments, alors que delete ne
libère qu’un seul objet.

16. Quelle opération permet d'extraire le bit 5 (en comptant de 0) d'un octet ?
R : (octet & 0x20) >> 5

Parce que 0x20 correspond au bit 5 (0010 0000), on fait un AND pour l’isoler
puis on décale à droite de 5 pour obtenir 0 ou 1.
Oui, dans la plupart des cas on le ramène à droite (bit 0)
On fait ça pour obtenir simplement 0 ou 1, ce qui permet de l’utiliser
facilement dans une condition ou un calcul.
Extraire un bit sert à vérifier l’état d’un seul bit précis dans une variable.

17. Une trame contient un champ longueur indiquant le nombre d'octets de


données utiles. Où devrait-on placer ce champ ?
R : Le champ longueur doit être au début (après la synchro, si présente) pour
savoir combien d'octets lire avant de traiter la trame. Sinon, impossible de
connaître à l'avance la quantité d'octets à lire, à moins d'avoir un code de
synchronisation de fin unique.

18. Quel type de mémoire est utilisé pour l'interaction avec des périphériques
en utilisant des adresses mémoire ?
R : mémoire mappée en entrée/sortie

19. Quels sont les trois composants fondamentaux d'un système informatique
?
CPU Mémoire Périphérique

20. Quel composant matériel est responsable de la gestion des adresses


virtuelles en mémoire virtuelle?
R: Unité de gestion de mémoire (MMU)

21. Quel terme décrit un système informatique conçu pour une tâche
spécifique, souvent avec des ressources limitées ?
Réponse attendue: Système embarqué

22. Déterminer la taille d’un fichier


[Link](0, std::ios::end); // Aller à la fin
size_t taille = [Link](); // Position = taille
[Link](0, std::ios::beg); // Revenir au début

23. Espace mémoire d’un programme


 .text : code du programme
 .data : variables globales/statique initialisées
 .bss : variables globales/statique non initialisées
 stack (pile) : variables locales, appels de fonctions
 heap (tas) : mémoire dynamique

24. appels système (surtout sur PC/Linux)


Un appel système est une demande faite par un programme au système
d’exploitation pour faire une action que le programme ne peut pas faire
tout seul.
Le programme demande au système d’exploitation, par exemple, de :
 ouvrir un fichier
 écrire à l’écran
 lire le clavier
 utiliser le réseau
 accéder à un périphérique
 créer un processus

25. Quelle est la différence entre microprocesseur, microcontrôleur et système


 Microprocesseur : CPU seul.
 Microcontrôleur : CPU + mémoire + périphériques sur une puce.
 Système embarqué : système informatique pour une tâche précise.
26. C’est quoi la stack ? C’est quoi le heap ?
 pile : mémoire des variables locales et appels de fonctions.
 tas : mémoire allouée dynamiquement.
27. À quoi sert la compilation ? À quoi sert le linking ?
 Compilation : transforme le code source en code objet/machine.
 Linking : assemble les fichiers objets et bibliothèques en exécutable.
28. Quelle est la différence entre memory-mapped I/O et port-mapped I/O ?
 Memory-mapped I/O : périphériques accessibles comme des adresses
mémoire.
 Port-mapped I/O : périphériques accessibles par des ports spéciaux et
instructions spéciale
29. Protocoles

UART :

 Asynchrone
 Bits start/stop

SPI :

 Synchrone

 Rapide

I²C :

 Bus 2 fils

 Synchrone

 Adressable

30. Le CPU interagit avec périphériques via registres de


contrôle.
Lire/écrire dans ces registres configure et contrôle le
périphérique.

31. Fiche express à mémoriser


 CPU = exécute les instructions
 Mémoire = stocke code et données
 Périphériques = lien avec le monde extérieur
 Von Neumann = code et données ensemble
 Harvard = code et données séparés
 Registres = mémoire la plus rapide
 ALU = fait les calculs
 Stack = variables locales, appels de fonctions
 Heap = mémoire dynamique
 Pointeur = adresse mémoire
 Référence = alias d’une variable
 Struct = groupe de variables
 Padding = octets ajoutés pour alignement
 Little-endian = octet faible en premier
 IEEE 754 = format des floats
 ASCII = encodage simple de caractères
 Compilation = code source → objet
 Linking = objets → exécutable

32. Interruptions
Un événement matériel interrompt le CPU pour traiter quelque chose
d’urgent.
Exemple : réception série, bouton pressé, minuterie
33. Memory-mapped I/O : Les registres des périphériques sont placés à des
adresses mémoire précises.

34. uint8_t buffer[10] = {0xAA, 0x55, 0x01, 0x02, 0x03};

uint8_t* ptr = buffer; // ptr pointe vers buffer[0]


std::cout << *ptr; // Affiche : 0xAA (170 en décimal)
std::cout << *(ptr + 1); // Affiche : 0x55 (85 en décimal)
std::cout << ptr[2]; // Affiche : 0x01 — notation équivalente

35. Alias :
int valeur = 42;
int& ref = valeur; // ref est un alias de valeur
ref = 100; // Modifie valeur
std::cout << valeur; // Affiche : 100 // Une référence ne peut pas être « réassignée
» int autre = 50; ref = autre; // Copie la valeur de autre dans valeur (via ref) // ref
réfère toujours à valeur, pas à autre

36. Exercice plus difficile – Machine de Turing


On considère la table de transition suivante :

Éta
Symb Nou
t Symb Déplace
ole vel
act ole lu ment
écrit état
uel

Q0 0 0 Droite Q0

Q0 1 1 Droite Q0

Q0 # # Gauche Q1

Q1 0 1 Gauche Q2

Q1 1 0 Gauche Q2

Q2 0 0 Gauche Q1

Q2 1 1 Gauche Q1

Q1 B B Aucun HAL
Éta
Symb Nou
t Symb Déplace
ole vel
act ole lu ment
écrit état
uel

HAL
Q2 B B Aucun
T

Ruban initial
B101100#
La tête commence sur le premier 1 en état Q0.

Question
Quel sera le contenu final du ruban après l’arrêt de la machine ?

Indice
 En Q0, la machine avance jusqu’à #
 Ensuite elle revient vers la gauche
 Mais cette fois, elle change un bit sur deux seulement
Solution :

Étapes
En partant du dernier bit :
 dernier 0 en Q1 → devient 1, puis gauche, état Q2
 0 en Q2 → reste 0, puis gauche, état Q1
 1 en Q1 → devient 0, puis gauche, état Q2
 1 en Q2 → reste 1, puis gauche, état Q1
 0 en Q1 → devient 1, puis gauche, état Q2
 1 en Q2 → reste 1, puis gauche, état Q1
 B → arrêt

B111001#

Vous aimerez peut-être aussi