🕒 : 3 h maximum
Prérequis:
- utilisation de Monia
- cours de C avec gcc et gdb
- utilisation de vi
- création d’un répertoire
But:
- Création de projet C
- Création d’une librairie statique en C
Répertoire de travail:
~/Works/TP5_C/fonctions
A la fin du TP on doit avoir
etudiant@ordi:~/Works/TP5_C$ tree fonctions/
fonctions/
├── ex1
├── ex2
│ ├── ex2
│ └── ex2.c
├── ex3
│ ├── ex3
│ └── ex3.c
├── ex4
│ ├── ex4
│ └── ex4.c
├── ex5
│ ├── ex5
│ └── ex5.c
├── ex6
│ ├── ex6
│ └── ex6.c
├── ex7
│ ├── ex7
│ ├── ex7.c
│ └── lib.h
├── ex8
| ├── ex8
| ├── ex8.c
| ├── lib.h
└── ex9
├── ex9
├── ex9.c
├── lib.c
├── lib.h
└── lib.o
9 directories, 18 files
etudiant@ordi:~/Works/TP5_C$
Présentation des fonctions en C
« En C, la véritable puissance ne vient pas d’écrire tout le code en un seul bloc, mais de le découper en fonctions, comme des boîtes à outils réutilisables qui rendent vos programmes plus clairs, plus faciles à corriger et à construire. »
Fonctions écrites pour nous
répertoire: ex1 et code ex1.c
Dans la librairie stdio.h on va utiliser la fonction printf() , bien utile et de la librairie math.h la fonction sqrt()
ex1.c:
/* ex1.1 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main() {
double nombre = 25.0;
double racine = sqrt(nombre); /* Fonction de math.h */
printf("Racine de %.1f = %.1f", nombre, racine); /* Fonction de stdio.h */
return EXIT_SUCCESS;
}
static et dynamic (statique et dynamique)
#pour compiler ce code
etudiant@ordi:~/Works/TP_C/fonctions/ex1/$gcc ex1.c -o ex1 -Wall -ansi -pedantic -lm -g
etudiant@ordi:~/Works/TP_C/fonctions/ex1/$ls -l
total 24
-rwxr-xr-x 1 bruno bruno 17320 24 nov. 16:13 ex1
-rw-r--r-- 1 bruno bruno 267 24 nov. 15:59 ex1.c
on obtient ici un exécutable de : 17320 octets!
etudiant@eordi:~/Works/TP_C/fonctions/ex1/$ ldd ex1
linux-vdso.so.1 (0x00007ffd0a5b5000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f13f09a9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f13f07c7000)
/lib64/ld-linux-x86-64.so.2 (0x00007f13f0ab5000)
#pour compiler ce code
etudiant@ordi:~/Works/TP_C/fonctions/ex1/$gcc ex1.c -o ex1 -Wall -ansi -pedantic -static -lm -g
etudiant@ordi:~/Works/TP_C/fonctions/ex1/$ ls -l
total 752
-rwxr-xr-x 1 bruno bruno 763824 24 nov. 16:16 ex1
-rw-r--r-- 1 bruno bruno 267 24 nov. 15:59 ex1.c
exécutable de 763824 Octets ! 44 fois plus gros !!
etudiant@ordi:~/Works/fonctions/ex1/$ ldd ex1
n'est pas un exécutable dynamique
En fait quand dans la première compilation on a mis -lm , et cela veut dire et pas le mot clé -static devant donc par défaut c’est compiler et lié avec la librairie dynamique libm.so.6
etudiant@ordi:~/Works/fonctions/ex1/$ whereis libm.so.6
libm.so.6: /usr/lib/x86_64-linux-gnu/libm.so.6 /usr/lib32/libm.so.6
Une librairie dynamique (.so = Shared Object) est un fichier binaire contenant du code compilé qui peut être partagé en mémoire entre plusieurs programmes simultanément.
etudiant@ordi:~/Works/fonctions/ex1/$ whereis libm.a
libm.a: /usr/lib/x86_64-linux-gnu/libm.a /usr/lib32/libm.a
Dans le cas avec l’option -static la libraire mathématique est ajoutée à votre code ! plus gros donc.
Il est préférable d’utiliser la librairie dynamique pour ne pas encombrer la mémoire. (par défaut)
remarque:
libm.a et libm.so contiennent les exécutables des fonctions en C de la librairie math.h
On va fabriquer des fonctions (maison)
on va voir un petit panel de fonction, pour comprendre le mécanisme des fonctions. Il existe « une infinité » de fonctions…
La fonction affiche_bonjour()
on va voir une fonction des plus simple.
la fonction
void affiche_bonjour(void)
cette fonction n’a besoin d’aucun argument (void) et ne rendra rien , c’est pourquoi le return ne renvoi rien, cette fonction est volontairement simple.
Dans le répertoire : ex2 fichier: ex2.c
/* ex2.c */
#include <stdlib.h>
#include <stdio.h>
void affiche_bonjour(void)
{
printf("Bonjour!\n");
return;
}
int main (int argc, char **argv)
{
affiche_bonjour();
affiche_bonjour();
return EXIT_SUCCESS;
}
Avec gdb on va pouvoir explorer ce code.
un point d’arrêt première instruction de main
break main
on va pouvoir entrer dans la fonction en faisant step (s) dans gdb
si on fait next (n) on va à la ligne suivante sur le code source , alors step on va dans la fonction..
La fonction qui a besoin d’un argument
Dans le répertoire : ex3 fichier: ex3.c
/* ex3.c */
#include <stdlib.h>
#include <stdio.h>
void affiche_nombre_entier(int nombre)
{
printf("Nombre= %d \n",nombre);
return;
}
int main (int argc, char **argv)
{
affiche_nombre_entier(22);
return EXIT_SUCCESS;
}
on voit que notre fonction est de la forme
void affiche_nombre_entier(int nombre)
cette fonction à besoin d’un paramètre ici nombre de type entier (int) et ne retourne rien (vide , void)
Avec gdb on va pouvoir explorer ce code.
un point d’arrêt première instruction de main
break main
on va pouvoir entrer dans la fonction en faisant step (s) dans gdb
si on fait next (n) on va à la ligne suivante sur le code source , alors step on va dans la fonction..
La fonction qui retourne une valeur entière
int retourne(void)
Dans le répertoire : ex4 fichier: ex4.c
/* ex4.c */
#include <stdlib.h>
#include <stdio.h>
int retourne(void)
{
/* on ne fait pas grand ici chose mais on peut */
return 2025;
}
int main (int argc, char **argv)
{
printf ("Ma fonction retourne: %d \n",retourne());
return EXIT_SUCCESS;
}
Avec gdb explorer le code
la fonction qui a besoin d’un argument et retourne un octet (char)
Dans le répertoire : ex5 fichier: ex5.c
char lettre_suivante(char lettre)
Pour montrer la puissance de ce langage on va utiliser ici des type char , et le code ASCII A
/* ex5.c */
#include <stdlib.h>
#include <stdio.h>
char lettre_suivante(char lettre)
{
/* grand calcul de la lettre suivante */
lettre=lettre+1;
return lettre;
}
int main (int argc, char **argv)
{
printf ("La lettre suivant le A est : %c \n",lettre_suivante('A'));
return EXIT_SUCCESS;
}
Avec gdb explorer le code
Créer une librairie
Réunir nos fonctions
on va réunir nos fonctions dans notre code
Et utiliser ces fonctions dans le main comme il se doit !
répertoire ex6 , fichier ex6.c
/* ex6.c */
#include <stdlib.h>
#include <stdio.h>
void affiche_bonjour(void)
{
printf("Bonjour!\n");
return;
}
void affiche_nombre_entier(int nombre)
{
printf("Nombre= %d \n",nombre);
return;
}
int retourne(void)
{
/* on ne fait pas grand ici chose mais on peut */
return 2025;
}
char lettre_suivante(char lettre)
{
/* grand calcul de la lettre suivante */
lettre=lettre+1;
return lettre;
}
/* la belle librairie */
int main (int argc, char **argv)
{
affiche_bonjour();
affiche_nombre_entier(73);
printf(" ma fonction retourne : %d \n",retourne());
printf ("La lettre suivant le c est : %c \n",lettre_suivante('c'));
return EXIT_SUCCESS;
}
on constate ici que j’ai une belle librairie de fonction ! (certes pas très utile , juste à but pédagogique vous aurez des occasions d’utiliser tous cela bien mieux)
char lettre_suivante(char lettre)
int retourne(void)
void affiche_nombre_entier(int nombre)
void affiche_bonjour(void)
ici on dispose de nos 4 fonctions.
Prototypage de fonctions
Le prototypage va servir au compilateur pour connaitre vos fonctions et les intégrer dans votre code
répertoire ex7 , fichier source :ex7.c
/* ex7.c */
#include <stdlib.h>
#include <stdio.h>
/* prototype des fonctions devant le main */
void affiche_bonjour(void);
void affiche_nombre_entier(int nombre);
int retourne(void);
char lettre_suivante(char lettre);
int main (int argc, char **argv)
{
affiche_bonjour();
affiche_nombre_entier(73);
printf(" ma fonction retourne : %d \n",retourne());
printf ("La lettre suivant le c est : %c \n",lettre_suivante('c'));
return EXIT_SUCCESS;
}
/* la belle librairie */
void affiche_bonjour(void)
{
printf("Bonjour!\n");
return;
}
void affiche_nombre_entier(int nombre)
{
printf("Nombre= %d \n",nombre);
return;
}
int retourne(void)
{
/* on ne fait pas grand ici chose mais on peut */
return 2025;
}
char lettre_suivante(char lettre)
{
/* grand calcul de la lettre suivante */
lettre=lettre+1;
return lettre;
}
pour que gcc puisse compiler le code , on a besoin de lui annoncer la forme de nos fonctions, on appel cela prototyper.
Tester le code ,et conclure sur cette version .
On va découper notre projet ! magie !
Dans le répertoire ex8 sources : lib.h et ex8.c
le fichier lib.h sera inclus avec la commande précompilation #include « lib.h »
/* ex8.c */
#include <stdlib.h>
#include <stdio.h>
/* prototype des fonctions devant le main */
#include "lib.h"
int main (int argc, char **argv)
{
affiche_bonjour();
affiche_nombre_entier(73);
printf(" ma fonction retourne : %d \n",retourne());
printf ("La lettre suivant le c est : %c \n",lettre_suivante('c'));
return EXIT_SUCCESS;
}
/* la belle librairie */
void affiche_bonjour(void)
{
printf("Bonjour!\n");
return;
}
void affiche_nombre_entier(int nombre)
{
printf("Nombre= %d \n",nombre);
return;
}
int retourne(void)
{
/* on ne fait pas grand ici chose mais on peut */
return 2025;
}
char lettre_suivante(char lettre)
{
/* grand calcul de la lettre suivante */
lettre=lettre+1;
return lettre;
}
lib.h
/* prototype des fonctions devant le main */ #ifndef ma_lib_a_moi #define ma_lib_a_moi void affiche_bonjour(void); void affiche_nombre_entier(int nombre); int retourne(void); char lettre_suivante(char lettre); #endif
A quoi sert #ifndef et #define et #endif
#pour compiler !
gcc ex8.c -o ex8 -Wall -ansi -pédantic -g
ici le gcc va inclure le code (lib.h) fichier header
Notre librairie a nous !
imaginons que nous avons fait une librairie de fonctions très utiles..
Répertoire : ex9 fichiers sources : ex9.c , lib.h et lib.c
ex9.c va contenir le code ou il y a le main de notre code
/* ex9.c */
#include <stdlib.h>
#include <stdio.h>
/* prototype des fonctions devant le main */
#include "lib.h"
int main (int argc, char **argv)
{
affiche_bonjour();
affiche_nombre_entier(73);
printf(" ma fonction retourne : %d \n",retourne());
printf ("La lettre suivant le c est : %c \n",lettre_suivante('c'));
return EXIT_SUCCESS;
}
notre fichier header lib.h
/* lib.h */ #ifndef ma_lib_a_moi #define ma_lib_a_moi void affiche_bonjour(void); void affiche_nombre_entier(int nombre); int retourne(void); char lettre_suivante(char lettre); #endif
et le fichier de nos fonctions : lib.c
/* lib.c */
#include <stdlib.h>
#include <stdio.h>
/* la belle librairie :) */
void affiche_bonjour(void)
{
printf("Bonjour!\n");
return;
}
void affiche_nombre_entier(int nombre)
{
printf("Nombre= %d \n",nombre);
return;
}
int retourne(void)
{
/* on ne fait pas grand ici chose mais on peut */
return 2025;
}
char lettre_suivante(char lettre)
{
/* grand calcul de la lettre suivante */
lettre=lettre+1;
return lettre;
}
on va transformer notre librairie en .o fichier objet (rien a voir avec la programmation objet)
gcc -c lib.c
on obtient un fichier objet (langage machine de nos fonctions)
on peut voir ce que contient notre librairie
etudiant@ordi:~/Works/TP_C/fonctions/ex8$ objdump -t lib.o
lib.o: format de fichier elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 lib.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 g F .text 000000000000001a affiche_bonjour
0000000000000000 *UND* 0000000000000000 puts
000000000000001a g F .text 000000000000002b affiche_nombre_entier
0000000000000000 *UND* 0000000000000000 printf
0000000000000045 g F .text 000000000000000f retourne
0000000000000054 g F .text 000000000000001d lettre_suivante
etudiant@ordi:~/Works/TP5_C/fonctions/ex8$
mais l’intérêt ici et de lier les fichiers .
en ayant fait le fichier .o
gcc ex9.c lib.o -o ex9 -Wall -ansi -pedantic -g
si on ne veut pas garder le fichier .o
gcc ex9.c lib.c -o ex9 -Wall -ansi -pedantic -g
ce qui va nous donner dans les 2 cas ./ex9 est un exécutable !
Le fait d’avoir un fichier .o va permettre de ne pas recompiler nos fonctions à chaque fois .. si on l’ utilise souvent .
etudiant@ordi:~/Works/TP5_C/fonctions/ex9$ ./ex9
Bonjour!
Nombre= 73
ma fonction retourne : 2025
La lettre suivant le c est : d
etudiant@ordi:~/Works/TP5_C/fonctions/ex9$
on va réaliser notre libraire statique , on pourra faire la manipulation juste par curiosité.
etudiant@ordi:~/Works/TP5_C/fonctions/ex9$ ar rcs lib.a lib.o
etudiant@ordi:~/Works/TP5_C/fonctions/ex9$ ar t lib.a
lib.o
etudiant@ordi:~/Works/TP5_C/fonctions/ex9$
etudiant@ordi:~/Works/TP5_C/fonctions/ex8$ gcc ex9.c lib.a -o ex9 -Wall -ansi -pedantic -g
etudiant@ordi:~/Works/TP5_C/fonctions/ex9$ ./ex9
Bonjour!
Nombre= 73
ma fonction retourne : 2025
La lettre suivant le c est : d
etudiant@ordi:~/Works/TP5_C/fonctions/ex9$
le fichier .a , est une archive obtenu par ar (ici un seul .o)
