À QUOI ÇA SERT ?
Avant d’apprendre les variables, les pointeurs ou les classes, il faut comprendre ce qu’est réellement un programme C++.
Quand tu écris :
Code: Select all
int main()
{
return 0;
}
Le chemin général ressemble à ceci :
Code source C++
↓
Préprocesseur
↓
Compilation
↓
Assemblage
↓
Fichier objet
↓
Édition de liens
↓
Exécutable
Cette chaîne est fondamentale en développement système, car elle explique ensuite :
- les symboles ;
- les fichiers .obj/.o ;
- les bibliothèques statiques ;
- les DLL et .so ;
- les erreurs du linker ;
- les imports/exports ;
- les conventions d’appel ;
- les tables de symboles ;
- le reverse engineering.
1. LA FONCTION main()
Dans un programme C++ classique, main() est le point d’entrée du programme au niveau du langage.
Code: Select all
int main()
{
return 0;
}
La valeur retournée est transmise au système d’exploitation.
Par convention :
Code: Select all
return 0;
Une valeur différente de 0 peut être utilisée pour signaler une erreur.
Exemple :
Code: Select all
int main()
{
if (somethingFailed())
return 1;
return 0;
}
Parce qu’un autre programme peut lancer ton exécutable et récupérer son code de sortie.
Sous Linux, le shell peut afficher ce code avec :
Code: Select all
echo $?
Donc main() n’est pas seulement une convention syntaxique : sa valeur de retour sert à communiquer avec l’environnement qui a lancé le programme.
2. main() AVEC DES ARGUMENTS
main() peut recevoir les arguments passés sur la ligne de commande.
Code: Select all
int main(int argc, char* argv[])
{
return 0;
}
argv signifie "argument vector".
Exemple d’exécution :
Code: Select all
programme.exe fichier.txt 42
Code: Select all
argc == 3
argv[0] = "programme.exe"
argv[1] = "fichier.txt"
argv[2] = "42"
On peut faire :
Code: Select all
#include <iostream>
int main(int argc, char* argv[])
{
for (int i = 0; i < argc; ++i)
{
std::cout << argv[i] << '\n';
}
return 0;
}
Les arguments sont préparés par le runtime avant l’appel à main(). En réalité, lorsqu’un exécutable est chargé, le système ne saute généralement pas directement à main(). Il saute vers un point d’entrée défini par le runtime C/C++, qui initialise l’environnement du programme avant d’appeler main().
Donc :
Point d’entrée PE/ELF
↓
runtime C/C++
↓
initialisations
↓
main()
C’est particulièrement important en reverse engineering.
3. COMMENTAIRES
Le commentaire sur une ligne :
Code: Select all
// Ceci est un commentaire
Code: Select all
/*
Ceci est un commentaire
sur plusieurs lignes
*/
Ils servent à expliquer l’intention du code.
Un bon commentaire explique surtout POURQUOI quelque chose est fait.
Exemple mauvais :
Code: Select all
i++; // incrémente i
Exemple plus utile :
Code: Select all
// On saute l’entrée 0 car elle contient l’en-tête du protocole.
++i;
4. #include ET LE PRÉPROCESSEUR
Exemple :
Code: Select all
#include <iostream>
Le préprocesseur traite notamment :
- #include
- #define
- #ifdef
- #ifndef
- #if
- #endif
Code: Select all
#ifdef _WIN32
// code Windows
#else
// autre plateforme
#endif
Cela permet de sélectionner du code selon la plateforme ou la configuration.
Exemple bas niveau :
Code: Select all
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
5. MODULES C++
Le C++ moderne introduit les modules.
Exemple :
Code: Select all
import std;
Historiquement, les headers sont inclus textuellement.
Si 50 fichiers incluent le même gros header, le compilateur doit analyser énormément de contenu plusieurs fois.
Les modules visent à :
- accélérer les compilations ;
- mieux isoler les interfaces ;
- éviter plusieurs problèmes liés aux macros ;
- réduire les dépendances textuelles.
Un module et un namespace ne sont PAS la même chose.
Le module organise l’unité de compilation/import.
Le namespace organise les noms.
Par exemple un module peut contenir :
Code: Select all
namespace Network
{
void connect();
}
namespace Database
{
void connect();
}
6. LES ÉTAPES DE COMPILATION
1) Prétraitement
Le préprocesseur traite les directives.
2) Compilation
Le compilateur :
- vérifie la syntaxe ;
- vérifie les types ;
- applique les règles du langage ;
- optimise éventuellement ;
- produit une représentation proche du code machine.
Création d’un fichier objet :
Windows :
Code: Select all
programme.obj
Code: Select all
programme.o
Le linker rassemble :
- les fichiers objets ;
- les bibliothèques ;
- les symboles externes.
main.cpp :
Code: Select all
int add(int, int);
int main()
{
return add(1, 2);
}
Code: Select all
int add(int a, int b)
{
return a + b;
}
Le linker retrouve la définition dans math.obj.
Si math.obj n’est pas lié, on obtient une erreur du type "unresolved external symbol".
7. DÉCLARATION ET DÉFINITION
Déclaration :
Code: Select all
int add(int a, int b);
"Une fonction add existe et accepte deux int."
Définition :
Code: Select all
int add(int a, int b)
{
return a + b;
}
Cette distinction devient capitale avec :
- headers ;
- fichiers .cpp ;
- DLL ;
- bibliothèques ;
- exports/imports.
8. ENTRÉES ET SORTIES
Sortie classique :
Code: Select all
std::cout << "Hello\n";
Code: Select all
std::cerr << "Erreur\n";
Code: Select all
int value{};
std::cin >> value;
Code: Select all
#include <iostream>
int main()
{
int age{};
std::cout << "Age : ";
std::cin >> age;
std::cout << "Tu as " << age << " ans\n";
}
Code: Select all
std::println("Valeur = {}", 42);
9. '\n' CONTRE std::endl
Code: Select all
std::cout << "Hello\n";
Code: Select all
std::cout << "Hello" << std::endl;
Un flush force les données à être poussées vers le flux.
Cela peut coûter plus cher en performances.
Donc ne pas utiliser std::endl systématiquement sans raison.
À RETENIR
- Le code C++ doit être transformé en code machine.
- Le préprocesseur agit avant le compilateur.
- Les fichiers .cpp sont généralement compilés séparément.
- Le linker résout les symboles entre fichiers.
- main() est le point d’entrée C++ visible, mais pas nécessairement le point d’entrée binaire.
- Comprendre compilation et linking est indispensable pour le système et le reverse.
