# 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` ```c #include int main(void) { printf("Hello\n"); return 0; } ``` Un environnement freestanding est minimal. ```c 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 : ```c #include 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 : ```c #include 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 ```c int main(void) { int x = 2147483647 + 1; // overflow return 0; } ``` Le comportement n’est pas défini. --- Exemple — portable ```c #include int main(void) { int32_t x = 2147483647; return 0; } ``` --- ## 2.7 Dépendances système Certains appels dépendent du système. --- Exemple : ```c #include 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 : ```c void afficher_message(void) { #ifdef _WIN32 // Code Windows #else // Code Linux ? #endif } ``` --- ## 2.9 Bonnes pratiques - utiliser - éviter les tailles implicites - éviter UB - tester sur plusieurs plateformes - isoler le code système