Vous êtes sur la page 1sur 42

Langage C (suite )

1
POINTEURS ET TABLEAUX
• Traitement de tableaux de chaînes de caractères
– Remplacement d’un tableau à deux indices par
un tableau de pointeurs Þ technique utile :
économie de place

Encombrement maximal de la chaîne

M a s s a m b a \0

P a u l \0

I b r a \0

D e m b a \0

Fig. Tableau bidimensionnel de caractères


2
POINTEURS ET TABLEAUX

M a s s a m b a \0 P a u l \0 I b r a \0 D e m b a \0

Encombrement réel des caractères


Fig. Tableau de chaînes

3
POINTEURS ET TABLEAUX
• Tableaux de chaînes de caractères
Initialisation de tableaux de pointeurs
char * jour[7] = {“lundi“, “mardi”, “mercredi”, “jeudi”,
“vendredi”, “samedi”, “dimanche” }
– création des 7 chaînes de caractères correspondant aux 7
jours de la semaine
– Initialisation du tableau jour avec les 7 adresses de ces 7
chaînes
main() {
char * jour [7] = {“lundi“, “mardi”, “mercredi”, “jeudi”, “vendredi”,
“samedi”, “dimanche” }
int i;
printf (“Donnez un entier entre 1 et 7 : “);
scanf(‘%d”, &i);
printf(“le jour numero %d de la semaine est %s”, i, jour[i – 1]);
}
4
POINTEURS ET TABLEAUX
• 5) Allocation dynamique
void * malloc(size_t n) – demande la réservation d’un
bloc de taille n
void free( void *p) – libération de la mémoire
Exemple : si on veut copier la chaîne b dans a, on doit
réserver de l’espace pour a, et copier la chaîne
char *a;
char * b = “Une chaine“;
if ( ( a = (char*) malloc (strlen(b) + 1) == NULL) {
printf(“Pas assez de mémoire”); exit(1);
}
strcpy (a, b);
5
POINTEURS ET TABLEAUX
• Allocation d un tableau d entiers et initialisation à 0
int *arr, n, i;
scanf( %d , &n);
if ( (arr = (int*) malloc (n * sizeof(int)) == NULL) {
printf( Pas assez de mémoire ); exit(1);
}
for (i = 0; i < n; i++)
arr[i] = 0;

6
POINTEURS ET TABLEAUX
• Manipulation d un vecteur d entiers
int *construire_vecteur(int dimension) {
int *p, i;
p = (int *) malloc(dimension * sizeof(int)); // Allocation
if ( p == NULL ) {
printf( ²Attention : memoire saturee ...\n²);
return(NULL);
}
for(i = 0; i < dimension; i++) p[i] = 0; // Initialisation
return(p);
}
void detruire_vecteur(int *p) {
if (p) free p;
}
int produit_scalaire(int *v1, int *v2, int dimension) {
int i, produit;
for(i=0, produit=0; i<dimension; i++)
produit+=v1[i]*v2[i];
return(produit);
7
}
POINTEURS ET TABLEAUX
• Allocation d un tableau d entiers
void main() {
int *v1, *v2, i;
v1 = construire_vecteur(3);
v2 = construire_vecteur(3);
for(i = 0; i < 3; i++) {
v1[i] = i;
v2[i] = 2*i;
}
cout << ²v1.v2 = ² << produit_scalaire(v1, v2, 3);
detruire_vecteur(v1);
detruire_vecteur(v2);
}

affichage :
v1.v2 = 10

8
POINTEURS ET TABLEAUX
• Allocation dynamique d un tableau de chaînes de
caractères
char * jour[7] = { lundi , mardi , mercredi , jeudi ,
vendredi , samedi , dimanche }
char **j;
if ( ( j = (char**) malloc (7 * sizeof(char*))) == NULL) {
printf(“Pas assez de mémoire“); exit(1);
}
for ( i = 0 i < 7; i++) {
if ( (j[i] = (char *) malloc (strlen(jour[i]) + 1) == NULL) {
printf(“Pas assez de mémoire”); exit(1);
}
strcpy( j[i], jour[i] ); 9
}
POINTEURS ET TABLEAUX
Pour libérer l’espace :
j for( i = 0; i < 7; i++)
free(j[i]);
free(j);

L u n d i \0

D i m a n c h e \0
10
POINTEURS ET TABLEAUX
Allocation dynamique d une structure
typedef struct vecteur {
int dimension;
int* elements;
} Vecteur;

Vecteur * construire_vecteur(int dimension) {


Vecteur *pv;
pv = (Vecteur *) malloc(sizeof(Vecteur));
if (pv==NULL) return(erreur());
pv->dimension = dimension;
pv->elements =(int *) malloc(dimension * sizeof(int));
if (pv->elements == NULL) return(erreur());
for(i = 0; i < dimension; i++) pv->elements[i] = 0;
return(pv);
}
void detruire_vecteur(Vecteur *pv) {
if (pv) { if (pv->elements) free pv->elements;
free pv; }
}
11
POINTEURS ET TABLEAUX
int produit_scalaire(Vecteur *pv1,Vecteur *pv2) {
int i, produit;
if (pv1->dimension != pv2->dimension) {
printf(²Calcul impossible : vecteurs de dimensions differentes\n ²);
return(-1);
}
for(i = 0, produit = 0; i < pv1->dimension; i++)
produit += pv1->elements[i] * pv2->elements[i];
return(produit);
}
void main()
{
Vecteur *pv1, *pv2, i;
pv1 = construire_vecteur(3);
pv2 = construire_vecteur(3);
for(i = 0; i < 3; i++) {
pv1->elements[i] = i;
pv2->elements[i] = 2*i;
}
printf(²v1.v2 = %d ², produit_scalaire(pv1, pv2));
detruire_vecteur(pv1); detruire_vecteur(pv2); 12
}
POINTEURS ET TABLEAUX
• 5) Déclarateurs complexes
Un déclarateur complexe se compose de 3 choses :
– Un type « terminal » qui est un type de base (numérique), enum,
struct, union ou bien un type auquel on a donné un nom par une
déclaration typedef;
– Un certain nombre d’occurrences des opérateurs ( ), [ ], * qui
représentent des procédés récursifs de construction de types
complexes;
– L’identificateur à déclarer

Pour simplifier l’usage de ces déclarateurs complexes, il


est proposé un procédé mécanique qui fabrique la
déclaration C souhaitée. 13
POINTEURS ET TABLEAUX
Pour cela, appelons description naturelle d’un type une
expression de l’un des types suivants :
– La description correcte en C d’un type de base, d’une enum,
struct ou union ou le nom d’un type baptisé par typedef;
– La formule « tableau de T », où T est la description naturelle
d’un type;
– La formule « adresse d’un T », où T est la description naturelle
d’un type;
– La formule « fonction rendant un T », où T est la description
naturelle d’un type;

14
POINTEURS ET TABLEAUX
Pour obtenir la déclaration d’une variable ou d’une
fonction
1. Ecrire : - à gauche, la description naturelle du type;
- à droite, l’identificateur à déclarer (un seul)
2. Tant que l’expression de gauche n’est pas un type de base,
enum, struct ou union, transformer les deux expressions de la
manière suivante :
fonction rendant un expg expd → expg expd ( )
tableau de expg expd → expg expd [ ]
adresse de expg expd → expg *expd
Exples
Tableau de adresses de int t1
adresses de int t1[ ]
int *t1[ ]

15
POINTEURS ET TABLEAUX
adresse de tableau de int t2
tableau de int *t2
int (*t2) [ ]
parenthèses nécessaires car priorité de [ ] > priorité de *

fonction rendant adresse d’1 fonction rendant void signal


adresse d’1 fonction rendant void signal( )
fonction rendant void *signal( )
void (*signal( )) ( )

16
POINTEURS DE FONCTIONS
• 5) Adresses de fonction
En C, on ne peut pas avoir une variable fonction, mais
on peut avoir une variable adresse de fonction, c’est-à-
dire une variable qui pointe sur une fonction et contient
ainsi son adresse.
De plus, le nom d’une fonction (employé seul) est traduit
par le compilateur en l’adresse de cette fonction
ex
adresse d’une fonction rendant un double f
fonction rendant un double *f
double (*f) ( );

17
POINTEURS DE FONCTIONS
double sin ( );
double (*f) ( );
f = sin;
int (*f) (double, int)
– (*f) – fonction à deux arguments qui renvoie un int
– f – pointeur de fonction
int f1(double, int);
int f2(double, int);
f = f1;
f = f2;

18
POINTEURS DE FONCTIONS
Il devient ainsi possible de programmer des appels de
fonction variable par
(*f) (9.5, 6)
Appel de la fonction dont l’adresse figure dans f (f1 ou f2)

Arguments de type « pointeurs vers une fonction »


L’utilisation d’arguments de type « pointeurs vers une
fonction » permet de faire passer des fonctions comme
arguments d’autres fonctions

19
POINTEURS DE FONCTIONS
Exemple 1 :
typedef struct pers {
char nom[20];
char prenom[20]:
int age;
} PERSONNE;
void personnel( PERSONNE t[ ], int n, void (*f) (PERSONNE)) {
int i;
for(i = 0; i < n; i++)
(*f) ( t[i] );
}
void afficher (PERSONNE p) {

}
main( ) {
PERSONNE table[100];
personnel(table, 100, afficher);
} 20
POINTEURS DE FONCTIONS
Exemple 2 : résolution de f(x)=0 par dichotomie
Soit une fonction f(x) continue. On veut résoudre l'équation f(x)=0 sur un
intervalle [a,b]. Pour cela, on utilise la méthode dichotomique : on divise
l'intervalle [a,b] en deux et on cherche la valeur de la fonction au point milieu; si
le produit f( (a+b)/2) * f(b) est négatif ou nul, on cherche le zéro dans l'intervalle
[ (a+b)/2, b], sinon on le cherche dans l'intervalle [a, (a+b)/2]. Cette recherche
est réitérée jusqu'à ce que l'amplitude de l'intervalle soit suffisamment petite,
c'est à dire plus petite qu'une certaine précision epsilon .

Trouver le zéro de la fonction f(x) = 4x2 + 5x -1 dans l'intervalle [0,1] avec une
précision de 1E-8.

21
POINTEURS DE FONCTIONS
double fonc(double x)
{
return (4*x*x+5*x-1);
}
double dicho(double a,double b,double eps,double (*f)(double))
{
double x;
while (b-a > eps)
if ((*f)(x=(a+b)/2) *(*f)(b) <= 0)
a=x;
else
b=x;
return x;
}
Appel
x = dicho(0.0, 1.0, 1E-8, fonc); 22
POINTEURS DE FONCTIONS
Exemple 3 : tableau de fonctions
Utilisation d un tableau d adresses de fonctions : chaque élément du tableau
est formé de deux champs :
• le nom d une fonction standard, sous forme de chaînes de caractères
• l adresse de la fonction correspondante
Lecture des lignes constituées d un nom de fonction, suivi d un ou plusieurs
Blancs, suivis d un nombre réel; évaluation, arrêt par « fin »

#include <stdio.h>
double sin(double), cos(double), exp(double), log(double);

23
POINTEURS DE FONCTIONS
struct {
char *nom;
double (*fon)(double);
} table[ ] = {
"sin", sin,
"cos", cos,
"exp", exp,
"log", log };
#define NBF (sizeof table / sizeof table[0])
main() {
char nom[80];
double x;
int i;
for(;;) {
scanf("%s", nom);
24
POINTEURS DE FONCTIONS
if (strcmp(nom, "fin") == 0)
break;
scanf("%lf", &x);
for(i = 0; i < NBF && strcmp(table[i].nom, nom) != 0; i++)
;
if (i < NBF)
printf("%f\n", (*table[i].fon)(x));
else
printf("%s ???\n", nom);
}
}
sin 1
0.841471
cos 1
0.540302
fin 25
CHAPITRE 7. GENIE LOGICIEL

26
Modularité :
Un programme mono-module
triselection.c
• 1) Modularité #define N 10 // pseudo constante
/*déclarations des fonctions Þ prototypes)
• Un programme = void saisirTableau(int*t);
void afficherTableau(int *t);
void triSelection(int *t);
un ensemble de
/* définition du main */
fonctions dont la
fonction main main() {
int t[N];
saisirTableau(t);
• Un seul fichier .c =
triSelection(t);
afficherTableau(t);
un unique module }
principal /* définitions des fonctions Þ les corps
void saisirTableau(int *t) {…}
void afficherTableau(int *t) {…}
void triSelection(int *t) {…} 27
Limites de l’approche mono-module

• Difficile à gérer si le programme devient trop « gros »

• Une seule unité de décomposition d’un programme


– La fonction = abstraction d’une action
– Pas d’unité pour abstraire une structure de données et ses opérations
associées

• Pas de réutilisation possible entre les programmes

• Mise au point non facilitée

28
La notion de module
• Définition
Unité de programme composée
de constantes, de types, de variables et de fonctions

• Interface d’un module


Partie accessible par les autres unités de programme
Définition de constantes et de types
Prototypes des fonctions
• Corps d’un module
Partie non accessible aux unités utilisatrices
Définition (initialisation) des variables
Définition des fonctions

29
Un programme structuré en modules
tabio.h main.c
void saisirTableau(int *t, int n);
#define N 10
void afficherTableau(int *t, int n);
#include ²tabio.h²
void triSelection(int *t, int n);
void main() {
tabio.c
int t[N];
#include ²tabio.h²
saisirTableau(t, N);
#include <stdio.h>
triSelection(t, N);
void saisirTableau(int *t, int n) {
afficherTableau(t, N);
… /* corps */ }
}
void afficherTableau(int *t, int n) {
… /* corps */ }
void triSelection(int *t, int n) {
…}
Module principal
30
Module tabio.c
Intérêts des modules
• Réutilisation
– Un module peut être réutilisé par un autre module / programme
• Extensibilité – Flexibilité
– Changement du corps d’un module
– Ajout d’un nouveau service à un module
– Ajout d’un module, remplacement d’un module
• Sécurité
– Une unité utilisatrice n’a pas accès au corps du module
• Validation
– Facilité à tester chaque module indépendamment, en vue de
vérifier le bon fonctionnement de l’application

