Cours gratuits » Cours informatique » Cours programmation » Cours langage C » Cours C 8 outils de developpement et programmation avec exemples

Cours C 8 outils de developpement et programmation avec exemples

Problème à signaler:

Télécharger



★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Cours C 8 outils de développement et programmation avec exemples

Rappel :

Un programme C est essentiellement constitué d’un ensemble de définitions et déclarations de types (voir Section 2.3), variables globales et constantes (voir Section 2.4), et fonctions (voir Chapitre 3). Les variables globales peuvent être modifiées par les fonctions du programme. Le comportement d’une fonction (donné lors de sa définition) est décrit par une séquence d’instructions (voir Chapitre 5) et de définitions de types, constantes et variables locales. Un identificateur (de variable, constante, fonction, type ; voir Section 2.1) doit toujours avoir été déclar&eacut

e; avant de pouvoir être utilisé. On peut noter que dans le cas des fonctions et des types, déclaration ne signifie pas définition : on peut déclarer une fonction sans pour autant donner son comportement ; de même, on peut déclarer un type sans en définir la structure. Cette possibilité est particulièrement intéressante en cas de références croisées : une fonction f fait appel, dans son comportement, à la fonction g qui, dans son comportement, fait aussi appel à la fonction f. Une solution est de déclarer les 2 fonctions puis de les définir à la suite de ces déclarations. Le même problème peut se poser lors de la déclaration d’un type et la même solution peut être apportée à ce problème. Lors de l’invocation du programme, une fonction particulière est appelée : c’est la fonction main (voir Section 3.2). Le comportement de cette fonction donne donc les instructions qui seront exécutées lors de l’appel du programme. Le comportement de la fonction main contient généralement des appels vers les fonctions auxiliaires du programme. Le langage C propose une librairie (c’est-à-dire une ensemble de fonctions) standard qui implémente des opérations courantes ou contient des déclarations de constantes et de types d’utilité générale (voir chapitres 6, 7, 8 et 9).

Expressions

Une expression est une instruction exécutable qui représente une valeur. expr ::= cst | id | ( expr ) | affect-expr | arith-expr | logic-expr | arith-affect-expr | cond-expr | array-access-expr | field-access-expr | func-call-expr | ptr-deref-expr | ptr-get-expr | type-conv-expr | pair-expr | sizeof-expr

n Les sources d'une application réelle :

n plusieurs fichiers sources, pas forcément tous  avec le même langage

n plusieurs fichiers include,

...

n la génération, au sens large, consiste à créer plusieurs librairies et exécutables

n Make est une commande permettant de générer une application d'après la description qui est faite dans un fichier appelé makefile.

n Cette génération est optimisée, seules les opérations nécessaires sont effectuées.

n En cas d'échec d'une opération, make s'arrête.

n Le principe de base est celui de cible/dépendance – action qui forme une arborescence.

n En réalité, peut s'appliquer à d'autres sujets que la génération d'application

n Format du fichier de description  cible: liste de dépendances

<tab> action

n Algorithme de make

