ITC34 - Algorithmique et programmation

Transcription

ITC34 - Algorithmique et programmation
ITC34 - Algorithmique et programmation
TP1 C /C++ : Compilation
Octobre 2010 - Benoît Darties
Exercice 1 : Erreurs de compilation avec gcc
Voici un programme "Hello World" écrit dans le fichier hello.c :
#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}
Les fichiers de la forme .h contiennent les en-têtes des fonctions utilisées dans le programme.
1. Compilez ce programme sans réaliser l’édition de liens, au moyen de la commande gcc -c
hello.c. Notez la création du fichier hello.o, puis réalisez l’édition de liens sur ce dernier.
2. en-têtes manquants :
(a) supprimez la première ligne #include <stdio.h> du fichier hello.c :
(b) essayez de compiler le programme, d’abord en mode "tout-en-un" avec la commande gcc
hello.c -o hello, puis en mode "compilation, puis édition de lien" avec les commandes
gcc -c hello.c, et gcc hello.o -o hello. Le problème se situe-t-il au niveau de la
compilation ou au niveau de l’édition de lien ?
3. implémentation manquante :
(a) créez le fichier addition.c suivant :
#include <stdio.h>
int addition (int a, int b);
int main() {
int a;
printf("my first addition\n");
a = addition (5, 10);
printf("5 + 10 = %i \n", a);
return 0;
}
(b) Compilez sans l’édition de lien (commande gcc -c addition.c). La compilation passet’elle ?
(c) Essayez de réaliser l’édition de lien.
(d) Ajoutez les éléments manquants pour réaliser correctement l’édition de lien et testez
votre programme
4. prototype manquant :
(a) créez le fichier additionPB.c suivant :
#include <stdio.h>
int main() {
float a;
1
printf("my first addition\n");
a = addition (5.0, 10.0);
printf("5.0 + 10.0 = %f \n", a);
return 0;
}
float addition(float a, float b) {
return a+b;
}
(b) La compilation de ce programme avec la commande commande gcc -c additionPB.c
doit normalement renvoyer une erreur. Etait-ce prévisible et d’après vous d’où vient ce
problème ?
(c) L’erreur renvoyée parle compilateur est pourtant étonnante à première vue : "conflicting
types", signifiant qu’un type de donnée a été entré alors qu’un autre type était attendu.
Le compilateur gcc analyse la syntaxe de haut en bas. Il rencontre un appel à la fonction addition() avant même que celle-ci ne soit déclarée ou implémentée. En pareil
circonstance, gcc considère implicitement que la fonction est de type int, et que chacun
de ses paramètres est de type int. Cela reviendrait à avoir écrit le prototype de fonction
int addition(int a , int b) ; en début de programme (avant le main). Lorsqu’il
rencontre l’implémentation de cette fonction, il découvre que la valeur retour ainsi que
les paramètres sont de type float, alors que lui a défini cette fonction comme un int. D’où
le type de conflits. Modifiez votre programme pour que la fonction addition additionne
désormais des entiers et retourne un entier, stocké dans la variable entière a, et affichée
telle qu’elle dans la fonction main().
(d) Compilez ce programme avec la commande gcc -c additionPB.c . La compilation estt-elle bien effectuée malgré que la fonction addition() soit utilisée avant même d’être
déclarée ? Réalisez l’édition de liens et vérifiez que votre programme marche bien.
(e) Bien que la compilation se soit déroulée sans erreur, le compilateur a noté qu’il y avait
un problème au niveau de la déclaration du prototype de addition(), mais n’a pas jugé
bon d’en informer l’utilisateur et a supposé que la fonction était de type int. Pour forcer
le compilateur à notifier la présence de problèmes mineurs dans ce genre sous forme
d’alertes, on utilise l’option -Wall (pour Warning all). Recompilez votre programme
avec la commande gcc -Wall additionPB.c -o additionPB pour afficher le message
d’alerte. Modifiez ensuite votre programme pour que la compilation avec option -Wall
n’affiche aucun message d’alerte.
Exercice 2 : Compilation séparée avec gcc
On cherche maintenant à définir la fonction addition() dans un fichier séparé. Pour cela, nous
créeons un fichier en-tête addition.h qui contient le prototype de la fonction, tandis que le fichier
addition.c contient l’implémentation de la fonction addition(). Le fichier addition.h contient la
ligne suivante :
int addition(int a, int b);
Le fichier addition.c contient le code suivant :
int addition(int a, int b) {
return a + b;
}
La fonction main() apparait désormais dans un fichier main.c présenté ci-après :
#include <stdio.h>
#include "addition.h"
2
int main() {
int a;
a = addition (5, 10);
printf("5 + 10 = %i \n", a);
return 0;
}
1. Compilez le fichier addition.c avec la commande gcc -c addition.c (sans réaliser l’édition
de lien). Vérifiez quel nouveau fichier est apparu.
2. Compilez le fichier main.c avec la commande gcc -c main.c (sans réaliser l’édition de lien).
Vérifiez quel nouveau fichier est apparu.
3. Réalisez l’édition de lien sur les deux fichiers objet précédemment créés par la commande
suivante : gcc main.o addition.o -o addition et exécutez le programme addition.
4. Cherchez sur votre ordinateur le fichier stdio.h
5. notez dans le fichier main.c la différence suivante : stdio.h est encadré par des chevrons, tandis que addition.h est encadré par des guillemets. L’utilisation de chevrons indique au compilateur d’aller chercher le fichier stdio.h dans les répertoires standards (ex : /usr/include)
, tandis que les guillemets précisent que le fichier se situe dans le répertoire courant.
Exercice 3 : Le pré-traitement
Un point passé jusqu’à présent sous silence est le pré-traitement, qui intervient avant même la
phrase de compilation. Les actions effectuées par le compilateur durant la phase de pré-traitement
sont diverses et nombreuses. Les lignes concernées par la pré-compilation sont précédées par le
caractère ’#’ En phase de compilation, les fichiers source sont d’abord soumis à un pré-traitement.
L’objectif de cet exercice est de voir les différents rôles du pré-traitement afin de pouvoir les
exploiter par la suite.
1. Concaténation des fichiers en-tête
(a) le résultat du pré-traitement peut être obtenu avec l’option -E de gcc. Reprendre le
fichier hello.c de l’exercice 1 et afficher le résultat de la pré-compilation avec la commande gcc -E hello.c.
(b) Vérifier que la sortie écran contient bien le fichier stdio.h suivi code du fichier hello.c
(c) Rediriger ensuite ce résultat dans un fichier hello2.c et vérifiez que ce dernier est bien
compilable et peut s’exécuter : gcc hello2.c -o bonjour2
(d) Dans la suite de l’exercice, on omettra volontairement d’inserer les directives d’inclusion
de fichier par soucis de clarté à l’affichage.
2. Définition de macros
(a) Enregistrez le code suivant dans un fichier macro.c
#define MESSAGE "hello"
#define FONCTION printf("hello again");
#define MAXBUFFERSIZE 100
int main() {
int buffer[MAXBUFFERSIZE];
printf(MESSAGE);
FONCTION
return 0;
}
(b) Affichez le résultat du pré-traitement. A quoi sert la directive "#define" ?
3
(c) La définition de macros permet également le passage de paramètres, qui vont être adaptés en fonction du contexte d’appel de la macro. Rajouter au fichier macro.c la macro
suivante :
#define max(a,b) a>b?a:b
(d) Rajoutez ensuite à l’interieur de la fonction main() les instructions suivantes :
int a=10;
int b=20;
int v1 = max(a,b);
int v2 = max(10,45);
int v3 = max(v1,v2);
3. Suppression des commentaires :
(a) Ajouter des commentaires à votre code, en les faisant précéder de // sur la ligne courante,
ou en les encadrant par /* ... */
(b) Vérifiez que ces commentaires ont disparu dans le résultat du pré-traitement.
4. Traitements conditionnels :
(a) recopiez le code suivant dans un fichier condition.c :
#define MESSAGE "bonjour je suis un gentil message"
#define MESSAGE "bonjour je suis un mechant message"
int main() {
printf(MESSAGE);
}
(b) effectuer le pré-traitement (commande : gcc -E condition.c ). Quel message sera retenu ? notez l’apparition d’une alerte.
(c) effectuer la compilation à partir du fichier condition.c (rajouter si besoin la directive
#include <stdio.h>. L’alerte est-elle visible ?
(d) effectuer la compilation à partir du résultat du pré-traitement (rajouter si besoin la
directive #include <stdio.h>. L’alerte est-elle visible ?
(e) Remplacez les directives de pré-traitement par les directives suivantes :
#ifndef MESSAGE
#define MESSAGE "bonjour je suis un gentil message"
#endif
#ifndef MESSAGE
#define MESSAGE "bonjour je suis un mechant message"
#endif
int main() {
printf(MESSAGE);
}
(f) re-effectuez le pré-traitement. L’alerte est-elle apparue ? quel message a été conservé ?
quel message a été ignoré ?
(g) inverser les 2 blocs de directives et ré-effectuez le pré-traitement. Qu’en concluez-vous
sur l’utilisation des directives #ifndef , #define et #endif ?
4