31
Modules et compilation séparée
• Unités de compilation
– Un fichier .h n’est pas une unité de compilation
– Un fichier .c est une unité de compilation qui inclut le fichier .h
• Règle de base
– Compiler un module (le fichier .c) avant toute unité utilisatrice
• Modification du corps d’un module
– Recompiler le fichier .c du module
– Édition des liens sans recompiler les unités utilisatrices
• Modification de l’interface d’un module
– Recompiler le fichier .c du module
– Recompiler toutes les unités utilisatrices

32
Mise en œuvre d’un programme
modulaire ( sous linux )
• Commandes de compilation
1. $ gcc –c tabio.c → production de tabio.o
2. $ gcc –c main.c → production de main.o
• Commandes d’édition de liens
– $ gcc main.o tabio.o –o tab
→ production du fichier exécutable tab

• Règles pour l’édition de liens


– La recompilation de n’importe quelle unité nécessite de refaire
une édition de liens

33
Utilisation des modules
• Gestion d’un ensemble de fonctions
– Modules standard : stdio, stdlib, string, math, …
– Modules graphiques, bases de données, …

• Gestion d’un ensemble de constantes ou de types


– Gestion de dates avec les jours, les mois, …
– …

• Création de nouveaux types de données


– L’ensemble des nombres complexes avec les opérations +, -, *, …
– Manipulation de points

