Compilation C
Compilation C
Contexte Contexte
Contexte Contexte
Langage de plus haut niveau et compilation 1 / 3 Langage de plus haut niveau et compilation 2 / 3
Constats
Le code assembleur est propre à chaque microprocesseur Gain en productivité
Il est très difficile d’écrire un code conséquent en assembleur Plus simple
⇒ Nécessité de développer avec des langages de plus haut niveau qu’un programme va Abstraction
traduire en code assembleur / code objet
Permet le changement de paradigme
Compilation vs interprétation Des contrôles (sécurité) peuvent être ajoutés
Traduction à la volée : langage interprété ⇒ Une instruction de haut niveau peut produire plusieurs instructions assembleur
Traduction complète du code : langage compilé
Compilation = vérifications avant exécution
Syntaxique : respect de la grammaire du langage
# include <stdio.h> endbr64 f3 0f 1e fa
✩0x8,%rsp
int main(){
sub
lea 0x0(%rip),%rdi
48
48
83
8d
ec
3d
08
00 00 00 00 Sémantique : respect du typage dans les opérations et les passages de paramètre
printf("Hello world\n"); callq 14 <main+0x14> e8 00 00 00 00
} mov ✩0x0,%eax b8 00 00 00 00
add ✩0x8,%rsp 48 83 c4 08
retq c3
Portabilité
Caractéristiques
Un code peut être compilé/interprété sur différents types de microprocesseurs (et de
systèmes d’exploitation) Langage compilé inventé en 1972 par Dennis Ritchie et Kenneth Thompson pour
redévelopper le système d’exploitation UNIX
Langage d’assez bas niveau parmi les langages de haut niveau
endbr64 f3 0f 1e fa
sub
lea
✩0x8,%rsp
0x0(%rip),%rdi
48
48
83
8d
ec
3d
08
00 00 00 00
Normalisé en 1989 (ANSI) et en 1990 (ISO, C90)
callq 14 <main+0x14> e8 00 00 00 00
x86 mov
add
✩0x0,%eax
✩0x8,%rsp
b8
48
00
83
00
c4
00 00
08
Deux nouvelles versions de la norme ISO en 1999 (C99) et 2011 (C11)
# include <stdio.h>
retq c3
int main(){
printf("Hello world\n");
Langage du paradigme de la programmation structurée
}
stp x29, x30, [sp, #-16]! a9 bf 7b fd Langage à typage statique et typage faible
ARM mov
adrp
x29, sp
x0, 0 <main>
91
90
00
00
03
00
fd
00
add
bl
x0, x0, #0x0
0 <puts>
91
94
00
00
00
00
00
00
Le cœur du C comprend peu d’instructions, mais il est accompagné de nombreuses
mov
ldp
w0, #0x0
x29, x30, [sp], #16
52
a8
80
c1
00
7b
00
fd bibliothèques standards
ret d6 5f 03 c0
Un langage. . . Un langage. . .
pas inclus dans le .o Inconvénients : exécutable plus volumineux et une même version d’une bibliothèque peut être présente plusieurs
fois en mémoire
Il faut donc lier l’appel du sous programme printf du code objet de helloworld.o avec
le code de ce sous programme, code qui se trouve dans une bibliothèque Bibliothèque dynamique (.so sous Unix, .dll sous Windows)
Le code objet de la bibliothèque est chargé en mémoire de l’ordinateur au lancement du programme (si ce n’est
⇒ L’édition des liens déjà fait) et le lien entre l’appel du sous programme et le corps du sous programme est réalisé à l’exécution
Avantage : une bibliothèque utilisée par plusieurs programmes n’est présente qu’une seule fois en mémoire
Inconvénients : gestion de plusieurs versions d’une bibliothèque et problème si la bibliothèque n’est pas pré installée
pr écompilation compilation édition des liens
code source avec macros −−−−−−−−→ code source sans macros −
−−−−−−
→ code objet −
−−−−−−−−−
→ exécutable
Un langage. . . Un langage. . .
L’édition des liens avec gcc sous UNIX Principales bibliothèques standards
Les bibliothèques sous UNIX
Le nom du fichier commence par lib
L’extension du fichier est .a ou .so Fichiers Entêtes Fonctionalités
libc.so assert.h macro assert pour activer des préconditions en mode debug
L’édition des liens avec gcc errno.h codes d’erreur ”renvoyés” par les fonctions standards
option -l suivi du nom de la bibliothèque (sans lib, sans l’extension) limits.h constantes bornes min et max des int
stdargs.h pour créer des fonctions à arité variable
le code objet
stdlib.h transtypage, nombres aléatoires, allocations de mémoire, . . .
option -o pour préciser le nom de l’exécutable (par défaut a.out) stdio.h gestion des entrées/sorties
... ...
Création et exécution du programme helloworld
libm.so complex.h manipulation des nombres complexes
✩ ls ✩ ls math.h fonctions mathématiques (trigo, exp, etc.)
helloworld.c helloworld.o helloworld helloworld.c helloworld.o
✩ gcc -o helloworld -lc helloworld.o ✩ ./helloworld
Hello world
✩
Le langage C - v3.0.1 15 / 93 Le langage C - v3.0.1 16 / 93
Un langage. . . Un langage. . .
.a
Sous UNIX, la libc est par défaut incluse au système (donc optionnelle)
Si le programme n’utilise aucun module/bibliothèque (outre libc), on peut compiler et
.c .o linker directement en utilisant gcc suivi du fichier C (avec optionnellement l’option -o),
int main() {...} par exemple :
gcc -o helloworld helloworld.c
prg
Il est fortement conseillé d’ajouter l’option -Wall pour avoir tous les warnings
.c .o
L’option -pedantic permet de s’assurer que le code C est ISO
.c .o
a. On peut lui adjoindre les qualificatifs short, long ou long long (sur architecture 64bits)
b. On peut leur adjoindre les qualificatifs signed ou unsigned int a;
float x=1.0;
char c;
Algo. ⇒ C
Algorithmique C
Entier,Naturel int, long, short Une variable globale est définie en dehors de toute fonction
Sa portée est le fichier où elle est définie à partir de sa définition (pas au dessus)
Réel float, double
Caractère char Une variable locale est définie à l’intérieur d’une fonction
Booléen int (1=VRAI, 0=FAUX) Sa portée est uniquement la fonction où elle a été définie
Chaı̂ne de caractères Voir les tableaux et pointeurs
Le langage C - v3.0.1 19 / 93 Le langage C - v3.0.1 20 / 93
Éléménts. . . La base Éléménts. . . La base
enum
Permet de définir un ensemble de valeurs constantes associées à un type
typedef Syntaxe
Transforme la déclaration d’une variable en déclaration d’un nouveau type enum [nom] {id1 [=val1], id2 [=val2 ], ...}
Syntaxe :
typedef declaration
enum crée un type dont le nom commence par enum
L’utilisation de enum est souvent couplé avec typedef
typedef int Entier ;
Algo. ⇒ C
On traduit les opérateurs en respectant les règles suivantes : Opérateurs ++ et −−
Algorithmique C Les opérateurs unaires ++ et −− sont des opérateurs particuliers qui peuvent avoir jusqu’à
deux effets de bord :
=, ̸= ==, !=
En dehors de toute affectation, elle incrémente l’opérande associée, par exemple
<, ≤, >, ≥ <, <=, >, >=
i++ et ++i sont équivalents à i=i+1
et, ou, non &&, ||, !
Lorsqu’ils sont utilisés dans une affectation, tout dépend de la position de l’opérateur par rapport
+, -, *, / +, -, *, /
à l’opérande, par exemple :
div, mod /, %
j=i++ est équivalent à j=i;i=i+1;
j=++i est équivalent à i=i+1;j=i;
Attention
En C l’affectation est une opération (opérateur =)
Attention
Contrairement à l’algortihmique le for est une itération indéterministe
Exemple
Exemple
float g,d,m;
g=1; char temp[MAX];
d=x; int motDePasseValide = 0;
while (d-g>epsilon) { int nbEssais = 0;
m=(d+g)/2.0; do {
if (m*m>x) printf("Mot de passe : ");
d=m; scanf("%s", temp);
motDePasseValide = hash(temp) == hashMotPassseRef;
else nbEssais++;
g=m; } while (!motDePasseValide && nbEssais<nbEssaisMax) ;
}
Lorsque l’on déclare une variable, par exemple un entier i, l’ordinateur réserve un espace On déclare une variable de type pointeur en préfixant le nom de la variable par le caractère *
mémoire pour y stocker la valeur de i
L’emplacement de cet espace dans la mémoire est nommé adresse int i , j ;
Un pointeur est une variable qui permet de stocker une adresse int ✯p;
Par exemple si on declare une variable entière i (initialisée à 10) et que l’on déclare un Opérateurs * et &
pointeur p dans lequel on range l’adresse de i (on dit que p pointe sur i), on a par exemple Il existe deux opérateurs permettant d’utiliser les pointeurs :
le schéma suivant : 1 & : permet d’obtenir l’adresse d’une variable, permettant donc à un pointeur de pointer sur une variable
2 * : permet de déréférencer un pointeur, c’est-à-dire d’accéder à la valeur de la variable pointée
i p
Mémoire 10 124
p=&i; // p pointe sur i
124 226 ✯p=✯p+2; // ajoute 2 a i
j=✯p; // met la valeur de i dans j (donc 12)
int i ;
int ✯p1,✯p2;
Par exemple
p1=&i;
p2=NULL;
int plusUn(int a){
return a+1;
}
scanf
Par exemple : L’instruction scanf (de la bibliothèque stdio.h) permet à l’utilisateur de saisir des
informations au clavier
int i =1;
float x=2.0; Syntaxe :
printf (”Bonjour\n”);
scanf(”chaı̂ne de formatage”,pointeur var1 , ...)
printf (”i = %d\n”,i);
printf (”i = %d, x = %f\n”,i,x); La chaı̂ne de formatage spécifie les caractères et le type des données attendues, par
exemple :
. . .affiche : %d pour les entiers (int, short, long)
Bonjour %f pour les réels (float, double)
i=1 %s pour les chaı̂nes de caractères
i = 1, x=2.0 l’espace, la tabulation, le retour chariot sont considéré comme un séparateur de chaı̂ne
préférez dans ce cas gets (voire fgets)
%c pour les caractères
On déclare une variable de type tableau en ajoutant après l’identificateur la taille de chaque
dimension (entre [ et ])
Par exemple
int i ; int tab1 [10]; // un tableau de 10 entiers (une dimension)
float x; float tab2 [2][5]; // un tableau de 10 réels (deux dimensions)
scanf(”%d%f”,&i,&x);
Une fois déclaré, un élément d’un tableau s’utilise comme une variable classique
Il n’y a pas de type chaı̂ne de caractères en C
int t [10]; // un tableau de 10 entiers (une dimension) Souvent les tableaux de caractères sont utilisés pour les représenter
int i ;
for ( i =0;i<10;i++)
scanf(”%d”,&t[i]);
char nom[30];
printf (”Votre nom: ”);
scanf(”%s”,nom);
Un tableau est considéré comme un pointeur constant
Lors d’un appel de fonction, le passage de paramètre pour un tableau est un passage de
paramètre par adresse Si n caractères sont saisis n + 1 éléments du tableau sont utilisés (’\0’).
Dans la signature d’une fonction, la dimension d’un tableau en tant que paramètre
formel est facultative
union Nombre {
Attention int unEntier ;
struct crée un nouveau type dont le nom commence par struct float unReel;
Complexe unComplexe;
L’utilisation de struct est souvent couplé avec typedef }
Le langage C - v3.0.1 49 / 93 Le langage C - v3.0.1 50 / 93
En fait la signature de la fonction main est int main(int argc, char **argv), tel que :
Le type FILE* argc est le nombre de paramètres
Pas de concept de fichiers texte ou d’enregistrement : ce sont des fichiers binaires argv est un tableau de chaı̂ne de caractères
Ouverture : FILE *fopen(const char *path, const char *mode); argv[0] est le nom du programme
mode : ”r”, ”r+”, ”w”, ”w+”, ”a”, ”a+”
”b” peut être ajouté pour la compatibilité C89 Exemple : args.c* a
Fermeture : int fclose(FILE *fp)
a. http://www.ai.univ-paris8.fr/~jalb/langimp/main.html
Fonction de lecture : fscanf, fgetc, fgets, fread, . . .
#include <stdio.h>
Fonction d’écriture : fprintf, fputc, fputs, fwrite, . . . #include <stdlib.h>
int main(int argc , char ✯✯argv) {
Fonction gestion curseur : fseek, ftell, rewind, feof, . . . int i ;
printf (”argc: %d\n”, argc);
Pointeurs de fichier prédéfinis : stdin, stdout, stderr for ( i = 0; i < argc; i ++)
printf (”argv[%d]: %s\n”, i, argv [ i ]);
return EXIT SUCCESS;
}
void triABulle ( int t [], int (✯comparer) (int , int ), int nbElements) {
int i , j , estTrie ;
Les fonctions peuvent être considérées comme des variables, elles possèdent : estTrie =0;
i =0;
un identifiant
while (( i <nbElements) && (!estTrie)) {
un type (type des paramètres et type de retour) estTrie =1;
Elles peuvent être un argument d’un appel de fonction for ( j=nbElements❂1;j>0;j❂❂) {
if (comparer(t[ j ❂1],t [ j])>0) {
echanger(&t[j ❂1],&t[j ]);
estTrie =0;
}
}
i ++;
}
}
Exemple 2 / 3 Exemple 3 / 3
Problème
L’interprétation de entierA.h par le préprocesseur va inclure entierB.h, qui va inclure
entierA.h, qui va inclure entierB.h, . . .
Le langage C - v3.0.1 59 / 93 Le langage C - v3.0.1 60 / 93
Particularité. . . Les bibliothèques Particularité. . . Les macros paramétrés (macro-fonction)
Particularité. . . Les macros paramétrés (macro-fonction) Particularité. . . Les macros paramétrés (macro-fonction)
Lorsque l’on déclare une variable locale, le compilateur ajoute des instructions dans le
On peut appliquer les opérateurs +, −, ++ et −− à des variables du type pointeurs code généré réservant de l’espace mémoire dans la pile lorsque la fonction de la variable
sera exécutée
C’est ce que l’on appelle l’allocation statique
int t [10]={1,2,3,4,1,7,2,4,9,60}; Il ajoute aussi du code libérant cet espace mémoire qui sera exécuté lorsque l’on sortira de
int ✯p; la fonction
p=t; On aimerait quelquefois pouvoir déterminer la taille de l’espace mémoire à réserver non
p=p+3; pas à la compilation, mais à l’exécution
✯p=0;
p❂❂; C’est ce que l’on appelle l’allocation dynamique
✯p=10; Elle se fait dans le tas
On réserve l’espace mémoire à l’aide de la fonction malloc (et consoeurs) et on libère cet
espace à l’aide de la fonction free
Premier exemple
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
char✯ saisieQuiNeMarchePas() {
char buffer [MAX]; Exécution
gets( buffer );
return buffer ; $ ./testChaine1
} un petit test qui risque de ne pas marcher
int main(int argc , char ✯✯argv) { un petit test q
printf (”%s\n”,saisieQuiNeMarchePas()); $
return EXIT SUCCESS;
}
Compilation
$ gcc -c -Wall -pedantic testChaine1.c
testChaine1.c: In function ’saisieQuiNeMarchePas’:
testChaine1.c:9:3: attention: cette fonction retourne l’adresse d’une
variable locale [enabled by default]
Compilation
$ gcc -c -Wall -pedantic testChaine2.c
$
Le langage C - v3.0.1 75 / 93 Le langage C - v3.0.1 76 / 93
Particularité. . . Les chaı̂nes de caractères Particularité. . . Les chaı̂nes de caractères
Compilation
$ gcc -c -Wall -pedantic testChaine3.c
$
Le langage C - v3.0.1 77 / 93 Le langage C - v3.0.1 78 / 93
Quatrième exemple
#include <stdio.h>
#include <stdlib.h>
#define MAX 10 Le C standard est livré avec des bibliothèques standards, dont les fichiers d’en-tête sont :
char✯ saisieQuiMarche() { assert.h locale.h stddef.h
char✯ buffer =(char✯)malloc(sizeof(char)✯MAX); ctypes.h math.h stdio.h
fgets ( buffer ,MAX,stdin); setjmp.h
return buffer ;
errno.h stdlib.h
} float.h signal.h string.h
limits.h stdarg.h time.h
int main(int argc , char ✯✯argv) {
char✯ ch;
ch=saisieQuiMarche();
printf (”%s\n”,ch);
free (ch);
return EXIT SUCCESS;
}
$ ./assert Objectifs :
-10 lib/libechanger.a
assert: assert.c:10: foo: Assertion ‘a>0’ failed. bin/tri
Abandon (core dumped)
Makefile Makefile
Règles 6
7
lib/libechanger.a : src/echanger.o
ar -r lib/libechanger.a src/echanger.o
8
9 src/echanger.o : src/echanger.c
10 gcc -o src/echanger.o -c -Wall -pedantic -Iinclude src/echanger.c
Règles 11
12 src/triParMinSuccessif.o : src/triParMinSuccessif.c
Permet de décomposer une action en plusieurs actions 13
14
gcc -o src/triParMinSuccessif.o -c -Wall -pedantic -Iinclude src/triParMinSuccessif.c
15 src/main.o : src/main.c
Structure : 16 gcc -o src/main.o -c -Wall -pedantic -Iinclude src/main.c
17
18 clean :
cible: dependance 19 rm -f bin/*
20 rm -f src/*.o
actions 21 rm -f lib/*.a
Makefile Makefile
Le langage C - v3.0.1 93 / 93