lsname: inverse layout: true class: center, middle, inverse --- # Inès de Courchelle ## Compilation Séparée ## 2024-2025  --- layout: false # Rappels ## Les pointeurs .otro-blockquote[Un pointeur est une variable qui mémorise une adresse mémoire. En bref si tu as free, tu as tout compris !] ```c int* a; int b; b=42; a=&b; ```
--- # Rappels ## Les Tableaux dynamiques Il y a des étapes à suivre 1. Déclaration 2. Allocation 3. Affectation 4. Traitements/affichage 5. Libération ```c int cases; int* p_tab; cases =5; p_tab = malloc(cases * sizeof(int)); /* ... */ free(p_tab); ``` --- # Rappel ## Les Tableaux dynamiques Un tableau dynamique 2D est un pointeur pointant vers un tableau de pointeurs !
--- # Aujourd'hui ## Objectifs - Délocaliser la fonction main - Classer les différentes méthodes/fonctions - Organiser son code - Utiliser une bibliothèque mathématiques - Lancer des commandes unix depuis un programme C
--- # La problématique ## Illustration Nous considérons le code suivant : .pull-left[ ```c void f1 (int a, int b){ .../... c=2*f2+f1; .../... } void f2 (int a, int b){ .../... c=2*f1+f2; .../... } ``` ] .pull-right[
] --- # La problématique ## Illustration
.underR[Problème] : f2 est appelé dans f1 alors qu'elle n'existe toujours pas ! ```c void f1 (int a, int b){ .../... c=2*f2+f1; .../... } void f2 (int a, int b){ .../... c=2*f1+f2; .../... } ``` --- # Solution 1 ## Prototypes/signatures .pull-left[ ```c void f1 (int a, int b); void f2 (int a, int b); int main(){ .../... } void f1 (int a, int b){ .../... c=2*f2+f1; .../... } void f2 (int a, int b){ .../... c=2*f1+f2; .../... } ``` ] .pull-right[ - Avant de débuter le main, il faut préciser le protype/signature de la fonction - Attention, il faut écrire la même chose en haut et en bas (à gauche, à droite ...)
] --- # Solution 2 ## La compilation séparée On va tout couper !
--- # C'est quoi la compilation séparée ? ## Définition La compilation séparée de fichiers sources est utilisée afin de lier des fichier pour générer un exécutable final ! ## Pourquoi ? - Organiser son code (surtout quand il y a bcp de lignes) - Avoir un nombre de lignes raisonnable par fichier ## Comment ? un fichier d'entête + un fichier de code ## Quand ? *le mieux, c'est quand même tout le temps !* --- # Step by step ## Exemple .pull-left-small[ .under[Mais il manque un truc !] 1. Comment relier tous les fichiers entres eux ? 2. Comment compiler ? 3. Comment exécuter ? ] .pull-right-big[
] --- # Step by step ## fonctions.h ```c int add (int a, int b); int sous (int a, int b); ``` RAS ici on ne met que les protos/signatures !
--- # Step by step ## fonctions.c .pull-left[ 1. Lier le fichier.c à son homologue fichier.h 2. Respecter les mêmes signatures/prototypes 3. .underR[Attention] : - le include est dans le .c - on inclut le .h dans le .c - le include est entre "" - on n'inclut jamais un .c
] .pull-right[ ```c #include "fonctions.h" int add (int a, int b){ int c; c=a+b; return c; } int sous (int a, int b){ int c; c=a-b; return c; } ``` ] --- # Step by step ## main.c ```c #include
#include "fonctions.h" int main(int argc, int* argv) { int resultat1; int resultat2; resultat1=sous(4,4); resultat2=add(4,4); return 0; } ``` .underR[Attention] *Si on utilise des fonctions dévelopées dans un fichier séparé* **Alors,** on inclut le fichier de signatures/protos (.h) dans le fichier en question --- # VAR ## Exemple
--- # Step by step ## Bilan .pull-left-small[
] .pull-right-big[ #### Comment je dois compiler ? 1. Compiler fonction.c ```shell gcc -c -Wall fonctions.c -o fonctions.o ``` 2. Compiler main.c ```shell gcc -c -Wall main.c -o main.o ``` 3. Compiler le tout ! ```shell gcc fonctions.o main.o -o exe ``` ]
.underR[ATTENTION, on NE compile JAMAIS un .h] --- # Comment compiler ? ## VAR
--- # ouverture Parenthèse ## (UNIX
## c-à-d Lancer des commandes unix depuis un programme C --- # Commandes unix en c ## exemple 1 : effacer le terminal au bout de 5 secondes
--- # Commandes unix en c ## exemple 2 : afficher un fichier texte
--- # Commandes unix en c ## exemple N : Toutes les commandes unix
--- # Fermeture parenthèse ## )
--- # Inclusions multiples ## New blème ! Lorsque plusieurs fichiers s'incluent entres eux
- A.h inclu B.h - B.h inclu A.h
Tout se passe dans la tête ! --- # Mais comment faire ? 1/2 ## A.h ```c #ifndef __A_H_ #define __A__H #include "B.h" /* signatures/prototypes des fonctions */ #endif ``` ## B.h ```c #ifndef __B_H_ #define __B__H #include "A.h" /* signatures/prototypes des fonctions */ #endif ``` --- # Mais comment faire ? 2/2 ## Traduction ligne par ligne pour le fichier A.h ```c #ifndef __A_H_ ``` Si la variable __A_H_ n'existe pas ```c #define __A__H ``` Alors je la créé ```c /* signatures/prototypes des fonctions */ ``` Je mets les signatures et prototypes des fonctions (comme avant) ```c #endif ``` Et surtout je n'oublie pas de fermer le if que j'ai ouvert un peu plus haut ! --- # Mais comment compiler 1/2
--- # Mais comment compiler 2/2 .pull-left[ #### Création des modules ```shell gcc -c -Wall A.c -o A.o gcc -c -Wall B.c -o B.o ``` #### Création du module pour le main ```shell gcc -c -Wall main.c -o main.o ``` #### Création des liens ```shell gcc main.o A.o B.o -o exe ``` #### Execution ```shell ./exe ``` ] .pull-right[
] --- # Les ifndef ## À quoi ça sert ? à ne pas ré-inclure plusieurs fois le même fichier d'entête ## Illustration On considère l'arborescence suivante :
--- # Les ifndef ## Si je ne les mets pô !
--- # Les ifndef ## Où je les rajoute ?
--- # Les ifndef ## Si je les mets !
--- # Solution ## Makefile
--- # Makefile ## Définition Un Makefile est un fichier constitué de plusieurs règles de la forme : ```shell cible: dependance commandes ``` Le nom du fichier est **makefile** Il doit être enregistrer au même endroit que les autres fichiers ! ## Pourquoi utilise t-on un makefile ? - Gagner du temps - Manipuler facilement les fichiers de compilation séparée - Compiler rapidement un projet - Mettre en place une application --- # Makefile ## Le mini Nous considérons l'arborescence de fichiers suivante :
## Le script ```shell exe: fonctions.o main.o gcc fonctions.o main.o -o exe fonctions.o: fonctions.c fonctions.h gcc -c fonctions.c -o fonctions.o -Wall main.o: main.c fonctions.h gcc -c main.c -o main.o -Wall ``` --- # Makefile ## Le Script ```shell exe: fonctions.o main.o gcc fonctions.o main.o -o exe fonctions.o: fonctions.c fonctions.h gcc -c -Wall fonctions.c -o fonctions.o main.o: main.c fonctions.h gcc -c -Wall main.c -o main.o ``` ## Explication - Pour créer l'executable **exe**, nous avons besoins de deux objets : - fonctions.o - main.o - Pour créer l'objet fonctions.o, nous avons besoins de : - fonctions.c - fonctions.h - Pour créer l'objet main.o, nous avons besoins de : - main.c - fonctions.h --- # Makefile ## Illustration
## Comment on l'utilise ? On créé un fichier makefile sans extension --- # Makefile ## Dans notre exemple
.pull-left[ #### Avant ```c gcc -c -Wall affichage.c -o a.o ``` ] .pull-right[ #### Après Pour créer le a.o ```shell a.o: affichage.c affichage.h gcc -c -Wall affichage.c -o a.o ``` ] --- # Makefile ## Dans notre exemple
## Pour créer le ad.o .pull-left[ #### Avant ```c gcc -c -Wall analyseData.c -o ad.o ``` ] .pull-right[ #### Après ```shell ad.o: analyseData.c analyseData.h affichage.h a.o gcc -c -Wall analyseData.c -o ad.o ``` ] --- # Makefile ## Dans notre exemple
## Pour créer le main.o .pull-left[ #### Avant ```c gcc -c -Wall main.c -o main.o ``` ] .pull-right[ #### Après ```shell main.o: main.c analyseData.h affichage.h gcc -c main.c -o main.o -Wall ``` ] --- # Makefile ## Dans notre exemple
## Pour créer l'exécutable .pull-left[ #### Avant ```c gcc -o ad.o a.o main.o -o d ``` ] .pull-right[ #### Après ```shell exe: ad.o main.o a.o gcc fonctions.o main.o -o exe ``` ] --- # Makefile ## Dans notre exemple ```shell exe: ad.o main.o a.o gcc ad.o a.o main.o -o exe ad.o: analyseData.c analyseData.h affichage.h a.o gcc -c -Wall analyseData.c -o ad.o a.o: affichage.c affichage.h gcc -c -Wall affichage.c -o a.o main.o: main.c analyseData.h affichage.h gcc -c main.c -o main.o -Wall ``` --- # Makefile ## VAR
--- # Avant VS Makefile ## Illustration .pull-left[ #### Avant ##### Création de l'objet pour fonctions ```shell gcc -c -Wall fonctions.c -o fonctions.o ``` ##### Création de l'objet pour le main ```shell gcc -c -Wall main.c -o main.o ``` ##### Création des liens ```shell gcc main.o fonctions.o -o exe ``` ##### Execution ```shell ./exe ``` ] .pull-right[ #### Makefile ##### Le fichier ```shell exe: fonctions.o main.o gcc fonctions.o main.o -o exe fonctions.o: fonctions.c fonctions.h gcc -c -Wall fonctions.c -o fonctions.o main.o: main.c fonctions.h gcc -c -Wall main.c -o main.o ``` ##### Compilation ```shell make ``` ##### Execution ```shell ./exe ``` ] --- # Attention inclusions multiples ## Hein .pull-left[ #### A.h ```c #ifndef __A_H_ #define __A_H_ #include "B.h" void fn1(); void fn2(); #endif ``` #### A.c ```c #include "A.h" void fn1(){ fn3(); } void fn2(){ fn4(); } ``` ] .pull-right[ #### B.h ```c #ifndef __B_H_ #define __B_H_ #include "A.h" void fn3(); void fn4(); #endif ``` #### B.c ```c #include "B.h" void fn3(){ fn1(); } void fn4(){ fn2(); } ``` ]
.underR[Boucle infinie !!]
--- # Bilan ## Règles - un fichier d'entête (*.h*) est associé un fichier de code (*.c*) - la fonction main est séparée des autres fichiers - ne pas oublier les tests d'inclusion en haut de chaque fichiers d'entête (*.h*) - attention aux inclusions multiples (à utiliser avec parcimonie)
--- # Update des objets 1/4 ## TimeLine .pull-left[ 1- Je créé mes fichiers.c
2- J'écris dedans !
3- Je créé mon makefile
4- J'écris encore dedans !
5- Je compile à l'aide de la commande make
6- J'exécute mon programme
7- J'apporte des modifications à mes fichiers
8- Je recompile à l'aide de la commande
```shell make ``` 9- J'ai le message suivant :
```shell make: 'exe' is up to date. ``` 10- J'exécute ] .pull-right[ 11- Les modifications n'ont pas été réalisées
12- J'appelle la prof
13- Elle s'énerve !
14- Je m'énerve
15- Elle s'énerve
16- Je m'énerve
17- ...
] --- # Update des objets 2/4 - Le problème vient de l'update des objets ! - Le makefile voit les objets ! - Comme il les voit, ils existent ! - Comme ils existent, il ne les modifie pas ! - Comme il ne les modifie pas, ... il ne les modifie pas !
.under[Comment faire pour les supprimer ?] Solution : On va rajouter des instructions dans le makefile !
--- # Update des objets 3/4 ## Le script ```shell exe: fonctions.o main.o gcc fonctions.o main.o -o exe fonctions.o: fonctions.c fonctions.h gcc -c -Wall fonctions.c -o fonctions.o main.o: main.c fonctions.h gcc -c -Wall main.c -o main.o clean: rm -f *.o ``` - On rajoute l'instruction de suppression des extensions .o - Par défaut, on l'appelle clean - Si on reprend la timeline de tout à l'heure ! --- # Update des objets 4/4 ## Timeline .pull-left[ 1- Je créé mes fichiers.c
2- J'écris dedans !
3- Je créé mon makefile
4- J'écris encore dedans !
5- Je compile à l'aide de la commande make
6- J'exécute mon programme
7- J'apporte des modifications à mes fichiers
8- Je recompile à l'aide de la commande
```shell make ``` 9- J'ai le message suivant :
```shell make: 'exe' is up to date. ``` 10- J'exécute ] .pull-right[ 11- Les modifications n'ont pas été réalisées
12- J'appelle la prof
13- Elle commence à s'énerver !
14- Je reviens à moi !
15- Je vérifie que dans le makefile, il y est l'instruction
```shell clean: rm -f *.o ``` 16- Je lance l'instruction suivante dans le terminal
```shell make clean make ``` 17- La prof :
] --- # CLEAN ## VAR
--- # Mais AUSSi ## Faire des trucs cool en makefile ! 1. Rajouter des commentaires ! ```shell # Ceci est un commentaire ! ``` 2. Mettre des affichages ```shell @echo "J'ai tout effacé" ```
--- # Exemple Final ! ## Le script ```shell # Mon premier makefile # c'est beau ! # (avec le hastag je fais des commentaires !) exe: fonctions.o main.o @echo "compilation de l'executable" gcc fonctions.o main.o -o exe fonctions.o: fonctions.c fonctions.h @echo "compilations de fonctions" gcc -c -Wall fonctions.c -o fonctions.o main.o: main.c fonctions.h @echo "compilations du main" gcc -c -Wall main.c -o main.o clean: @echo "J'ai tout effacé" rm -f *.o ``` --- # Math.h 1/2 ## Illustration Nous désirons faire un programme qui calcul la racine carrée et la puissance d'un nombre saisi par l'utilisateur : ```c #include
#include
int main(){ int a; float racine; int puissance; printf("veuillez saisir un nombre : \n"); scanf("%d",&a); racine=sqrt(a); puissance=pow(a,2); printf("la racine de %d est %f \n",a,racine); printf("la puissance de %d est %d \n",a,puissance); return 0; } ``` --- # Math.h 2/2 ## Illustration Il faut rajouter : ```c #include
``` Une compilation classique ne fonctionne pas : ```shell gcc -Wall monProgramme.c -o exe ``` Il faut rajouter le lien biblio (**-lm**) : ```shell gcc -Wall monProgramme.c -o exe -lm ``` --- # BILAN - N'ayait pas peur de poser des questions - *Mais avant vérifier que l'information n'est pas dans le cours* - Aucune question n'est idiote : .otro-blockquote[<< n'est stupide que la stupidité >>]