34
Un module de gestion du type point

point.h Point.c
#include “point.h“
typedef struct point {
int x; POINT milieu (POINT X, POINT Y) {
int y; POINT M;
} POINT; M.x = (X.x + Y.x) / 2;
M.y = (X.y + Y.y) / 2;
POINT milieu(POINT, POINT); return M;
}

35
Un module de gestion du type point
main.c

#include “point.h“
int main( ) {
POINT point_courant;
POINT A = {1, 2};
POINT B = {3, 4};
point_courant = milieu(A, B);
printf(“%d %d”, point_courant.x, point_courant.y);
return 0;
}

36
GENIE LOGICIEL
• 2) Arguments du programme principal
En-tête complet de main
int main(int argc, char *argv[ ])
argc – nombre d’arguments du programme;
argv – tableau de chaînes de caractères (arguments du
programme. Par convention, le premier argument
est le nom du programme lui-même.
ex
Soit un programme de nom « exec » qui est lancé par la commande
exec Mamadou Ibra

37
GENIE LOGICIEL
e x e c \0 M a m a d o u \0 I b r a \0

argc
argv
3

NULL

Affichage de la liste des arguments de « exec »


main(int argc, char*argv[ ] ) {
for(int i = 0; i < argc; i++)
puts(argv[i]);
return 0;
38
}
GENIE LOGICIEL
La commande : exec Mamadou Ibra
produit l’affichage suivant :
exec
Mamadou
Ibra

double fonc(double x)
{
return (4*x*x+5*x-1);
}
double dicho(double a,double b,double eps,double (*f)(double))
{
double x;
while (b-a > eps)
if ((*f)(x=(a+b)/2) *(*f)(b) <= 0)
39
GENIE LOGICIEL
a=x;
else
b=x;
return x;
}
int main(int argc,char *argv[])
{
double x1,a1,b1,eps1;
//a1=0.0;b1=1.0;eps1=1E-8;
clrscr();
if (argc != 4)
{
printf("Mauvais usage de la commande"); exit(1);
}

40
GENIE LOGICIEL
a1=atof(argv[1]);
b1=atof(argv[2]);
eps1=atof(argv[3]);
x1=dicho(a1,b1,eps1,fonc);
printf("x1=%e",x1);
getch();
return 0;
}
Appel
dicho 0.0 1.0 1E-8

∫ab f(x) dx

integrale a b
41
GENIE LOGICIEL
3) Fonction exit
exit met fin à un programme : void exit (int status)
Pour exit status est fourni au processus appelant pour lui indiquer le code de
sortie du processus
En général, une valeur nulle indique une sortie normale et une valeur non
nulle indique une erreur
Valeurs symboliques de status
EXIT_SUCCESS fin normale
EXIT_FAILURE fin anormale, signale au SE que le
programme s’est terminé avec une erreur

42

Vous aimerez peut-être aussi