Cours de Langage C UNA
Cours de Langage C UNA
Cours de Langage C UNA
PROGRAMMATION
STRUCTURE
Le langage C
UNA/UFR-SFA H. EDI
PROCESSUS DE DEVELOPPEMENT 2
le délai de mise sur le marché et les coûts de développement sont des facteurs décisifs
La maîtrise du Génie Logiciel devient alors une nécessité incontournable pour les entreprises
développant des systèmes logiciels de grandes tailles ou à exploitation critique :
Projet logiciel
Système de conduite
Système de conduite de centrale nucléaire
de procédés industriels
PROCESSUS DE DEVELOPPEMENT 3
Aujourd’hui, deux grandes familles de processus de développement définissent la manière dont ces
phases doivent s’enchaînées :
les processus séquentiels : modèle en cascade, modèle en V, …
les processus itératifs : Unified Process (UP, RUP, 2TUP,…), méthodes agiles (XP,…)
La plupart de ces processus s’appuie sur une modélisation orientée objet
ETAPE 1 n : Conception.
Modélisation du monde réel
ETAPE 2 n ETAPE 3 n
Programmation Cycle n Test du programme
Implémentation
Projet Informatique
PROCESSUS DE DEVELOPPEMENT 4
Le langage C++
Génération
de code
Visual C++ 6.0 Pro
Rational Rose Visual Studio. net
Reverse
Rational Rose Enterprise Edition.lnk engineering Microsoft Visual C++ 6.0.lnk
APPROCHE OBJET 5
Diagramme de classes,
Organigramme
Diagramme de séquences,
etc …
Le langage C est un langage de bas niveau dans le sens où il permet l’accès à des données que
manipulent les ordinateurs (bits, octets, adresses) et qui ne sont pas souvent disponibles à partir de
langages évolués tels que Fortran, Pascal ou ADA.
Le langage C a été conçu pour l’écriture de systèmes d’exploitation et du logiciel de base. Plus de 90%
du noyau du système UNIX est écrit en langage C. Le compilateur C lui-même est écrit en grande partie
en langage C ou à partir d’outils générant du langage C. Il en est de même pour les autres outils de la
chaîne de compilation (assembleur, éditeur de liens, pré-processeur). De plus, tous les utilitaires du
système sont écrits en C (shell, outils).
Il est cependant suffisamment général pour permettre de développer des applications variées de type
scientifique ou encore pour l’accès aux bases de données. Par le biais des bases de données, il est
utilisé dans les applications de gestion. De nombreux logiciels du domaine des ordinateurs personnels,
tels que Microsoft Word ou Microsoft Excel, sont eux-aussi écrits à partir de langage C ou de son
successeur orienté objet.
Fichier source
STRUCTURE DE BASE 9
Délimiteurs de commentaires
/* et */ pour des commentaires qui se prolongent sur plusieurs lignes
// pour un commentaire en fin de ligne (unique)
Le point d’entrée de tout
programme est repéré par la
procédure main() /* programme principal
------------------- */
principal.cpp
void main ( )
Début du bloc {
déclaration var 1 ; // pression Un bloc d’instructions est
Le corps du programme … divisé en 2 zones :
zone de déclaration,
(ou d’un bloc d’instructions) déclaration var n ;
est toujours délimité par séquence d’instructions,
les accolades { et }
instruction 1 ;
…
instruction m ;
Fin du bloc
}
REGLES DE COMPILATION
Règle 1 : toute variable ou constante doit être déclarée avant de pouvoir être utilisée
Règle 2 : tout type défini par l’utilisateur doit être déclaré avant de pouvoir être utilisé.
Règle 3 : tout sous-programme (procédure ou fonction) doit être déclaré avant de pouvoir être
appelé.
INTERET DE LA DECLARATION
permet de vérifier dès la compilation que :
l’utilisation d’une variable ou d’un type est cohérent (affectation de valeurs compatibles avec le
type ou la structure de la variable),
l’appel d’un sous-programme est cohérent avec la manière dont celui-ci est spécifié
(notamment, bon nombre et bon type d’arguments)
#include ”nom.h”
TYPES SIMPLES PREDEFINIS 13
Type Algorithme C
entier simple signé ENTIER int
entier long signé Entier long
réel simple précision REEL float
réel double précision REEL double
caractère CHARACTERE char
pointeur POINTEUR *
non typé - void
Remarques :
Attribut unsigned pour les types à valeur entière (char, int, long)
Un entier égal à 0 est associé à la valeur booléenne false (et 0, à la valeur true)
L’instruction typedef permet de renommer un type :
typedef ancien_nom nouveau_nom;
DECLARATION D’UNE VARIABLE 14
Obligatoire Facultatif
CONSTANTE : Entité dont la valeur ne peut pas être modifiée durant l’exécution du
programme,
const id_type id_const = valeur ;
Mot clef C Obligatoire
Exemple :
const int a = 12 ;
const PRECISION = 5.0E-6 ;
const double VOLUME_MAX = 20.0 ;
const char Lettre = ’a’ ;
enum Les_Mois { JANVIER=1, FEVRIER, MARS, AVRIL, MAI, JUIN, JUILLET, AOUT,
SEPTEMBRE, OCTOBRE, NOVEMBRE, DECEMBRE} ;
Exemples :
Logical TERMINER ;
Noms de type Feux_Tricolore Feux = ROUGE ;
« énumération » Les_Mois Precedent = AOUT ;
Noms des variables de type « énumération »
Seuls les opérateurs de comparaisons (>,<, ==, !=,<=,>=) sont directement utilisables,
Les_Mois Courant;
Les_Mois Suivant = OCTOBRE ;
if (Courant < Suivant) ...
Ils sont formés de caractères quelconques placés entre les symboles /* et */ ou //.
Ils peuvent apparaître à tout endroit du programme où un espace est autorisé. En
général, cependant, on se limitera à des emplacements propices à une bonne
lisibilité du programme.
LES INSTRUCTIONS
INSTRUCTIONS D’ENTREE/SORTIE 21
Sortie standard
Entrée standard l’écran
le clavier
spécificateurs de format
Pour réaliser ces transformations ces fonctions sont guidées par des formats qui
décrivent le type des objets manipulés (vision interne) et la représentation en
chaîne de caractères cible (vision externe).
Par exemple, un format du type %x signifie d’une part que la variable est du type
entier et d’autre part que la chaîne de caractères qui la représente est exprimée
en base 16 (hexadécimal).
Pour printf(), un format est une chaîne de caractères dans laquelle sont insérés
les caractères représentant la ou les variables à écrire.
Pour scanf(), un format est une chaîne de caractères qui décrit la ou les variables
à lire.
Pour chaque variable, un type de conversion est spécifié. Ce type de
conversion est décrit par les caractères qui suivent le caractère “%”.
résumé des déclarations de variables et des formats 23
déclaration lecture écriture format externe
int i ; scanf ("%d",&i); printf ("%d",i ); décimal
int i ; scanf ("%o",&i); printf ("%o",i ); octal
int i ; scanf ("%x",&i); printf ("%x",i ); hexadécimal
unsigned int i ; scanf ("%u",&i); printf ("%u",i ); décimal
short j ; scanf ("%hd",&j); printf ("%d",j ); décimal
short j ; scanf ("%ho",&j); printf ("%o",j ); octal
short j ; scanf ("%hx",&j); printf ("%x",j ); hexadécimal
unsigned short j ; scanf ("%hu",&j); printf ("%u",j ); décimal
long k; scanf ("%ld",&k); printf ("%ld",k); décimal
long k; scanf ("%lo",&k); printf ("%lo",k); octal
long k; scanf ("%lx",&k); printf ("%lx",k); hexadécimal
unsigned long k; scanf ("%lu",&k); printf ("%lu",k); décimal
Exemple
# include <stdio.h>
C/C++
vrai faux
condition_logique
if (condition_logique)
{
séquence 1 séquence 2 séquence 1 ;
}
else
{
séquence 2 ;
}
vrai if (condition_1)
Condition_1 séquence 1
{
séquence 1 ;
faux }
vrai else if (condition_2)
Condition_2 séquence 2 {
séquence 2 ;
faux }
vrai else if (condition_n )
Condition_n séquence n {
séquence n ;
faux }
séquence n+1 else
{
séquence n+1 ;
}
- permet « d’aiguiller » l’exécution du programme vers le bloc d'instructions dont la condition logique
d’entrée est vérifiée,
#include <stdio.h>
#define TAUX_TVA 18.6
main()
{
double ht, ttc, net, tauxr, remise ;
printf("donnez le prix hors taxes : ") ;
scanf ("%lf", &ht) ; EXECUTION
ttc = ht * ( 1. + TAUX_TVA/100.) ;
if ( ttc < 1000.) donnez le prix hors taxes : 500
tauxr = 0 ; prix ttc 593.00
else if ( ttc < 2000 ) remise 0.00
tauxr = 1. ; net à payer 593.00
else if ( ttc < 5000 ) __________________________
tauxr = 3. ; donnez le prix hors taxes : 4000
else prix ttc 4744.00
tauxr = 5. ; remise 142.32
remise = ttc * tauxr / 100. ; net à payer 4601.68
net = ttc - remise ;
printf ("prix ttc %10.2lf\n", ttc) ;
printf ("remise %10.2lf\n", remise) ;
printf ("net à payer %10.2lf\n", net) ;
}
INSTRUCTIONS CONDITIONNELLES 30
valeur_1
expression séquence 1
switch ( expression )
{
valeur_2
séquence 2 case valeur_1 : séquence 1 ;
break ;
valeur_3 case valeur_2 : séquence 2 ;
séquence 3 break ;
case valeur_3 : séquence 3 ;
break ;
valeur_n
séquence n
default case valeur_n : séquence n ;
break ;
séquence n+1 default : séquence n+1 ;
}
- permet « d’aiguiller » l’exécution du programme vers le bloc d'instructions dont la condition d’entrée (valeur associée au
case) correspond au résultat de l’expression (située dans le switch),
- la valeur de expression doit être un type discret (char, int, long) ou un type énumération,
- l'étiquette default n'est pas obligatoire mais recommandée,
- l’instruction break permet de sortir de la structure switch après l’exécution des instructions associées à un case donné,
- Pour exécuter la même séquence d’instructions pour des case différents (conjonction de valeurs), il
suffit d’écrire ces case consécutivement et de n’associer le break qu’au dernier case de la liste.
Exemple de switch 31
main()
{
int n ;
initialisation C/C++
for ( var=début ; var<=fin ; var=var+pas )
faux {
condition_logique
séquence d'instructions ;
}
vrai
séquence
d'instructions
Pas < 0
modifieur
C/C++
for ( var=début ; var>=fin ; var=var+pas )
{
séquence d'instructions ;
}
- à utiliser lorsque le nombre de répétition est connu
d'avance (1 à n fois),
- doit être interprétée comme une structure while
Exemple de for 33
main() Exécution
{
int i ;
bonjour 1 fois
for ( i=1 ; i<=5 ; i++ ) bonjour 2 fois
{ bonjour 3 fois
printf ("bonjour ") ; bonjour 4 fois
printf ("%d fois\n", i) ;
bonjour 5 fois
}
}
C/C++
faux
condition_logique
vrai
while ( condition_logique )
{
séquence séquence d'instructions ;
d'instructions }
TD 1 : Exercice 4.4
Exemple de while() 35
main() Exécution
{ donnez un nombre : 15
int n, som ; donnez un nombre : 25
donnez un nombre : 12
som = 0 ; donnez un nombre : 60
while (som<100)
{ somme obtenue : 112
printf ("donnez un nombre : ") ;
scanf ("%d", &n) ;
som += n ;
}
printf ("somme obtenue : %d", som) ;
}
répète l’instruction qui suit (ici un bloc) tant que la condition mentionnée est
vraie, comme le ferait do... while. En revanche, cette fois, la condition de
poursuite est examinée avant chaque parcours de la boucle et non
après. Ainsi, contrairement à ce qui se passait avec do... while, une telle
boucle peut très bien n’être parcourue aucune fois si la condition est fausse
dès qu’on l’aborde (ce qui n’est pas le cas ici).
INSTRUCTIONS DE REPETITION 36
C/C++
séquence
d'instructions
do
{
séquence d'instructions ;
}
condition_logique while ( condition_logique ) ;
vrai
faux
- utilisée lorsque le nombre de répétition est a priori inconnu (1 à ? fois),
TD 1 : Exercice 4.6
Exemple de do … while() 37
Exécution
main() donnez un nb >0 : -3
{ vous avez fourni -3
int n ; donnez un nb >0 : -9
do vous avez fourni –9
{ donnez un nb >0 : 12
printf ("donnez un nb >0 : ") ; vous avez fourni 12
scanf ("%d", &n) ; réponse correcte
printf ("vous avez fourni %d\n", n) ;
}
while (n<=0) ;
printf ("réponse correcte") ;
}
répète l’instruction qu’elle contient (ici un bloc) tant
que la condition mentionnée (n<=0) est vraie (c’est-à-
dire, en C, non nulle). Autrement dit, ici, elle demande
un nombre à l’utilisateur (en affichant la valeur lue)
tant qu’il ne fournit pas une valeur positive.
38
LES TABLEAUX
TABLEAUX A UNE DIMENSION 39
TABLEAU : Structure de données de dimension finie dont les éléments sont tous de
même type
Exemple :
int tab[5] ; (tableau de 5 entiers)
float Temperature[3] = {23.0, 10.4, 34.1 }; (tableau de 3 réels initialisés)
unsigned char PDU[] = { 0x1C, 0xFE, 0x2A, 0x10 } ; (dimension 4)
double Cond_Init[6] = { 5.0E-3, 1.0e-8 } ; ( 4 derniers éléments initialisés à 0)
Remarques :
en C, les tableaux commencent à l’indice 0 ( indice du 1er élément : 0),
pas de vérification de débordement,
Exemple pour remplir le tableau de 10 entier:
For(i=0;i<10;i++)
scanf(‘’%d’’,&tab[ i ]);
CHAINE DE CARACTERES 40
CHAINE DE CARACTERE :
Tableau de caractères où le dernier caractère est le caractère \0 (délimiteur de
chaîne).
"Chaine" C h a i n e \0 (Attention : ‘a’ “a“)
La fonction strlen: La fonction strlen fournit en résultat la longueur d’une chaîne dont on lui
a transmis l’adresse en argument.
strlen ("bonjour") vaudra 7 ; de même, avec : char * adr = "salut" ; strlen (adr) vaudra 5.
La fonction strcat: strcat ( but, source ); Cette fonction recopie la seconde chaîne (source)
à la suite de la première (but), après en avoir effacé le caractère de fin.
La fonction strncat: strncat (but, source, lgmax); travaille de façon semblable à strcat en
offrant en outre un contrôle sur le nombre de caractères qui seront concaténés à la chaîne
d’arrivée (but).
La fonction: strcmp ( chaîne1, chaîne2 ); compare deux chaînes dont on lui fournit
l’adresse et elle fournit une valeur entière définie comme étant :
● positive si chaîne1 > chaîne2 (c’est-à-dire si chaîne1 arrive après chaîne2, au
sens de l’ordre défini par le code des caractères) ;
● nulle si chaîne1 = chaîne2 (c’est-à-dire si ces deux chaînes contiennent exactement
la même suite de caractères) ;
● négative si chaîne1 < chaîne2.
Par exemple (quelle que soit l’implémentation) : strcmp ("bonjour", "monsieur") est négatif et :
strcmp ("paris2", "paris10") est positif.
42
Généralités sur les fonctions portant sur des chaînes
La fonction : strncmp ( chaîne1, chaîne2, lgmax ); travaille comme strcmp mais elle limite
la comparaison au nombre maximal de caractères indiqués par l’entier lgmax.
Par exemple : strncmp ("bonjour", "bon", 4) est positif tandis que : strncmp ("bonjour", "bon",
2) vaut zéro.
La fonction : strcpy ( but, source ); recopie la chaîne située à l’adresse source dans
l’emplacement d’adresse destin. Là encore, il est nécessaire que la taille du second
emplacement soit suffisante pour accueillir la chaîne à recopier, sous peine d’écrasement
intempestif. Cette fonction fournit comme résultat l’adresse de la chaîne but.
La fonction : strrchr ( chaîne, caractère ); réalise le même traitement que strchr, mais en
explorant la chaîne concernée à partir de la fin. Elle fournit donc la dernière occurrence du
caractère mentionné.
TABLEAU MULTIDIMENSIONNEL :
Remarque : l’initialisation d’un tableau multidimensionnel est réalisé ligne par ligne
For(i=0;i<10;i++)
For(j=0;j<10;j++)
scanf(‘’%d’’,&tab[ i ] [ j ]);
Les enregistrements : le type structure 44
Une structure est donc une variable composée de plusieurs champs qui sert à
représenter un objet réel ou un concept.
Par exemple une voiture peut être représentée par les renseignements suivants :
la marque, la couleur, l’année, . . .
Le langage C permet de définir des modèles de structures comme les autres
langages évolués.
Déclaration d’une structure 45
Par exemple, si les structures art1 et art2 ont été déclarées suivant le modèle enreg
défini précédemment, nous pourrons écrire :
art1 = art2 ; Une telle affectation globale remplace avantageusement :
art1.numero = art2.numero ;
art1.qte = art2.qte ;
art1.prix = art2.prix ;
Notez bien qu’une affectation globale n’est possible que si les structures ont été définies
avec le même nom de modèle ; en particulier, elle sera impossible avec des
variables ayant une structure analogue mais définies sous deux noms différents.
L’opérateur d’affectation et, comme nous le verrons un peu plus loin, l’opérateur d’adresse
& sont les seuls opérateurs s’appliquant à une structure (de manière globale).
Remarque : L’affectation globale n’est pas possible entre tableaux. Elle l’est, par contre,
entre structures. Aussi est-il possible, en créant artificiellement une structure contenant un
seul champ qui est un tableau, de réaliser une affectation globale entre tableaux.
Utilisation d’une structure 48
Initialisations de structures
On retrouve pour les structures les règles d’initialisation qui sont en vigueur pour tous les
types de variables, à savoir :
● En l’absence d’initialisation explicite, les structures de classe statique sont, par défaut,
initialisées à zéro ; celles possédant la classe automatique ne sont pas initialisées par
défaut (elles contiendront donc des valeurs aléatoires).
Vous voyez que la description des différents champs se présente sous la forme d’une liste
de valeurs séparées par des virgules, chaque valeur étant une constante ayant le type du
champ correspondant. Là encore, il est possible d’omettre certaines valeurs.
Utilisation d’une structure : typedef 49
La déclaration typedef permet de définir ce que l’on nomme en langage C des types
synonymes.
A priori, elle s’applique à tous les types et pas seulement aux structures. C’est pourquoi
nous commencerons par l’introduire sur quelques exemples avant de montrer l’usage que
l’on peut en faire avec les structures. Exemples d’utilisation de typedef
De même : typedef int * ptr ; signifie que ptr est synonyme de int *. Les déclarations
suivantes sont équivalentes : int * p1, * p2 ; ptr p1, p2 ;
Notez bien que cette déclaration est plus puissante qu’une substitution telle qu’elle
pourrait être réalisée par la directive #define. Nous n’en ferons pas ici de description
exhaustive, et cela d’autant plus que son usage tend à disparaître. À titre indicatif, sachez,
par exemple, qu’avec la déclaration : typedef int vecteur [3] ;
les déclarations suivantes sont équivalentes : int v[3], w[3] ; vecteur v, w ;
Typedef : Application aux structures 50
struct enreg
{
int numero ;
int qte ;
float prix ;
};
typedef struct enreg s_enreg ;
s_enreg art1, art2 ;
ou encore, plus simplement :
typedef struct
{
int numero ;
int qte ;
float prix ;
} s_enreg ;
s_enreg art1, art2 ;
Par la suite, nous ne ferons appel qu’occasionnellement à typedef, afin de ne pas vous
enfermer dans un style de notations que vous ne retrouverez pas nécessairement dans
les programmes que vous serez amené à utiliser.
Tableaux de structures 51
Supposez que, à l’intérieur de nos structures employe et courant définies, nous ayons besoin
d’introduire deux dates : la date d’embauche et la date d’entrée dans le dernier poste
occupé. Si ces dates sont elles-mêmes des structures comportant trois champs
correspondant au jour, au mois et à l’année, nous pouvons alors procéder aux déclarations
suivantes : struct date
{
int jour, mois, annee ;
};
struct personne
{
char nom[30] , prenom[20] ;
float heures [31] ;
struct date date_embauche ;
struct date date_poste ;
} employe, courant ;
Vous voyez que la seconde déclaration fait intervenir un modèle de structure (date)
précédemment défini.
La notation : employe.date_embauche.annee représente l’année d’embauche
correspondant à la structure employe. Il s’agit d’une valeur de type int.
SOUS-PROGRAMMES ET MODULES
NOTION DE FONCTION ET DE PROCEDURE 54
En C, il n’existe qu’une seule sorte de module, nommé fonction (il en ira de même en C++
et en Java, langage dont la syntaxe est proche de celle de C). Ce terme, quelque peu abusif,
pourrait laisser croire que les modules du C sont moins généraux que ceux des autres
langages. Or il n’en est rien, bien au contraire ! Certes, la fonction pourra y être utilisée
comme dans d’autres langages, c’est-à-dire recevoir des arguments et fournir un résultat
scalaire qu’on utilisera dans une expression, comme, par exemple, dans : y = sqrt(x)+3 ;
Mais, en C, la fonction pourra prendre des aspects différents, pouvant complètement
dénaturer l’idée qu’on se fait d’une fonction. Par exemple :
● La valeur d’une fonction pourra très bien ne pas être utilisée ; c’est ce qui se passe
fréquemment lorsque vous utilisez printf ou scanf. Bien entendu, cela n’a d’intérêt que parce
que de telles fonctions réalisent une action (ce qui, dans d’autres langages, serait
réservée aux sous-programmes ou procédures).
● Une fonction pourra ne fournir aucune valeur.
● Une fonction pourra fournir un résultat non scalaire (nous n’en parlerons toutefois que dans
le chapitre consacré aux structures).
● Une fonction pourra modifier les valeurs de certains de ses arguments (il vous faudra
toutefois attendre d’avoir étudié les pointeurs pour voir par quel mécanisme elle y parviendra).
Ainsi, donc, malgré son nom, en C, la fonction pourra jouer un rôle aussi général que la
procédure ou le sous-programme des autres langages.
NOTION DE FONCTION 56
Spécifie le type de la La liste d’argument peut être vide mais la Ne pas oublier le ; qui marque le
valeur retournée par la présence des ( ) reste obligatoire fait qu’il s’agit juste d’une
fonction déclaration (pas de corps en
suivant)
APPEL D’UNE FONCTION Liste de paramètres compatibles avec les paramètres formels
Les noms des arguments figurant dans l’en-tête de la fonction se nomment des «
arguments muets », ou encore « arguments formels » ou « paramètres formels »
(de l’anglais : formal parameter). Leur rôle est de permettre, au sein du corps de
la fonction, de décrire ce qu’elle doit faire.
Notez qu’une telle « liberté » n’aurait aucun sens dans le cas des paramètres
formels : il serait impossible d’écrire un en-tête de fexple sous la forme float
fexple (float a+b, ...) pas plus qu’en mathématiques vous ne définiriez une
fonction f par f (x + y) = 5 !
60
FONCTION : Quelques règles
L’instruction return
● L’instruction return peut mentionner n’importe quelle expression. Ainsi, nous
aurions pu définir la fonction fexple précédente d’une manière plus simple :
float fexple (float x, int b, int c)
{
return (x * x + b * x + c) ;
}
● L’instruction return peut apparaître à plusieurs reprises dans une fonction,
comme dans cet autre exemple :
double absom (double u, double v)
{
double s ;
s=a+b;
if (s>0) return (s) ;
else return (-s)
}
Notez bien que non seulement l’instruction return définit la valeur du résultat,
mais, en même temps, elle interrompt l’exécution de la fonction en revenant dans
la fonction qui l’a appelée (n’oubliez pas qu’en C tous les modules sont des
fonctions, y compris le programme principal).
NOTION DE PROCEDURE 61
Quand une fonction ne renvoie pas de résultat, on le précise, à la fois dans l’en-tête et
dans sa déclaration, à l’aide du mot-clé void. Par exemple, voici l’en-tête d’une fonction
recevant un argument de type int et ne fournissant aucune valeur : void sansval (int n)
et voici quelle serait sa déclaration : void sansval (int) ;
Naturellement, la définition d’une telle fonction ne doit, en principe, contenir aucune
instruction return. Certains compilateurs ne détecteront toutefois pas l’erreur. Quand une
fonction ne reçoit aucun argument, on place le mot-clé void (le même que précédemment,
mais avec une signification différente !) à la place de la liste d’arguments (attention, en
C++, la règle sera différente : on se contentera de ne rien mentionner dans la liste
d’arguments). Voici l’en-tête d’une fonction ne recevant aucun argument et renvoyant une
valeur de type float (il pourrait s’agir, par exemple, d’une fonction fournissant un nombre
aléatoire !) : float tirage (void)
Sa déclaration serait très voisine (elle ne diffère que par la présence du point-virgule !) :
float tirage (void) ;
Enfin, rien n’empêche de réaliser une fonction ne possédant ni arguments ni valeur de
retour. Dans ce cas, son en-tête sera de la forme : void message (void)
et sa déclaration sera : void message (void) ;
Remarque En toute rigueur, la fonction main est une fonction sans argument et sans
valeur de retour. Elle devrait donc avoir pour en-tête « void main (void) ». Certains
compilateurs fournissent d’ailleurs un message d’avertissement (« warning ») lorsque vous
vous contentez de l’en-tête usuel main.
DECOMPOSITION EN MODULE 63
: principal.cpp
Déclaration d'éléments situés dans des
#include <librairie.h>
librairies prédéfinies (par exemple,
<stdio.h>, <string.h>, etc.).
#include "liste_proc.h"
Déclaration de procédures (par
exemple) contenues dans des fichiers void main ( )
définis par l'utilisateur (ici, fichiers
{
situés dans le répertoire courant).
// définition des variables ;
// séquence d'instructions ;
nom_proc_2 ( arguments ) ;
}
: liste_proc.h
: liste_proc.cpp
#include <librairie.h>
Modification de
la valeur de a
dans Moyenne
PASSAGE D’ARGUMENTS PAR ADRESSE 65
Lorsqu’une variable est transmise par adresse (ou encore par référence) à un sous-
programme, le contenu de cette variable peut être modifiée par les traitements effectués
sur la variable formelle correspondante de ce sous-programme.
Pour cela, il suffit simplement d’ajouter dans l’entête du sous-programme le symbole & devant
le nom des paramètres formels que l’on désire transmettre par adresse.
au sein du sous-programme, ce paramètre est manipulé comme s’il avait été transmis par valeur,
à l’appel du sous-programme, on fournit simplement la variable en paramètre.
Argument transmis par
référence
#include <iostream.h>
module.h
#include "module.h"
float Moyenne ( int &a, int b ) ;
void main ( )
{ L’adresse de x est transmise au sous-
int x = 100 ;
int y = 300 ;
programme les variables x et a
module.cpp
float Resultat = 0.0 ; occupent alors la même zone
mémoire.
Resultat = Moyenne ( x, y ) ; float Moyenne ( int &a, int b )
cout << "Moyenne de " << x << " et " << y << " = " {
<< Resultat ; float Res ;
} la variable x ressort avec la valeur de a
a = a + b ;
Modification de Res = ( float )(a) / 2.0 ;
Moyenne de 400 et 300 = 200 la valeur de a (et
return ( Res ) ;
donc aussi de x) }
dans Moyenne
RETOUR DE FONCTION 66
operateur.h affichage.h
int minimum ( int val1, int val2 ) ; void Afficher ( int Nbre, char Lettre );
operateur.cpp affichage.cpp
int minimum ( int val1, int val2 ) #include <iostream.h>
{
int min ; void Afficher ( int Nbre, char Lettre )
{
if (val1>val2) int i ;
min = val2 ;
else for (i=1 ; i<=Nbre ; i++)
min = val1 ; {
cout << Lettre ;
return ( min ) ; }
} cout << endl ;
}
}
#include <iostream.h>
#include "operateur.h"
#include "affichage.h"
void main()
{
const int Nmax = 10 ; principal.cpp
int Nbre ;
Mémoire
La phase de déclaration permet au
compilateur de savoir où et comment créer
Adresses en une variable.
int i = 698 ;
64520 0xFC08
64521 0xFC09 10111010 Variable
64522 0xFC0A 00000010 i Au niveau du compilateur, les informations suivantes
64523 0xFC0B sont déduites :
64524 0xFC0C nom de la variable : i
adresse mémoire : 64521 (par exemple)
nombre de registres occupés : 2 (car entier)
valeur initiale : 698 00000010 10111010
LE TYPE * (« pointeur ») 70
Type * : le type * (« pointeur ») est le type à déclarer pour les variables stockant
des adresses mémoires (au même titre que le type int permet de stocker des valeurs
entières, par exemple)
Utilisation
Permet d’accéder à une variable de manière indirecte (par son adresse)
type_élément_pointé * idptr ;
Nom de la variable
Type de l’élément pointé
Identificateur du type « pointeur »
Connaître l’adresse de début d’une zone mémoire n’est pas suffisant pour y accéder,
Il faut aussi connaître la taille de la zone mémoire pointée fournie par le type de
l’élément pointé.
VARIABLE “AUTOMATIQUE” / DYNAMIQUE 71
Variable
Vue de la machine, une variable est définie par une adresse
mémoire et son occupation mémoire.
Opérateur d’indirection * : opérateur unaire qui permet d’accéder (en lecture ou écriture) à la valeur
contenu dans une variable dynamique.
Variable « automatique » Variable dynamique
void main() void main()
{ {
int a ; int * a ;
La variable a n’existe pas encore (non utilisable)
a = 6 ; a = malloc(sizeof(int)) ;
cout << a ; *a = 6 ;
Accès direct à la valeur
contenue dans la variable a printf(format, a) ;
Accès indirect à la valeur de la
*a = *a+1 ; variable a
a = a+1 ;
free(a) ;
La variable a n’existe plus (non utilisable)
} }
Opérateur unaire & : opérateur qui permet d’obtenir l’adresse Adresses en Mémoire
mémoire de toute variable d’un programme Décimale
0
Hexadécimales
0x0000 octet 1
1 0x0001 octet 2
Soit la déclaration de l’entier i initialisé avec la 2 0x0002 octet 3
0x0003 octet 4
valeur 698 (sur un P 16 bits) : 3
4 0x0004 octet 5
int i = 698 ;
Commençons par étudier les deux fonctions les plus classiques de gestion
dynamique de la mémoire, à savoir malloc et free.
La fonction malloc
Premier exemple
Considérez ces instructions :
#include <stdlib.h>
.....
char * adr ;
.....
adr = malloc (50) ;
.....
for (i=0 ; i<50 ; i++) *(adr+i) = 'x' ;
Enfin, il faut savoir que malloc fournit un pointeur nul (NULL) dans le cas où l’allocation
mémoire a échoué. Bien entendu, dans un programme opérationnel, il sera nécessaire de
s’assurer qu’aucun problème de cette sorte n’apparaît.
La gestion dynamique de la mémoire 77
La fonction free
L’un des intérêts essentiels de la gestion dynamique est de pouvoir récupérer des
emplacements dont on n’a plus besoin. Le rôle de la fonction free est de libérer un
emplacement préalablement alloué. Voici un exemple de programme, exécuté ici dans un
environnement DOS. Il vous montre comment malloc peut profiter d’un espace préalablement
libéré sur le tas. Notez que la dernière allocation a pu se faire dans l’espace libéré par le
précédent appel de free.
En fait, pour des fichiers disque (ou disquette), la distinction entre accès
séquentiel et accès direct n’a plus véritablement de raison d’être. D’ailleurs,
comme vous le verrez, en langage C, vous utiliserez les mêmes fonctions dans
les deux cas (exception faite d’une fonction de déplacement de pointeur de
fichier). Qui plus est, rien ne vous empêchera de mélanger les deux modes
d’accès pour un même fichier. Cependant, pour assurer une certaine
progressivité à notre propos, nous avons préféré commencer par vous montrer
comment travailler de manière séquentielle.