si (

(la cible n'existe pas) ou

(il n'y a pas de dépendances) ou

(la cible est plus ancienne qu'au moins une dépendance)

)

Alors exécuter action (1 ou n commandes)

n Autre manière d'exprimer cet algorithme :

"Si au moins une de dépendances est plus récente que la cible, les actions associées sont exécutées (en général, génération de la cible)."

# Exemple de makefile

myprog : myprog.o myutil.o

ld myprog.o myutil.o -o myprog

myprog.o : myprog.c mystd.h

cc -c myprog.c

myutil.o : myutil.c myutil.h mystd.h

cc -c myutil.c

#effacer les fichiers .o clean:

rm -f myprog.o myutil.o

n Les feuilles de l'arbre apparaissent uniquement comme dépendance;

n la racine n'apparaît que comme cible

n les sommets intermédiaires apparaissent a la fois comme cible et comme dépendance.

n Sans l'option -f, make cherche un fichier

makefile puis Makefile (dans le répertoire courant)

n avec l'option -f file, make utilise le fichier file.

$ make

$ make -f mymakefile

n Sans nom de cible comme argument, make reconstruit la première cible trouvée

n Avec un nom de cible en argument, make considère cette cible comme le sommet de l'arbre des dépendances et la reconstruit :

$ make cible

$ make ciblet cible2 ... ciblen

n une cible au sommet d'un arbre et qui n'est pas la première, il faut absolument la nommer pour exécuter les actions associées:

$ make clean

 option -n,

 make affiche les commandes mais ne les exécute pas.

Cette forme est utilisée pour le test.

 option -i

 permet d'ignorer les code d'erreurs des commandes.

 make continue même avec des code retour ≠ 0

 option macro=value

 définir des macros (au sens make)

n peut contenir les types de lignes suivantes :

n lignes blanches: ignorées

n commentaires : #...

n ligne de dépendance: cible: dep1 dep2 ...

n commande: commence par un TAB

n règles implicites: .x.y:

n définition de macros macro=val

n inclusion de fichier include autremakefile

n (instruction conditionnelle, ...)

n + une ligne vide à la fin

 les buts:

 optimiser

 simplifier : make fait tout

 sécuriser

 usage facile pour le développeur utilisateur

 cohérence de l'ensemble obtenu

 Support de contextes lourds

 des dizaines de milliers de fichiers, des centaines de modules, des dizaines de développeurs ...

 Ça vaut le coup de compliquer un peu la syntaxe!

n Objectifs

n simplifier les makefiles, les rendre plus portables, plus évolutifs !

n Définir une macro

n dans le makefile:

macro1=valeur1

n lors de l'appel de make (prioritaire) :

$ make macro1=valeur1 macro2=valeur2

n Pour faire référénce à une macro dans le makefile

$(macro1)

make: exemples de macros

n définir les répertoires où se trouvent les fichiers include

INCLUDE=-I../../malib/include -I../include

n définir les commandes à utiliser:

CCPP=g++

CC=gcc

LINK=g++

n définir les flags de compilation

DEBUGFLAGS=-g -DDEBUG

CFLAGS=$(CFLAGS) $(DEBUGFLAGS)

n Objectifs : simplifier les makefiles, les rendre plus portables, plus évolutifs !

n Syntaxe

$(macro1:substr=replace)

n remplace substr par replace dans la macro macro1.

n Exemple

SRC=main.c utils.c test.c

OBJ=$(SRC:.c=.o)

exec : $(OBJS) macros prédéfinies

 liste des dépendances plus récentes que la cible :

$?

 Exemple : très utile avec la mise à jour des librairies static

lib.a: $(OBJS)

ar r lib.a $?

 nom de la cible courante

 $@

 Très utilisée pour limiter la redondance

lib.a: $(OBJS)

ar rvu $@ $?

n les modificateurs D et F permettent d'obtenir le répertoire uniquement ou le nom de fichier uniquement des macros $@ (et $* et $<)

OBJS = main.o utils.o test.o

CFLAGS = -ansi -DDEBUG -g

exec: $(OBJS)

echo "build de $(@F) dans $(@D)"

$(CC) $(OBJS) -o $@

n Objectifs : simplifier les makefiles, les rendre plus portables, plus évolutifs.

n une règle unique décrit comment générer certains fichiers

n Par exemple, pour générer un module objet à partir d'un fichier source en C.

.SUFFIXES:

.SUFFIXES: .o .c

.c.o :

$(CC) $(CFLAGS) -c $<

Règles d'inférences implicites

 Macros spécifiques

 nom de la dépendance qui entraîne la reconstruction de la cible (car plus récente)

 $<

 nom de la cible sans le suffixe

 $*

Utilisation des modifications F et D

 $(AF) $(*D)

make : divers

n pour avoir le $, il faut utiliser $$

echo $$HOME

n ignorer le code d'erreur d'une commande ponctuellement : ¬

-rm -f $(TMPFILE)

n ne pas afficher la ligne de commande : @

@$(CC) ....

n continuer sur la ligne suivante : caractère \

SRC= 1.c 2.c .... 12.c \

13.c ...

n Parties conditionnelles

n include de makefile

n métacaractères du shell (*,?,[abc]) accepté dans les noms de fichiers

n métacaractère spécifique %

n Pour les fichiers include, il est très fastidieux et quasi-impossible à grande échelle de lister tous les .h inclus par les modules C !

n 2 approches :

n générer automatiquement la liste dépendances correspondantes

n makedepend

n supprimer les .o correspondants pour forcer la regénération.

n Petit programme de 300 ou 400 lignes !

Génération des makefiles

n Les makefiles sont indispensables dans un environnement réel.

n leur gestion peut être lourde et source d'erreurs.

n autoconf, automake, configure

n Adaptation automatique au système d'exploitation, aux différents compilateurs, aux bibliothèques (recherche de la présence des fonctions) et utilitaires présents sur le système (yacc/bison, cc/gcc, lex/flex, awk/gawk,...)

n Très employé dans les distributions GNU

n imake

n utilisation d'un fichier modèle (template) Imakefile.

n cmake développement et/ou déploiement

n make est adapté dans le domaine du développement

n mais aussi pour installer des logiciels,

n appliquer des séries de traitements à des fichiers

n si un arbre de dépendances a un sens, make aussi !

n Besoin : trouver les erreurs dans un programme

n Solution

n un programme capable de simuler l'exécution d'un programme,

n pouvoir faire le lien entre l'exécution du programme et les fichiers sources ayant servi à le générer.

n pouvoir arrêter la simulation à un instant donné, la reprendre,

n pouvoir afficher la valeur d'une expression ou d'une variable,

n pouvoir consulter la pile des appels de fonctions

n Et aussi:

n attacher un programme ayant commencer à s'exécuter

n pouvoir définir à l'avance les points d'arrêt (breakpoint)

n point d'arrêt conditionnel

n gestion des signaux (SEGV)

n modification de variables

n le compilateur doit permettre de stocker des informations relatives aux fichiers sources :

n la table des symboles

n les noms et localisations dans les sources des variables et fonctions, ainsi que les informations de typage.

n liens entre instructions machines et fichiers sources

n Ces informations ne sont pas utiles à l'exécution mais uniquement au debogage.

n Avec gcc, on ajoute ces informations par l'option-g

Table des symboles

n Regarder nm pour comprendre ...

n gcc -c 1.c

n nm -l 1.o

n gcc -c -g 1.c

n nm -l 1.o

n gcc -c -O3 1.c

n nm -l 1.o

gdb

 Nous parlerons ici du debugger GNU, compatible avec gcc : gdb

 Interfaces graphiques

 s'appuient sur gdb :

 ddd, xxgdb, mxgdb, kgdb (kdevelop), ddd, ...

 Langages de programmation

 gdb supporte plusieurs langages de programmations : C, C++, Modula-2, ...

gdb : fonctions principales

n Exécution instruction par instruction

n points d'arrêts (breakpoints), arrêt sur condition,

n arrêt sur signal (par exemple SIGSEGV)

n affichage des valeurs des variables

n évaluation d'expressions

n modification de la valeur de variable

n affichage de la pile d'appel des fonctions

n affichage du code source ou assembleur

 $ gdb a.out

$ gdb

 (gdb) file a.out

 si le programme s'exécute dèjà:

 $ gdb

 (gdb) attach 4425

 (gdb) run arg1 arg2 ...

 Les arguments sont donnés comme sur la ligne de commande.

 L'environnement

 hérité de GDB et donc du shell ayant lancé gdb.

 modifié par :

 (gdb) set environment variable=value

 répertoire courant de gdb, donc du shell ayant lancé gdb.

 modifié par :

 (gdb) cd /var/tmp

 La commande list (ou l) avec optionnellement une référence à une ligne, qui peut être:

 absolue :

 nom du fichier:numéro de ligne

 (gdb) l test1.c:52

 absolue pour le fichier courant:

 (gdb) l 52

 relative à la position courante

 (gdb) l +6

 (gdb) l -10

 une référence à un nom de fonction

 (gdb) l test1.c:unefonc

 (gdb) l unefonc

n Possibilité de mettre des breakpoints en référence à une ligne ou à une fonction

n (gdb) break main

n (gdb) b main

n (gdb) b 556

n Commandes principales: break, clear

n possibilité d'indiquer une condition booléenne

n (gdb) help breakpoint

Après un breakpoint

n continue (c) pour continuer l'exécution

n step (s) pour s'arrêter à la ligne suivante, en rentrant éventuellement dans les appels de fonctions

n next (n) pour s'arrêter à la ligne suivante, sans rentrer dans les appels de fonctions

n finish pour aller jusqu'à la fin de la fonction courante.

n (gdb) help running

n bt (ou where) : affiche la pile des appels

n up : remonter dans la pile des appels

n down : descendre dans la pile des appels

n pour inspecter des variables

n pas de modification d'exécution !

n (gdb) bt full pour voir aussi les variables locales

n Intéressant pour bien comprendre les appels récursifs

Pile + variable locales (gdb) bt

#0 f (k=2, z=3.14159203) at td2_ex3.c:10

#1 0x08048426 in f (k=3, z=3.14159203) at td2_ex3.c:7

#2 0x080484dd in main (argc=1, argv=0xbff27fb4) at td2_ex3.c:25

(gdb) bt full

#0 f (k=2, z=3.14159203) at td2_ex3.c:10

tmp = 3.14159203

#1 0x08048426 in f (k=3, z=3.14159203) at td2_ex3.c:7

tmp = 0

#2 0x080484dd in main (argc=1, argv=0xbff27fb4) at td2_ex3.c:25

a = 3.14159203

b = 3

r = 0

Affichage des valeurs

n print p ou print(p)

n affiche la valeur d'une expression

n printf

n utilisation de description de format ressemblant

au printf du C

n display var ou expression

n display automatique à chaque point d'arrêt

 La commande info permet d'obtenir de nombreuses informations sur le programme en cours de debug.

 info source : nom du fichier courant

 info sources : liste des fichiers sources

 Y compris ceux qu'on ne connaît pas ! (libs)

 info types : description des types connus

 info functions : description des fonctions

 info variables : description des variables

 Y compris stdin, stdout, ...

 Interne

 (gdb) help

 (gdb) help <sous-rubrique>

 (gdb) help <commande>

 Intégration emacs

 ALT-x compile

 ALT-x gdb

 affichage des sources

 positionner les breakpoints

 Aide GNU : plus de 200 pages !!!

n examiner les symboles

n strip

n suppression des symboles

$ nm -l 1.o

$ strip 1.o

$ nm -l 1.o


547