3.0 KiB
3.0 KiB
Chapitre 2 — Environnements et portabilité
2.1 C hébergé vs C freestanding
Il existe deux modes d’exécution du C.
Un environnement hébergé fournit :
- un OS
- une libc complète
- un point d’entrée
main
#include <stdio.h>
int main(void)
{
printf("Hello\n");
return 0;
}
Un environnement freestanding est minimal.
void start(void)
{
// code bas niveau
}
2.2 Plateformes
Linux, Windows, macOS ont des APIs différentes.
Un programme C peut s’exécuter sur plusieurs systèmes.
Mais tous les systèmes sont différents.
- Linux
- API POSIX
- forte portabilité
- outils standards
- macOS
- basé sur UNIX
- proche de POSIX
- spécificités Apple
- Windows
- API spécifique (WinAPI)
- comportement différent sur certains points
- pas POSIX natif
- Android
- basé sur Linux
- libc spécifique (Bionic)
- contraintes mobiles
- Embarqué
- pas d’OS ou OS minimal
- ressources limitées
- accès direct au matériel
2.3 Architecture vs système
Un programme dépend de plusieurs couches.
Il faut distinguer :
- architecture (CPU)
- système (OS)
- compilateur
🔹 Architecture
Exemples :
- x86
- x86_64
- ARM
- RISC-V
Chaque architecture :
- a ses registres
- son alignement
- son endianness
🔹 Système
Le système fournit :
- fichiers
- processus
- mémoire
🔹 Compilateur
Il traduit le code C en binaire.
Exemples :
- gcc
- clang
- compilateurs embarqués
2.4 Endianness
L’endianness définit l’ordre des octets en mémoire.
🔹 Little endian
Octet faible en premier.
🔹 Big endian
Octet fort en premier.
Exemple :
#include <stdio.h>
int main(void)
{
int x = 0x12345678;
unsigned char *p = (unsigned char*)&x;
printf("%02x %02x %02x %02x\n", p[0], p[1], p[2], p[3]);
}
Le résultat dépend de l’architecture.
2.5 Taille des types
La taille des types n’est pas fixe.
Exemple :
#include <stdio.h>
int main(void)
{
printf("%zu\n", sizeof(int));
}
2.6 Ce qui est portable
Code portable :
- respecte la norme
- évite les extensions
- évite les hypothèses
Exemple — non portable
int main(void)
{
int x = 2147483647 + 1; // overflow
return 0;
}
Le comportement n’est pas défini.
Exemple — portable
#include <stdint.h>
int main(void)
{
int32_t x = 2147483647;
return 0;
}
2.7 Dépendances système
Certains appels dépendent du système.
Exemple :
#include <stdlib.h>
int main(void)
{
system("ls"); // Linux uniquement
return 0;
}
2.8 Abstraction
Pour être portable, il faut abstraire.
Cela signifie :
- isoler le code dépendant
- utiliser des interfaces
--
Exemple :
void afficher_message(void)
{
#ifdef _WIN32
// Code Windows
#else
// Code Linux ?
#endif
}
2.9 Bonnes pratiques
- utiliser <stdint.h>
- éviter les tailles implicites
- éviter UB
- tester sur plusieurs plateformes
- isoler le code système