Ecole Nationale des Sciences
de l'Informatique
TP: Programmer sous GNU/LINUX
Syst me dexploitation & Mise en Ruvre Unix
M. SELLAMI
Ce premier TP introductif la programmation en C va vous familiariser avec :
la syntaxe du langage ;
la mise en oeuvre du compilateur ;
le pr processeur cpp ;
l'utilisation d'un d bogueur/d vermineur/metteur au point ;
la compilation s par e et l'utilisation de make ;
lutilisation des biblioth ques
"
"
"
"
"
"
" divers outils relatifs au d veloppement de programmes en C.
Quelques unes des questions du pr sent sujet de TP sont inspir es d'un TP sur les sites : http://videoc.lip6.fr et
http://www.lifl.fr/~marquet/ens/cu/tpc1.html
Exercice 1
Question 1 crivez un programme qui calcule la factorielle de 10, et affiche le r sultat. On d finira une fonction
unsigned int factorielle (unsigned int n)
{
unsigned int i = 1, res = 1;
while (i <= n)
res = res * i++;
return res;
}
qui calcule la factorielle de n de fa on it rative. On placera le source de ce programme dans le fichier source
factorielle.c ;
Question 2 Compilez ce programme (le fichier source) par la commande
% gcc -g -Wall -Werror -ansi -pedantic -o factorielle factorielle.c
" on utilise le compilateur C du projet GNU, gcc ;
"
les options -ansi et -pedantic pr cisent que l'on produit des programmes C au standard ANSI C et ce de
mani re stricte. Ces options seront syst matiquement utilis es pour tous vos d veloppements, au moins dans
le cadre de ce module ;
" de m me, vous utiliserez syst matiquement les options -Wall (signaler tous les warnings) et -Werror
(consid rer les warnings comme des erreurs) ;
l'option -g du compilateur C permet de conserver des informations utiles au d vermineur ;
l'option -o permet de sp cifier le nom du fichier ex cutable, (factorielle ici, a.out par d faut).
Publicité
"
"
Si il n'y a pas d'erreur, le compilateur n'affiche rien et g n re l'ex cutable factorielle. Sinon, le compilateur affiche
la liste des erreurs qu'il a d tect es. Par exemple
% gcc -g -Wall -Werror -ansi -pedantic -o factorielle factorielle.c
factorielle.c: In function `factorielle':
factorielle.c:6: parse error before `n'
cc1: warnings being treated as errors
factorielle.c: At top level:
factorielle.c:11: warning: return-type defaults to `int'
indique que dans la fonction factorielle du fichier source factorielle.c, il y a une erreur. C'est une erreur de
syntaxe la ligne 6 (while (i n)) o il manque un op rateur entre i et n. La seconde erreur est une admonestation
(warning) la ligne 11 (main()), o le type retourn par la fonction main a t fix par d faut int. Lisez donc
toujours bien les messages d'erreurs. Ex cutez ensuite ce programme.
% factorielle
362880
Exercice 2
Question 1
1. Placez un point d'arr t avant l'appel la fonction factorielle().
2. Ex cutez alors le programme en mode pas pas (step pour entrer dans la fonction, next saute les appels de
fonction).
3. Affichez les valeurs des variables res et i (display) : leurs valeurs sont quelconques car elles n'ont encore pas
t initialis es. Les variables locales sont en effet allou es automatiquement dans la pile, qui cet instant de
l'ex cution peut contenir n'importe quoi.
4. En continuant ex cuter en mode pas pas, vous verrez les variables res et i voluer au cours des it rations
de la boucle.
Question 2 Relancez l'ex cution du programme (run) avec le m me point d'arr t. Modifier la valeur de n dans la
fonction main() :
(xxgdb) set n=12
et terminez son ex cution (cont). Le programme a bien calcul la factorielle de 12, et non pas de 10. Recommencez
avec n=13, et v rifiez que le r sultat n'est pas gal 13 fois le pr c dent. Les entiers de type int ne font bien que 32
bits sur cette machine.
Exercice 3
Le pr processeur permet de s lectionner des zones de code compiler ou ne pas compiler selon diff rentes conditions.
Il est ainsi possible de tester l'existence de macros avec les directives #ifdef/#endif, et d'inclure ou non le code
compris entre ces deux directives. On peut aussi tester la valeur d'une macro avec la directive #if/#endif. Ceci s'av re
tr s utile pour fournir des programmes pouvant tre compil s sur diff rentes architectures. Par exemple :
#if ARCH = i386
/ code pour un intel i386 /
#endif
#if ARCH = alpha
/ code pour un DEC Alpha /
#endif
Publicité
Question 1 Renommez la fonction factorielle() en factorielle_iterative(). crivez la fonction
unsigned int factorielle_recursive (unsigned int n);
qui calcule la factorielle de n de fa on r cursive. Placez les corps de ces fonctions dans une directive #ifdef/#endif
de la mani re suivante :
#ifdef RECURSIVE
unsigned int
factorielle_recursive (unsigned int n)
{
...
}
unsigned int
factorielle (unsigned int n)
{
return factorielle_recursive (n);
}
#else
unsigned int
factorielle_iterative (unsigned int n)
{
...
}
unsigned int
factorielle(unsigned int n)
{
return factorielle_iterative (n);
}
#endif
De cette mani re, si la macro RECURSIVE est d finie, seule la fonction factorielle_recursive() sera d finie, et
l'appel la fonction factorielle() sera un appel factorielle_recursive(). Sinon seule
factorielle_iterative() est d finie, et un appel factorielle sera un appel factorielle_iterative().
Question 2 La macro RECURSIVE peut tre soit d finie dans le fichier source (#define RECURSIVE), soit d finie au
moment de la compilation gr ce l'option -D nom=definition du compilateur. V rifiez le travail du pr processeur
cpp en demandant au compilateur de ne faire que la phase de pr processing :
% gcc -E -P factorielle.c | grep -v '^[ ]*$' | more
Vous voyez appara tre les d clarations de types, variables et fonctions standard, puis celles contenues dans <stdio.h>,
et enfin la d finition de factorielle_iterative(), factorielle() et main(). Recommencez en d finissant la
macro RECURSIVE :
% gcc -E -P -D RECURSIVE factorielle.c | grep -v '^[ ]*$'
C'est bien maintenant la version r cursive qui est d finie.
Question 3 Compilez la version r cursive pour tudier son comportement sous d vermineur (par exemple xxgdb, vous
pouvez pr f rer ddd) :
% gcc -D RECURSIVE -g -Wall -Werror -ansi -pedantic -o factorielle factorielle.c
% xxgdb factorielle
Publicité
1. Placez un point d'arr t avant le return de factorielle_recursive() et lancez l'ex cution (run).
2. Puis avancez de quelques appels r cursifs (step).
3. Vous pouvez examiner la pile des appels de fonctions avec la commande bt, et vous placer dans n'importe quel
cadre d'appel par la commande frame n o n est le num ro du cadre choisi ( gauche dans l'affichage fait par
bt). Par exemple,
4.
5.
6.
7.
8.
9.
(xxgdb) print n
$1 = 4
(xxgdb) frame 3
#3 0x804840d in factorielle_recursive (n=7) at factorielle.c:5
(xxgdb) print n
$2 = 7
Exercice 4
La commande make permet de d terminer automatiquement quelles parties d'un programme sont recompiler et
g n re les commandes pour ce faire. Il est tr s utile pour la compilation s par e, o un programme est constitu de
plusieurs fichiers sources.
Question 1 S parez votre programme en deux fichiers sources factorielle.c et main.c. Le premier ne contient
que les d finitions des fonctions factorielle_iterative(), factorielle_recursive() et factorielle(). Le
second ne contient que la fonction main(). Cr ez un fichier factorielle.h contenant la d claration de la fonction
factorielle :
extern unsigned int factorielle(unsigned int);
et incluez ce fichier dans main.c :
#include <stdio.h>
#include "factorielle.h"
...
Remarquez l'utilisation des doubles quotes pour indiquer au compilateur de chercher le fichier d'ent tes
factorielle.h dans le r pertoire courant en premier. Sans utiliser make, pour compiler le programme, il faut
ex cuter les commandes suivantes:
% gcc -c -Wall -Werror -ansi -pedantic factorielle.c
% gcc -c -Wall -Werror -ansi -pedantic main.c
% gcc -Wall -Werror -ansi -pedantic -o factorielle factorielle.o main.o
L'option -c de gcc permet de sp cifier au compilateur de ne pas faire l' dition de liens, et de ne g n rer que les fichiers
objets .o.
Question 2 Cr ez un fichier Makefile (ou makefile) avec le contenu suivant :
CC = gcc
CFLAGS = -Wall -Werror -ansi -pedantic
CFLAGS += -g
factorielle: factorielle.o main.o
Publicité
$(CC) $(CFLAGS) -o factorielle factorielle.o main.o
factorielle.o: factorielle.c
$(CC) -c $(CFLAGS) factorielle.c
main.o: main.c
$(CC) -c $(CFLAGS) main.c
En modifiant la date du fichier factorielle.c, et en recompilant,
% touch factorielle.c
% make
seule la compilation de factorielle.c et l' dition de liens sont ex cut es, le fichier main.c tant rest inchang .
Question 3 Dans un Makefile, on place aussi une entr e clean pour faire le m nage des fichiers interm diaire et une
entr e realclean qui ne conserve que le strict n cessaire. Ces entr es ne correspondent pas des fichiers
construire, on le pr cise make par la directive .PHONY :
CC = gcc
CFLAGS = -Wall -Werror -ansi -pedantic
CFLAGS += -g
factorielle: factorielle.o main.o
$(CC) $(CFLAGS) -o factorielle factorielle.o main.o
.PHONY: clean realclean
clean:
$(RM) factorielle.o main.o
realclean : clean
$(RM) factorielle
Partisan du moindre effort (et de la moindre erreur), on peut rassembler l'ensemble des fichiers .o dans une variable
OBJ et utiliser la variable automatique $@ :
Exercice 5
Question 1 Modifier Makefile pour pouvoir cr er une biblioth que statique libfact.a et lutiliser par le programme
factorielle.
Question 2 Modifier Makefile pour pouvoir cr er une biblioth que dynamique libfact.so et lutiliser par le programme
factorielle.
Exercice 6 Devant les difficult s et pi ges du langage C, d'autres outils sont aussi a consid rer :
" lint, dont une version moderne est LCLint (install sur le r seau enseignement et disponible
http://lclint.cs.virginia.edu), fournit nombre d'avertissements sur votre code C.
Essayez la commande lclint sur les sources crits lors de ce TP.
" gprof (GNU profiler) analyse les performances d'une ex cution d'un programme. Le programme C doit avoir t
instrument lors de la compilation (option -pg du compilateur). L'ex cution du programme produira un fichier
gmon.out. L'exploitation de ce fichier par gprof permet de visualiser l'arbre des appels des fonctions du
programme, le pourcentage de temps CPU pass dans chacune des fonctions, etc.
" nm liste les symboles pr sents dans un fichier objet (.o) ou un ex cutable. Il est ainsi possible d'identifier les
variables d finies et export es, les fonctions d finies, les fonctions utilis es sans tre d finies... dans un fichier
objet.