Cours C pour Étudiants en Ingénierie
Cours C pour Étudiants en Ingénierie
Faicel CHAMROUKHI
chamroukhi@[Link]
http ://[Link]
2
Table des matières
1 Introduction 7
3
4 TABLE DES MATIÈRES
4.4 Identificateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.4.1 Les mots-clefs du Langage C . . . . . . . . . . . . . . . . . . 27
4.5 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.5.1 Constantes littérales . . . . . . . . . . . . . . . . . . . . . . 27
[Link] Constantes entières . . . . . . . . . . . . . . . . . 28
[Link] Constantes réelles (flottants) . . . . . . . . . . . . . 28
[Link] Constantes caractères . . . . . . . . . . . . . . . . 28
[Link] Chaı̂ne de caractères . . . . . . . . . . . . . . . . . 29
4.5.2 Constantes symboliques . . . . . . . . . . . . . . . . . . . . 29
4.5.3 Expressions constantes . . . . . . . . . . . . . . . . . . . . . 29
4.6 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.7 Mémoire d’un programme . . . . . . . . . . . . . . . . . . . . . . . 30
5 Expressions et opérateurs 33
5.1 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5.2 Expressions simples . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.3 Opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.3.1 Opérateurs arithmétiques . . . . . . . . . . . . . . . . . . . . 35
5.3.2 Comparateurs . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.3.3 Les opérateurs logiques booléens . . . . . . . . . . . . . . . 37
[Link] Négation . . . . . . . . . . . . . . . . . . . . . . . 37
[Link] Conjonction et disjonction . . . . . . . . . . . . . . 37
5.3.4 Les opérateurs logiques bit à bit . . . . . . . . . . . . . . . . 38
5.3.5 Changement de type (Conversion) . . . . . . . . . . . . . . . 38
5.3.6 Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
[Link] Les opérateurs d’incrémentation et de décrémentation 39
[Link] Les opérateurs d’affectation composée . . . . . . . 40
5.3.7 L’opérateur virgule . . . . . . . . . . . . . . . . . . . . . . . 40
5.3.8 L’opérateur conditionnel ternaire . . . . . . . . . . . . . . . . 40
5.3.9 L’opérateur adresse . . . . . . . . . . . . . . . . . . . . . . . 40
5.4 Priorité et associativité des opérateurs . . . . . . . . . . . . . . . . . 41
5.5 Saisie de données et affichage de résultats . . . . . . . . . . . . . . . 42
5.6 printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.7 scanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6 Instructions 45
6.1 Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.2 Instruction vide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.3 Instruction expression . . . . . . . . . . . . . . . . . . . . . . . 46
6.4 Bloc d’instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.5 Instruction conditionnelle if − − − else . . . . . . . . . . . . . . . . 47
6.6 Instruction de choix multiple : switch . . . . . . . . . . . . . . . . . 48
6.7 Instructions d’itération (boucles) . . . . . . . . . . . . . . . . . . . . 49
6.7.1 Boucle while . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.7.2 Boucle do − −while . . . . . . . . . . . . . . . . . . . . . . 49
6.7.3 Boucle f or . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
6.7.4 Choisir entre f or et while . . . . . . . . . . . . . . . . . . . 50
6.8 Rupture de séquence : break . . . . . . . . . . . . . . . . . . . . . . 51
6.9 Branchement non conditionnel continue . . . . . . . . . . . . . . . . 51
6.10 Retour d’un appel de fonction . . . . . . . . . . . . . . . . . . . . . 52
TABLE DES MATIÈRES 5
7 Fonctions et programmes 53
7.1 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
7.2 Définition d’une fonction . . . . . . . . . . . . . . . . . . . . . . . . 54
7.2.1 Appel d’une fonction . . . . . . . . . . . . . . . . . . . . . . 55
7.2.2 Fonctions sans arguments ou sans valeur de retour . . . . . . 55
7.2.3 Déclaration d’une fonction . . . . . . . . . . . . . . . . . . . 56
[Link] Durée de vie des variables . . . . . . . . . . . . . 57
7.2.4 Lancement et sortie d’un programme : les fonctions main et exit 57
9 Entrées/Sorties 71
9.1 Saisie et affichage de données dans les E/S standards . . . . . . . . . 72
9.1.1 Lecture et écriture d’un caractère . . . . . . . . . . . . . . . 72
9.1.2 Lecture et écriture d’une chaı̂ne de caractères . . . . . . . . . 73
[Link] Ecriture avec format dans le fichier de sortie standard 73
[Link] Lecture avec format dans le fichier d’entrée standard 74
9.2 Lecture et écriture dans des fichiers texte . . . . . . . . . . . . . . . . 74
9.3 Opérations sur un ficher . . . . . . . . . . . . . . . . . . . . . . . . . 75
9.3.1 Création, ouverture et fermeture d’un fichier . . . . . . . . . 76
9.3.2 Test de fin de fichier ou d’erreur . . . . . . . . . . . . . . . . 76
9.3.3 Lecture et écriture d’un caractère . . . . . . . . . . . . . . . 77
[Link] Lecture caractère . . . . . . . . . . . . . . . . . . 77
[Link] Écriture d’un caractère . . . . . . . . . . . . . . . 77
9.3.4 Lecture et écriture d’une ligne . . . . . . . . . . . . . . . . . 78
[Link] Lecture d’une ligne . . . . . . . . . . . . . . . . . 78
[Link] Écriture d’une ligne . . . . . . . . . . . . . . . . . 78
9.3.5 Lecture et écriture avec format . . . . . . . . . . . . . . . . . 80
9.3.6 Lecture avec format . . . . . . . . . . . . . . . . . . . . . . 80
9.3.7 Écriture avec format . . . . . . . . . . . . . . . . . . . . . . 80
9.4 Positionnement dans un fichier . . . . . . . . . . . . . . . . . . . . . 81
6 TABLE DES MATIÈRES
11 Adresses et pointeurs 93
11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
11.2 Notion d’adresses (pointeurs) . . . . . . . . . . . . . . . . . . . . . . 94
11.2.1 Définition d’un pointeur . . . . . . . . . . . . . . . . . . . . 94
11.2.2 Description d’un type adresse (pointeur) . . . . . . . . . . . . 94
11.3 Adresses de variables, de tableaux et de fonctions . . . . . . . . . . . 95
11.3.1 Adresses de variables . . . . . . . . . . . . . . . . . . . . . . 95
11.3.2 Adresses de structures : pointeurs et structures . . . . . . . . 95
11.3.3 Adresse et valeur d’une variable . . . . . . . . . . . . . . . . 96
[Link] Accès aux champs d’une structure ou d’une union
pointée . . . . . . . . . . . . . . . . . . . . . . . . 97
11.3.4 Adresses de tableaux : pointeurs et tableaux . . . . . . . . . . 97
11.3.5 Arithmétique des pointeurs . . . . . . . . . . . . . . . . . . . 98
11.3.6 Passage d’arguments par adresse . . . . . . . . . . . . . . . . 99
[Link] Passage en argument de l’adresse d’une variable . . 100
[Link] Passage en argument de l’adresse d’une structure . . 100
[Link] Passage en argument de l’adresse d’un tableau . . . 101
11.4 Allocation dynamique de variables ou de tableaux . . . . . . . . . . . 101
11.4.1 Taille des instances d’un type . . . . . . . . . . . . . . . . . 102
11.4.2 Allocation dynamique d’une variable . . . . . . . . . . . . . 102
11.4.3 Allocation dynamique d’un tableau . . . . . . . . . . . . . . 103
11.4.4 Libération d’un emplacement mémoire alloué dynamiquement 104
Chapitre 1
Introduction
Avant propos
Ce cours est dispensé à des étudiants de deuxième année (L2) Sciences de L’Ingénieur
(SI) qui n’ont pas de fait de programmation C avant. Les étudiants ont eu comme pré-
requis des enseignements en algorithmique et programmation sous Python (le langage
enseigné dès la première année de la licence SI).
Cet enseignement est pour un volume horaire de 15 heurs de Cours Magistral (CM)
(accompagnés de 8 heurs de Travaux Dirigés (TD) et de 27 heures de Travaux Pratiques
(TP)).
Je cite en particulier le livre de Jacques Le maı̂tre et celui d’Anne Canteaut qui ont
servi de base pour ce cours :
– Jacques Le Maı̂tre, Programmer en langage C , ISBN : 978-2-7298-7228-1,
2012, Technosup , Ellipses.
– Programmation en langage C, Anne Canteaut, INRIA
7
8 CHAPITRE 1. INTRODUCTION
Avantages du Langage C
– un des langages de programmation les plus utilisés au monde dans des applica-
tions très diverses ;
– un langage simple, ayant un nombre réduit de constructions, et relativement fa-
cile à apprendre et
– a permis le développement de compilateurs performants ;
– a servi de base à d’autres langages très utilisés tels que C++ ou Java ;
– C a été initialement conçu comme langage de programmation UNIX ⇒ s’intègre
parfaitement à l’environnement UNIX.
Inconvénients du langage C
– Il ne permet pas la manipulation globale des tableaux ou des chaı̂nes de ca-
ractères. Il demande au programmeur une certaine agilité à manipuler les adresses
des données : les pointeurs, ce qui n’est pas toujours évident pour les débutants.
– L’utilisation de certains opérateurs peut conduire à des programmes peu lisibles.
⇒ Cependant, on peut contourner ces défauts par une écriture rigoureuse des pro-
grammes et par l’utilisation d’options de compilation fournissant un maximum d’aver-
tissements (e.g., -Wall) sur les incorrections dans le texte d’un programme.
Dans cet enseignement, Le langage C décrit est conforme à la norme ANSI dite
C89.
Chapitre 2
Contents
2.1 Classification des langages de programmation les plus marquants 10
2.1.1 Classement par niveau . . . . . . . . . . . . . . . . . . . . 10
2.1.2 Classement par leur degré de généralité : Langage de pro-
grammation généraliste VS spécialisé . . . . . . . . . . . . 10
2.1.3 Classement par paradigme de programmation . . . . . . . 10
2.2 Les différents paradigmes de programmation . . . . . . . . . . 10
2.3 La programmation impérative (ou procédurale) . . . . . . . . . 11
2.4 La programmation fonctionnelle . . . . . . . . . . . . . . . . . 11
2.5 La programmation logique . . . . . . . . . . . . . . . . . . . . 12
2.6 La programmation orientée objet . . . . . . . . . . . . . . . . . 13
2.7 Chronologie des langages de programmation les plus marquants 14
9
10CHAPITRE 2. PRÉSENTATION DES DIFFÉRENTS PARADIGMES DE PROGRAMMATION
B Remarque 2.1.1. Les langages spécialisés sont souvent des langages de plus haut
niveau que les langages généralistes.
Le lange C est un langage de programmation impérative. Dans la partie suivante,
nous donnons les différents paradigmes de programmation.
3. la programmation logique,
4. la programmation orientée objet.
Description du programme :
– La ligne 1 contient la définition de trois variables : x, y et z dont les valeurs
sont des nombres entiers.
– Les lignes 2, 3 et 4 contiennent chacune une instruction d’affectation qui de-
mande l’enregistrement de la valeur de l’expression qui suit le symbole := dans
la case mémoire associée à la variable dont le nom précède ce symbole.
– La ligne 5 contient une instruction qui demande l’affichage à l’écran de la valeur
de l’expression qui suit le mot-clé afficher.
⇒ L’exécution d’un programme impératif consiste à exécuter les instructions les unes
après les autres, ce qui dans le cas du programme ci-dessous produira l’affichage à
l’écran du nombre 7.
⇒C, pascal, Python sont des langages de programmation impérative.
Caml se prête à la programmation dans un style fonctionnel, impératif ou orienté
objets.
Description du programme :
12CHAPITRE 2. PRÉSENTATION DES DIFFÉRENTS PARADIGMES DE PROGRAMMATION
aire_disque(5);
( 7 ) p a r e n t ( x , y ) s i p è r e ( x , y ) ;
( 8 ) p a r e n t ( x , y ) s i m ère ( x , y ) ;
( 9 ) grand−p a r e n t ( x , z ) s i p a r e n t ( x , y ) e t p a r e n t ( y , z ) ;
Description du programme :
– Les lignes 1 à 6 contiennent des relations qui sont des faits : Alain est le père de
Jean, Nicole est la mère de Jean, etc.
– La ligne 7 contient une relation qui est une règle stipulant que x est un parent de
y si x est le père de y.
– La ligne 8 contient une relation qui est une règle stipulant que x est un parent de
y si x est la mère de y.
– La ligne 9 contient une relation qui est une règle stipulant que x est un grand-
parent de z s’il existe un y tel que x est le parent de y et y est le parent de
z.
2.6. LA PROGRAMMATION ORIENTÉE OBJET 13
⇒ L’exécution d’un programme logique est lancée en posant une question sous la
forme d’une relation à vérifier.
Par exemple, pour connaı̂tre les grands-parents de François, l’utilisateur posera la
question :
grand-parent(x, "François");
qui signifie : “Quelles sont les valeurs de x pour lesquelles la relation
grand-parent(x, "François") est vraie ?”. La réponse sera :
{x = "Alain", x = "Nicole", x = "Paul", x = "Claire"}.
Par exemple, Prolog est un langage de programmation logique.
Notion de classe en programmation orientée objet Une classe définit les propriétés
des objets de cette classe et les opérations que l’on peut leur appliquer.
. Exemple 2.6.1. Par exemple, la classe Personne possédant les propriétés nom et
annee_de_naissance et l’opération âge définie par
age(p) = annee courante - annee_de_naissance(p) où p est un ob-
jet (une instance) de la classe Personne.
Notion d’objet en programmation orientée objet Un objet est défini par un identifi-
cateur unique et invariable et par les valeurs de ses propriétés qui, elles, sont variables.
Héritage Une classe peut être fille d’une autre classe. Dans ce cas, les objets de la
classe fille héritent de toutes les propriétés et de toutes les opérations de la classe
mère.
. Exemple 2.6.2. Par exemple, si la classe Etudiant est une sous-classe de la
classe Personne alors, un étudiant est aussi une personne qui a un nom et une
année de naissance et dont l’âge peut être calculé par l’opération age définie
dans la classe Personne. Un étudiant pourra avoir par ailleurs des propriétés supplémentaires
telle que numero_etudiant, etc.
⇒ En programmation orientée objet, la manipulation des données peut, elle, être
réalisée selon le paradigme impératif ou fonctionnel.
B Remarque 2.6.1. Les langages de programmation utilisent en général les principes
de plusieurs de ces quatre paradigmes. Par exemple, la notion de fonctions, notamment,
est présente dans pratiquement tous les langages de programmation.
Résumé : Le Langage C est un langage de haut niveau (de plus bas que quasiment
tous les langages de haut niveau), généraliste et impératif, ç-a-d conforme au paradigme
de la programmation impérative.
historique : Le langage C a été créé au début des années 1970 par des chercheurs
du laboratoire de la compagnie Bell aux USA. Il a été conçu, à l’origine, pour être
le langage de programmation du système d’exploitation UNIX. C est un langage très
largement diffusé. Il a de plus servi de base au langage orienté objet C++ et au langage
Java très utilisé notamment dans les applications web.
14CHAPITRE 2. PRÉSENTATION DES DIFFÉRENTS PARADIGMES DE PROGRAMMATION
Autres Langages
Structure générale et
compilation d’un programme C
Contents
3.1 Structure générale d’un programme C . . . . . . . . . . . . . . 15
3.1.1 Exemple d’un programme C monofichier . . . . . . . . . . 16
3.1.2 Exécution de ce programme . . . . . . . . . . . . . . . . . 17
3.2 Cycle de vie d’un programme . . . . . . . . . . . . . . . . . . . 17
3.2.1 Compilation et exécution d’un programme C monofichier . 18
Ici nous allons commencé par voir comment est structuré un programme C simple.
Ensuite nous verrons quelles sont les différentes étapes par lesquelles ont doit passer
pour exécuter un tel programme : de la conception, en passant par la compilation et à
l’exécution.
B Remarque 3.1.1. une fonction est dite à effet de bord si elle modifie un état autre
que sa valeur de retour. Par exemple, une fonction peut modifier une variable statique
15
16CHAPITRE 3. STRUCTURE GÉNÉRALE ET COMPILATION D’UN PROGRAMME C
ou globale, modifier un ou plusieurs de ses arguments, écrire des données vers un écran
ou un fichier ou lire des données provenant d’autres fonctions à effet de bord.
Les effets de bord rendent souvent le comportement des programmes plus difficile
à comprendre et nuisent à la réutilisabilité des fonctions et procédures.
Tout programme C doit comporter au moins une fonction : la fonction main (fonc-
tion principale). L’exécution d’un programme C commence par l’appel de cette fonc-
tion.
Un programme C peut appeler des fonctions prédéfinies telles que les fonctions qui
permettent de lire ou d’écrire des données ou les fonctions mathématiques usuelles. Ces
fonctions constituent la bibliothèque standard de C. (par exemple stdio.h stdlib.h
math.h)
Un programme C est exécuté sous le contrôle du système d’exploitation(par exemple
UNIX, Windows de Microsoft ou MacOS d’Apple.). Un OS qui est le logiciel qui as-
sure la gestion de toutes les tâches soumises à l’ordinateur.
Un programme C communique avec l’extérieur par l’intermédiaire des unités d’entrées-
sorties, qui peuvent être un clavier, un écran, une imprimante ou un disque, etc.
⇒ On dit qu’un programme écrit sur une unité de sortie (affichage à l’écran, im-
pression sur papier ou enregistrement sur un disque,...) et qu’il lit sur une unité d’entrée
(données saisies au clavier ou enregistrées sur un disque, etc).
( 2 ) # i n c l u d e < s t d i o . h>
(3) i n t max ( i n t x , i n t y )
(4) {
(5) if (x > y)
(6) return x ;
(7) else
(8) return y ;
(9) }
(10) i n t main ( v o i d )
(11) {
(12) i n t m;
(13) m = max ( 2 5 , 3 0 ) ;
(14) p r i n t f ( ”Le maximum de 25 e t 30 e s t %d . \ n” , m) ;
(15) return 0;
(16) }
L’option facultative -Wall est recommandée car elle génère une liste très complète
d’avertissements (“warnings”) sur des incorrections éventuelles ou des oublis
dans le texte du programme qui pourraient provoquer des problèmes lors de
l’exécution.
L’option facultative -o prog permet d’indiquer le nom du programme éxécutable
à construire (ici prog) après la compilation du programme source prog.c
3. Exécuter le programme en lançant la commande :
./prog
Le système d’exploitation appellera alors la fonction main. Cet appel déclenchera
l’exécution du programme.
B Remarque 3.2.1. Dans le cas où l’option -o prog n’est pas présente, le fichier
exécutable généré aura pour nom [Link] et l’exécution du programme sera lancée par
la commande ./[Link].
Contents
4.1 Types numériques, constantes et variables . . . . . . . . . . . . 21
4.1.1 Données en langage C . . . . . . . . . . . . . . . . . . . . 21
4.2 Représentation des nombres . . . . . . . . . . . . . . . . . . . . 23
4.2.1 Représentation des nombres entiers . . . . . . . . . . . . . 23
4.2.2 Représentation des nombres réels . . . . . . . . . . . . . . 23
4.3 Types numériques . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.3.1 Les types entiers . . . . . . . . . . . . . . . . . . . . . . . 24
4.3.2 Les types flottants . . . . . . . . . . . . . . . . . . . . . . 25
4.3.3 Constantes symboliques INT MIN et INT MAX . . . . . . 26
4.4 Identificateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.4.1 Les mots-clefs du Langage C . . . . . . . . . . . . . . . . 27
4.5 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.5.1 Constantes littérales . . . . . . . . . . . . . . . . . . . . . 27
4.5.2 Constantes symboliques . . . . . . . . . . . . . . . . . . . 29
4.5.3 Expressions constantes . . . . . . . . . . . . . . . . . . . 29
4.6 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.7 Mémoire d’un programme . . . . . . . . . . . . . . . . . . . . . 30
21
22 CHAPITRE 4. REPRÉSENTATION DES DONNÉES EN MÉMOIRE
Tableaux Il est de plus possible de définir des tableaux qui, en C, ne sont pas des
variables mais des suites de variables de même type rangées de façon contigüe dans la
mémoire.
. Exemple 4.1.2. Par exemple, le tableau des nombres d’étudiants des classes de Li-
cence à la fac.
B Remarque 4.1.2. Il n’existe pas de type spécifique pour les booléens, les caractères
et les chaı̂nes de caractères. Le booléen faux est représenté par le nombre zéro et
le booléen vrai par toute valeur différente de zéro (par exemple 1).
Un caractère est codé par un nombre entier (conformément au code ASCII1, en
général). Une chaı̂ne de caractères est représentée comme un tableau de caractères (c.-
à-d. comme un tableau de nombres entiers).
char
int
float double
short long signed unsigned
N = n × b−k .
. Exemple 4.2.1. Par exemple, si k = 3, en base 10 le nombre 17, 648 sera représenté
par le nombre 17648 (on a 17648 = 17, 648 × 10−3 ). Le problème posé par cette
représentation est que la partie décimale devra être tronquée si le nombre de chiffres
24 CHAPITRE 4. REPRÉSENTATION DES DONNÉES EN MÉMOIRE
après la virgule est supérieur à k. Par exemple, le nombre 6, 4817 sera représenté par
6481 et donc tronqué car 6481 × 10−3 = 6, 481.
Remarquons que bien que les nombres 6, 4817 et 17, 648 aient le même nombre de
chiffres significatifs (k = 3), il a fallu tronquer le premier mais pas le second. Pour
résoudre ce problème, une autre représentation a été proposée : la représentation en
virgule flottante qui consiste à introduire un exposant.
. Exemple 4.2.2. Par exemple, si b = 10, le nombre 17, 648 sera représenté par
Format simple et format double pour nombres décimaux Dans la plupart des ar-
chitectures actuelles d’ordinateurs, les nombres décimaux sont représentés en virgule
flottante selon la norme IEEE 754. Cette norme définit deux formats principaux :
– le format simple précision pour des nombres codés sur 32 bits, qui permet de
représenter des nombres dans l’intervalle 10−37 à 1037 avec une précision de 9
chiffres décimaux ;
– le format double précision pour des nombres codés sur 64 bits, qui permet de
représenter des nombres dans l’intervalle 10−307 à 10307 avec une précision de
16 chiffres décimaux.
– les types entiers signés : signed char, short int, int, long int et
les types entiers non signés : unsigned char, unsigned short int,
unsigned int, unsigned long int.
On notera que les noms des types entiers ont plusieurs synonymes (la paire de
crochets autour d’un mot indique que ce mot est facultatif). Par exemple, short
(le plus employé), signed short et signed short int sont des synonymes de
short int. Le type char est destiné à la manipulation des caractères du jeu de ca-
ractères de la machine utilisée (en général, ceux du code ASCII ou d’un code ASCII
étendu).
Le code ASCII de base prend en compte tous les caractères nécessaires à l’écriture
de l’anglais : il leur attribue un code numérique de 0 à 127 (7 bits). Les codes ASCII
étendus profitent du 8e bit pour attribuer un code numérique aux caractères des langues
européennes non pris en compte par le code ASCII de base, les caractères accentués
entre autres.
B Remarque 4.3.1. Attention ! Dans ce cours, nous n’utiliserons que les caractères
du code ASCII de base dans les textes des programmes et dans les fichiers lus par ces
programmes. Les caractères accentués ne seront donc pas utilisés.
Le type int est destiné à la représentation des entiers dont la taille est manipulée
la plus efficacement par la machine utilisée (en général, 2 ou 4 octets).
B Remarque 4.3.2. ⇒ L’intérêt des types non signés est de permettre la représentation
dans le même espace mémoire de nombres positifs ou nuls deux fois plus grands que
s’ils pouvaient aussi être négatifs.
La représentation d’un nombre flottant est caractérisée par son signe, sa mantisse
et son exposant :
26 CHAPITRE 4. REPRÉSENTATION DES DONNÉES EN MÉMOIRE
Notez que l’extension et la précision de ces types sont dépendantes de chaque implan-
tation (architecture de la mémoire de la machine utilisé), mais les valeurs minimales
indiquées dans les tableaux 4.1 et 4.2 ainsi que le respect des contraintes suivantes sont
garanties par la norme ANSI :
1. extension(signed char) ⊆ extension(signed short int) ⊆ extension(int)⊆
extension(signed long int)
2. extension(float) ⊆ extension(double) ⊆ extension(long double)
3. précision(float) ≤ précision(double) ≤ précision(long double)
4. extension(signed type) ⊆ extension(unsigned type) où
type ∈ {char, short int, int, long int}
5. La place mémoire occupée par un entier de type signed type est la même
que celle occupée par un entier de type unsigned type et la représentation
d’un même entier dans chacun de ces deux types est la même.
Les opérations de changement de type (que nous verrons plus tard) s’appuient sur le
respect de ces contraintes. La 1ère contrainte garantit notamment qu’un entier de type
short int est convertible en type int ou long int. Les 2e et 3e contraintes
garantissent notamment qu’un flottant de type float est convertible en type double
ou long double. Enfin, la 4e contrainte garantit que la conversion d’un type signé
en un type non signé se fait sans travail.
4.4 Identificateurs
Les identificateurs servent à nommer les constantes, les variables, les structures,
les unions, les champs d’une structure ou d’une union, les tableaux et les fonctions. Un
4.5. CONSTANTES 27
identificateur est une suite contiguë de caractères dont chacun peut être une lettre, un
chiffre ou le caractère (blanc souligné). Par convention, les identificateurs commençant
par un blanc souligné sont réservés à la bibliothèque standard. Tous les identificateurs
d’un programme utilisateur devront donc commencer par une lettre. Un identificateur
peut avoir de 1 à 31 caractères. Une lettre majuscule est un caractère différent de la
même lettre en minuscule. Le premier caractère d’un identificateur ne peut pas être un
chiffre.
4.5 Constantes
Les valeurs sont des abstractions, mais il faut pouvoir les représenter (les écrire)
dans le texte d’un programme.
Dans un langage de programmation, on appelle constante la représentation d’une
valeur. En C, on distingue :
– les constantes littérales, (entiers, réels, caractères)
– les constantes symboliques
– et les expressions constantes.
Une constante littérale de type chaı̂ne de caractères est notée en plaçant cette chaı̂ne
de caractères entre guillemets ("). Par exemple :
Dans une constante littérale de type caractère ou chaı̂ne de caractères, tout caractère
imprimable ou non, peut être représenté par un caractère ou un nombre octal (en base
8) précédé du caractère d’échappement \ (anti-slash).
Par exemple, la constante littérale "Il a dit : \"Bonjour\"" représente
la chaı̂ne de caractères : “ Il a dit : “Bonjour” ”.
Une chaı̂ne de caractères est une suite de caractères entourés par des guillemets.
Par exemple, "Ceci est une chaı̂ne de caractères" Une chaı̂ne de ca-
ractères peut contenir des caractères non imprimables, désignés par les représentations
vues précédemment. Par exemple, "ligne 1 \n ligne 2" A l’intérieur d’une
chaı̂ne de caractères, le caractère " doit être désigné par \". Enfin, le caractère \ suivi
d’un passage à la ligne est ignoré. Cela permet de faire tenir de longues chaı̂nes de
caractères sur plusieurs lignes. Par exemple,
#define PI 3.14
définit une constante symbolique PI qui désigne le nombre flottant 3.14 : une valeur
approchée de π.
4.6 Variables
Nous nous restreindrons pour le moment à la définition de variables numériques (de
type entier ou flottant). La forme la plus simple d’une définition de variable numérique
est la suivante :
où :
– type est l’un des types numériques des tableaux 4.1 et 4.2 ;
– var1, ... , varn sont les noms des variables définies (des identificateurs) ;
– exp1, ... , expn sont des expressions dont les valeurs constituent les valeurs
initiales respectives des variables définies.
Cette définition déclare n variables de type type, nommées var1,..., varn, et les
crée.
1. La déclaration d’une variable consiste à lui associer un type
2. la création d’une variable consiste à lui attribuer une case de la mémoire et à y
enregistrer sa valeur initiale (initialisation), si elle est indiquée.
L’indication d’une valeur initiale est facultative. L’expression d’une valeur initiale ainsi
que la valeur initiale affectée à une variable lorsque cette valeur n’est pas spécifiée dans
la définition de cette variable, diffèrent selon que cette variable est globale ou locale
comme nous le verrons plus tard.
Par exemple, les définitions :
int i, j, k;
float longueur = 10.5, largeur = 6.34;
char une_lettre = ’a’;
définissent respectivement :
– trois variables i, j et k de type int ;
– deux variables longueur et largeur de type float et de valeurs initiales
respectives 10,5 et 6,34 ;
– une variable une_lettre de type char intialisée au caractère ’a’.
type var v
représente la case mémoire dans laquelle est placée la valeur de la variable (le
type pourra être omis) ;
– un extrait de la pile ou de la mémoire statique apparaitra comme une suite de va-
riables rangées dans l’ordre de leur déclaration, de bas en haut ou éventuellement
de gauche à droite dans le cas des tableaux et le fond de la pile sera marqué par
un double trait :
type var v
type var v
Dans un programme C, il peut y avoir plusieurs variables de même nom mais nous
verrons que les règles de visibilité font qu’en un point du texte d’un programme, il ne
peut pas y avoir deux variables visibles de même nom. En conséquence, la valeur d’une
variable dont le nom est donné est celle qui est liée à l’unique variable visible ayant ce
nom. Dans notre représentation graphique, le nom d’une variable invisible en un point
d’un programme sera grisé :
– une variable var, de type type et de valeur v sera représentée par :
type var v
. Exemple 4.7.1. Considérons, par exemple, le programme C suivant qui définit trois
variables x, y et s (ligne 3), demande à l’utilisateur de saisir la valeur des variables x
et y (lignes 4 et 5), affecte à la variable s la somme des valeurs de x et de y (ligne 6) et
enfin affiche la valeur de s (ligne 7).
(8) return 0;
(9) }
Quand le bloc qui constitue le corps de la fonction main est exécuté (lignes 2 à 8)
les variables qui y sont définies (x, y et s dans cet exemple) sont empilées dans l’ordre
présentée dans la figure ci-après. Quand la valeur de x a été saisie par l’utilisateur, elle
est lue et affectée dans la case associée à x. Il en est de même pour y. Enfin, la valeur
de l’expression x + y est calculée et rangée dans la case associée à s. L’état de la
pile immédiatement après l’exécution de l’instruction 6 sera le suivant, si les valeurs
saisies pour x et y sont 3 et 7 :
int s 10
int y 7
int x 3
Chapitre 5
Expressions et opérateurs
Contents
5.1 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5.2 Expressions simples . . . . . . . . . . . . . . . . . . . . . . . . 34
5.3 Opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.3.1 Opérateurs arithmétiques . . . . . . . . . . . . . . . . . . 35
5.3.2 Comparateurs . . . . . . . . . . . . . . . . . . . . . . . . 36
5.3.3 Les opérateurs logiques booléens . . . . . . . . . . . . . . 37
5.3.4 Les opérateurs logiques bit à bit . . . . . . . . . . . . . . . 38
5.3.5 Changement de type (Conversion) . . . . . . . . . . . . . 38
5.3.6 Affectation . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.3.7 L’opérateur virgule . . . . . . . . . . . . . . . . . . . . . 40
5.3.8 L’opérateur conditionnel ternaire . . . . . . . . . . . . . . 40
5.3.9 L’opérateur adresse . . . . . . . . . . . . . . . . . . . . . 40
5.4 Priorité et associativité des opérateurs . . . . . . . . . . . . . . 41
5.5 Saisie de données et affichage de résultats . . . . . . . . . . . . 42
5.6 printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.7 scanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.1 Expressions
Une expression est formée à l’aide de constantes, de noms de variables et d’opérateurs.
Une expression est destinée à être évaluée. Par exemple, dans un état du programme où
la variable x a la valeur 8 :
12
x
(x + 12) - 3
(x > 4) && (x < 10)
33
34 CHAPITRE 5. EXPRESSIONS ET OPÉRATEURS
sont des expressions qui ont pour valeurs respectives 12, 8, 17 et 1 ( vrai ). En C,
toute expression a obligatoirement un type et une valeur. Elle peut avoir une adresse et
un effet de bord qui change l ?état de la mémoire ou déclenche des entrées-sorties.
Soit expr une expression, nous noterons :
– type(expr) son type ;
– val(expr) sa valeur ;
– adr(expr) son adresse.
Une expression peut être mise entre parenthèses. Si expr est une expression, alors
(exp) est une expression équivalente à exp.
Une expression peut être :
1. soit une expression simple : une constante ou un nom de variable ;
2. soit une expression composée à l’aide d’opérateurs.
B Remarque 5.2.1. Attention ! Un même nom peut être associé à des constantes ou à
des variables différentes dans un même programme. La conséquence est que lorsqu’un
nom apparaı̂t dans une expression, il faut pouvoir déterminer de façon univoque quelle
est la valeur ou la variable associée à ce nom. Il ne peut pas y avoir deux entités (type,
constante ou variable) de même nom qui sont visibles en un même point du texte d’un
programme. Les règles de visibilité des déclarations se résument de la façon suivante :
un nom dans une expression se rapporte à sa déclaration la plus récente dans l’ordre
de lecture du programme.
5.3 Opérateurs
On distingue
1. l’affectation.
2. les opérateurs arithmétiques ; opérateurs relationnels (de comparaison)
3. les opérateurs logiques booléens ;
4. la conversion de type ;
5. Les opérateurs d’affectation composée
6. Les opérateurs d’incrémentation et de décrémentation
7. L’opérateur virgule
5.3. OPÉRATEURS 35
+ addition
- soustraction
* multiplication
/ division
% reste de la division de deux nombres entiers (modulo)
• Si expr1 et expr2 sont des expressions de type numérique, et op est l’un des
opérateurs + - * / % alors :
– type(expr1 op expr2) = type commun de type(exp1) et de type(exp2)
– valeur(expr1 op expr2) = val1 op val2, où val1 et val2 sont
le résultat des conversions de valeur(expr1) et de valeur(expr2) en
type commun
• Si expr1 et expr2 sont de type entier, l’opérateur / effectue la division entière de
leurs valeurs.
• Notons enfin qu’il n’y a pas en C d’opérateur effectuant l’elévation à la puissance.
Il y a cependant la fonction pow de la librairie math.h telle que pow(x,y) calcule
xy .
. Exemple 5.3.1. Par exemple
– l’expression ((4.0 + 2.25) * 2.0) est de type double (flottant) et a la
valeur 12, 5 ;
36 CHAPITRE 5. EXPRESSIONS ET OPÉRATEURS
Attention ! Comme nous venons de le voir, lorsque ses opérandes sont des entiers,
l’opérateur / calcule le quotient de la division de ces deux entiers. Supposons que l’on
veuille calculer la moyenne arithmétique des deux entiers 4 et 7. On ne pourra pas
l’obtenir en évaluant l’expression (4 + 7) / 2, car on obtiendra l’entier 5 et non le
flottant 5, 5 attendu. Pour l ?obtenir, il faudra forcer la division flottante en écrivant l’un
des opérandes sous forme d’une constante littérale de type flottant ou en convertissant
sa valeur en un flottant à l’aide de l’opérateur de changement de type (3.2.6 ci-dessous).
Par exemple, l’expression :
(4 + 7) / 2.0
a la valeur 5, 5.
5.3.2 Comparateurs
Les comparateurs == != < <= > >= testent respectivement l’égalité, la différence,
l’infériorité, l’infériorité ou l’égalité, la supériorité, la supériorité ou l’égalité, de deux
nombres.
== égal
!= différence
< strictement inférieur
<= inférieur ou égal
> strictement supérieur
>= supérieur ou égal
La syntaxe est
expr1 op expr2
Les deux expressions sont évaluées puis comparées. Rappelons qu’il n’y a pas de
type booléen en C. La valeur rendue est de type int. En C, le booléen faux est
représenté par 0 et le booléen vrai par toute valeur différente de 0.
Si expr1 et expr2 sont des expressions de type numérique et op est l’un des
opérateurs de comparaison ==, !=, <, <=, > ou >=, alors :
expr1 op expr2
&& et logique
|| ou logique
! négation logique
[Link] Négation
L’opérateur ! calcule la négation d’un booléen. Si expr est une expression, alors :
!exp
est une expression qui a les propriétés suivantes :
(i) type(exp) = int
(ii) valeur(!exp) = 1 si valeur(expr) = 0 et 0 sinon.
& et | ou inclusif
ˆ ou exclusif ˜ complément à 1
<< décalage à gauche >> décalage à droite
& 0 1 | 0 1 ˆ 0 1
0 0 0 0 0 1 0 0 1
1 0 1 1 1 1 1 1 0
(T) expr
où (T) est un nom de type et epxr est une expression dont la valeur est convertible
en (T) et on a :
(i) type((T) expr) = T
(ii) valeur((T) expr) = valeur(expr) convertie en T
Par exemple,
main ( )
{
int i = 3 , j = 2;
p r i n t f ( ”%f \ n ” , ( f l o a t ) i / j ) ;
}
B Remarque 5.3.2. Attention ! Il n’est pas possible de changer le type d’une structure
ou d’une union.
5.3.6 Affectation
L’opérateur = affecte une valeur à une variable. Sa syntaxe est la suivante
variable = expression
5.3. OPÉRATEURS 39
imprime pour x la valeur 6.5 (et non 7), car dans l’instruction i = j + x;, l’ex-
pression j + x a été convertie en entier.
B Remarque 5.3.3. Attention ! Ne faut pas confondre l’opérateur d’affectation = avec
l’opérateur d’égalité ==.
affiche b = 5.
Opérateurs Associativité
() [] . -> de gauche à droite
! ˜ ++ -- -(unaire) *(unaire) (type) sizeof &(adresse) de gauche à droite
* / % de droite à gauche
+ -(binaire) de gauche à droite
<< >> de gauche à droite
< <= > >= de gauche à droite
== != de gauche à droite
&(et bit à bit) de gauche à droite
ˆ (ou exclusif) de gauche à droite
| (ou bit à bit) de gauche à droite
?: de droite à gauche
&& de gauche à droite
|| de gauche à droite
= += -= *= /= %= &= ˆ= |= <<= >>= de droite à gauche
, de gauche à droite
≡(!a) && b|| x > 3, car la priorité de ! est supérieure à celle de &&
≡((!a) && b)|| x > 3, car la priorité de && est supérieure à celle de ||
≡ ((!a) && b)|| (x > 3), car la priorité de || n’est pas supérieure à
celle de >
(iv) 3 + 4 - 5 ≡ (3 + 4) - 5, car + et - ont la même priorité et + est asso-
ciatif de gauche à droite
(v) x = y = 5 ≡ x = (y = 5), car = est associatif de droite à gauche
B Remarque 5.4.1. En cas de doute, il est plus sûr de spécifier l’ordre des opérations
d’une expression en la parenthésant. Par exemple, les opérateurs logiques bit-à-bit sont
moins prioritaires que les opérateurs relationnels. Cela implique que dans des tests sur
les bits, il faut parenthéser les expressions. Par exemple, il faut écrire if ((x ˆ y) != 0).
#include <stdio.h>
5.6 printf
L’affichage des valeurs d’une suite d’expressions expr1, ..., exprn au sein d’une
chaı̂ne de caractères qui les nomme ou les commente, est obtenue par l’appel :
Les valeurs des expressions expr1, ..., exprn sont converties en une suite de ca-
ractères selon les spécifications de conversion %conv1, ..., %convn puis substituées à
ces spécifications dans la chaı̂ne de caractères ”...%conv1...%convn...”. La chaı̂ne de
caractères obtenue est affichée.
5.7 scanf
La lecture de la valeur d’une variable var est obtenue par l’appel :
scanf("%conv", &var)
5.7. SCANF 43
Les caractères saisis par l’utilisateur sont lus après que l’utilisateur ait tapé sur la touche
Entrée , ils sont ensuite convertis en un nombre conformément à la spécification de
conversion %conv. Ce nombre est affecté à la variable var dont l’adresse est calculée
par l’opérateur &.
Les spécifications de conversion les plus classiques sont :
– %c pour convertir un caractère en un nombre de type char ou inversement,
– %d pour convertir une suite de caractères en un nombre de type int ou inverse-
ment
– %f pour convertir une suite de caractères en un nombre de type float ou inver-
sement.
. Exemple 5.7.1. Par exemple :
– si x vaut 5 et y vaut 9, printf("%d + %d = %d\n", x, y, x + y)
affiche “5 + 9 = 14” puis passera à la ligne ;
– printf(”Entrer une note ? ”) afficher Entrer une note ;
– scanf("%f", ¬e) aura pour effet de bord de lire le nombre flottant saisi
au clavier et de l’affecter à la variable note.
44 CHAPITRE 5. EXPRESSIONS ET OPÉRATEURS
Chapitre 6
Instructions
Contents
6.1 Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.2 Instruction vide . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.3 Instruction expression . . . . . . . . . . . . . . . . . . . . 46
6.4 Bloc d’instructions . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.5 Instruction conditionnelle if − − − else . . . . . . . . . . . . . 47
6.6 Instruction de choix multiple : switch . . . . . . . . . . . . . . 48
6.7 Instructions d’itération (boucles) . . . . . . . . . . . . . . . . . 49
6.7.1 Boucle while . . . . . . . . . . . . . . . . . . . . . . . . 49
6.7.2 Boucle do − −while . . . . . . . . . . . . . . . . . . . . 49
6.7.3 Boucle f or . . . . . . . . . . . . . . . . . . . . . . . . . 50
6.7.4 Choisir entre f or et while . . . . . . . . . . . . . . . . . 50
6.8 Rupture de séquence : break . . . . . . . . . . . . . . . . . . . 51
6.9 Branchement non conditionnel continue . . . . . . . . . . . . . 51
6.10 Retour d’un appel de fonction . . . . . . . . . . . . . . . . . . . 52
6.1 Instructions
Une instruction est un ordre donné à l’ordinateur de réaliser une suite d’actions dont
chacune a pour effet de changer l’état de la mémoire ou le déroulement du programme
ou bien de communiquer avec les unités périphériques (clavier, écran, imprimante...).
En programmation impérative, un algorithme se traduit par une suite d’instructions.
Les instructions offertes par C sont les suivantes :
– instruction vide ;
– instruction expression ;
– bloc d’instructions ;
– instruction conditionnelle if---else ;
– instruction de choix multiple switch ;
– instruction d’itération (boucles while, do--while, for ;
– instruction de rupture de séquence break ;
45
46 CHAPITRE 6. INSTRUCTIONS
expr;
x = 3 + 5;
est une instruction qui déclenche l’action : affecter 8 à x . Par contre, l’instruction :
3 + 5;
{
decl1
...
declm
inst1
...
instn
}
Variables locales Parmi les déclarations d’un bloc, les déclarations de variables qui
ne sont pas précédées du mot-clé extern sont des définitions de variables. Les va-
riables qui sont définies dans un bloc sont dites locales à ce bloc. Elles sont créées sur
la pile au début de l’exécution de ce bloc et ôtées de la pile à la fin de son exécution.
La durée de vie d’une variable locale à un bloc est donc celle de l’exécution de ce
bloc.
Variables globales Les variables qui sont utilisées dans un bloc et qui n’y sont pas
définies doivent avoir été définies dans une autre partie du programme, soit globalement
à l’extérieur des fonctions du programme, soit dans un bloc englobant. Ces variables
sont dites globales.
ou
if ( expression1 )
instruction1
else if ( expression2 )
{
instruction2
.
.
.
}
48 CHAPITRE 6. INSTRUCTIONS
else if ( expressionn )
instructionn
else
instruction
avec un nombre quelconque de else if. Le dernier else est toujours facultatif.
Chaque instruction peut être un bloc d’instructions.
B Remarque 6.6.1. Attention ! Dans une expression switch, un cas doit être as-
socié à chacune des valeurs que peut prendre l’expression de choix. Lorsque parmi ces
valeurs, il en existe qui ne correspondent pas à une opération valide, alors, on leur
associera un cas de traitement d’erreur : le cas default en général.
while (expression)
instruction
do
instruction
while(expression) ;
où, comme dans l’instruction while, instruction est le corps de la boucle et ex-
pression est le test de continuation.
Le corps de la boucle est exécuté au moins une fois, contrairement à l’instruction
while où il peut ne jamais l’être.
int a;
do
{
p r i n t f ( ”\ n E n t r e z un e n t i e r e n t r e 1 e t 10 : ” ) ;
s c a n f ( ”%d” ,&a ) ;
}
w h i l e ( ( a <= 0 ) | | ( a > 1 0 ) ) ;
6.7.3 Boucle f or
L’instruction d’itération for a la forme suivante :
où expr1, expr2, expr3 sont des expressions et instruction est une instruction.
Elle est équivalente, par définition, à :
expr1;
while (expr2)
{
instruction
expr3;
}
. Exemple 6.7.3. Par exemple, pour afficher tous les entiers de 0 à 9, on écrit :
f o r ( i = 0 ; i < 1 0 ; i ++)
p r i n t f ( ”\ n i = %d” , i ) ;
B Remarque 6.7.1. Dans une instruction for (expr1 ; expr2 ; exp3), les expres-
sions expr1 et expr3 peuvent être absentes. Le test de continuation (expr2) peut lui
aussi être absent, on considère alors qu’il est toujours vrai. Par exemple, l’instruction :
for (;;)
instruction
est une boucle qui répète indéfiniment l’instruction instruction. Elle utilisée lorsque
l’arrêt de la boucle est provoquée par une instruction de rupture de séquence (break,
par exemple), placée dans le corps de la boucle.
– Dans le cas où le nombre d’itérations dépend d’un paramètre dont les valeurs
initiale et finale ainsi que l’incrément sont connus avant l’exécution de la boucle,
on utilisera plutôt l’instruction for.
. Exemple 6.7.4. Par exemple, pour le calcul de la somme des carrés des entiers de 1
à n
Xn
i2
i=1
p r i n t f ( ” v a l e u r de i a l a s o r t i e de l a b o u c l e = %d\n ” →
,→ , i ) ;
}
return;
return expression;
où expression est une expression, sont destinées à être placées dans le corps d’une
fonction. L’exécution de l’instruction return; par une fonction appelée redonne la
main à la fonction appelante en lui retournant la valeur valeur(expression) si l’expres-
sion expression est spécifiée.
Si la fonction appelée est la fonction main, l’exécution de l’instruction return pro-
voque la fin du programme.
Nous étudierons cette instruction plus en détail au chapitre suivant consacré aux
fonctions et aux programmes.
Chapitre 7
Fonctions et programmes
Contents
7.1 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
7.2 Définition d’une fonction . . . . . . . . . . . . . . . . . . . . . . 54
7.2.1 Appel d’une fonction . . . . . . . . . . . . . . . . . . . . 55
7.2.2 Fonctions sans arguments ou sans valeur de retour . . . . . 55
7.2.3 Déclaration d’une fonction . . . . . . . . . . . . . . . . . 56
7.2.4 Lancement et sortie d’un programme : les fonctions main
et exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.1 Fonctions
Une fonction est définie par son nom, par le type des valeurs qu’elle reçoit de la
fonction appelante (arguments, par le type de la valeur qu’elle lui retourne (valeur de
retour et par le bloc d’instructions qui permet de calculer cette valeur (corps de la
fonction.
Une fonction peut être appeler par elle-même, dans ce dernier cas, on dit que la
fonction est récursive).
Dans ce chapitre, nous étudierons :
– la définition et l’appel des fonctions
– les règles de structuration d’un programme composé de plusieurs fichiers com-
pilés ou non
53
54 CHAPITRE 7. FONCTIONS ET PROGRAMMES
où
– La première ligne de cette définition est l’en-tête de la fonction (on dit aussi son
prototype). Cet en-tête déclare le nom de la fonction et le type de la fonction
type fonction, c’est-à-dire le type de la valeur qu’elle retourne.
– type fonction est le type de la fonction ;
– si la fonction ne possède pas de valeur de retour, on remplace le type de valeur
de retour par le mot clef void
– nom fonction est le nom de la fonction : un identificateur ;
– Les arguments de la fonction arg 1,..., arg n sont appelés paramètres formels,
par opposition aux paramètres effectifs qui sont les paramètres avec lesquels
la fonction est effectivement appelée. Les paramètres formels peuvent être de
n’importe quel type. Leurs identificateurs n’ont d’importance qu’à l’intérieur de
la fonction.
– si la fonction ne possède pas de paramètres, on remplace la liste de paramètres
formels par le mot-clef void.
– Enfin, le bloc instructions constitue le corps de la fonction.
Le corps de la fonction débute éventuellement par des déclarations de variables, qui
sont locales à cette fonction. Il se termine par l’instruction de retour à la fonction appe-
lante, return. Dans le corps de la fonction on doit trouver au moins une instruction :
return expression ;
dont l’exécution a pour effet de retourner à la fonction appelante la valeur valeur(expression)
appelée valeur de retour.
Si la fonction ne retourne pas de valeur (fonction de type void), sa définition se
termine par
return;
. Exemple 7.2.1. Fonction puissance. La fonction qui calcule xn où x est un réel et n
un entier positif ou nul, peut être définie de la façon suivante :
1 float puissance ( float x , int n)
2 {
3 float p;
4 int i ;
5 i f ( n < 0)
6 {
7.2. DÉFINITION D’UNE FONCTION 55
7 p r i n t f ( ” A p p e l de p u i s s a n c e i n c o r r e c t ! ” ) ;
8 exit (1) ;
9 }
10 p = 1;
11 f o r ( i = 1 ; i <= n ; i ++)
12 p = p ∗ x;
13 return p;
14 }
Une fonction peut ne pas avoir de valeur de retour. Elle est alors de la forme :
où void est le type vide. Dans ce cas, les instructions return présentes dans le corps
de cette fonction ne spécifieront pas d’expression de la valeur de retour. Elles seront de
la forme return;
Remarque : Une fonction sans valeur de retour n’a de sens que si son appel produit
un effet de bord. C’est le cas, par exemple, de la fonction suivante qui affiche un entier
à l’écran :
void e c r i r e e n t i e r ( i n t i )
{
p r i n t f ( ”%d ” , i ) ;
return ;
}
Les deux fonctions précédentes peuvent être combinées pour écrire l’entier lu :
ecrire entier ( lire entier () ) ;
Les fonctions secondaires peuvent être déclarées et définies avant la fonction main
ou déclarés avant et définies après la fonction main. Par exemple, on écrira
int puissance ( int , int ) ;
int puissance ( int a , int n)
{
7.2. DÉFINITION D’UNE FONCTION 57
i f ( n == 0 )
return ( 1 ) ;
r e t u r n ( a ∗ p u i s s a n c e ( a , n −1) ) ;
}
i n t main ( )
{
int a = 2 , b = 5;
p r i n t f ( ”%d \ n ” , p u i s s a n c e ( a , b ) ) ;
returun 0;
}
On peut également
B Remarque 7.2.2. La déclaration est parfois facultative, par exemple quand les
fonctions sont définies avant la fonction main et dans le bon ordre. Cependant, la
déclaration est importante car elle seule permet au compilateur de vérifier que le
nombre et le type des paramètres utilisés dans la définition concordent bien avec le
prototype. De plus, la présence d’une déclaration permet au compilateur de mettre en
place d’éventuelles conversions des paramètres effectifs, lorsque la fonction est ap-
pelée avec des paramètres dont les types ne correspondent pas aux types indiqués dans
le prototype.
int main(void)
où
58 CHAPITRE 7. FONCTIONS ET PROGRAMMES
L’entier retourné par la fonction main est, par convention, égal à 0 si le programme
s’est déroulé correctement, différent de 0 sinon (1, en général). En conséquence, l’appel
de la fonction main doit se terminer par l’exécution de l’instruction
return expression;
où expression est une expression qui a pour valeur le code d’erreur.
B Remarque 7.2.3. La fonction main retourne un entier. Considérer que la fonction
main() est de type void est toléré par le compilateur. Toutefois l’écriture main()
provoque un message d’avertissement lorsqu’on utilise l’option -Wall de gcc.
exit, EXIT SUCCESS et EXIT FAILURE Il peut arriver que le déroulement d’un
programme doive être interrompu à cause d’une situation exceptionnelle : données in-
correctes, mémoire insuffisante, demande d’accès à une ressource inconnue, etc. Dans
un tel cas, la sortie du programme peut être provoquée en utilisant la fonction exit
d’en-tête :
r e t u r n ( EXIT FAILURE ) ;
}
/ ∗ a t o i c o n v e r t i t un c h a r en i n t ∗ /
a = a t o i ( argv [ 1 ] ) ;
b = a t o i ( argv [ 2 ] ) ;
p r i n t f ( ”\ nLe p r o d u i t de %d p a r %d v a u t : %d \n” , a , b , →
,→ a ∗ b ) ;
r e t u r n ( EXIT SUCCESS ) ;
}
l’exécution de la commande
./produit 12 3
affichera :
Le produit de 12 par 3 vaut : 36
60 CHAPITRE 7. FONCTIONS ET PROGRAMMES
Chapitre 8
Programmation modulaire et
compilation séparée
Contents
8.1 Structure d’un programme modulaire et compilation séparée . 61
8.1.1 Programmation modulaire . . . . . . . . . . . . . . . . . . 61
8.1.2 Compilation séparée . . . . . . . . . . . . . . . . . . . . . 62
8.1.3 Fichiers sources . . . . . . . . . . . . . . . . . . . . . . . 62
8.1.4 Fichiers d’en-têtes . . . . . . . . . . . . . . . . . . . . . . 62
8.1.5 Génération du code exécutable d’un programme modulaire 63
8.1.6 Directives au préprocesseur . . . . . . . . . . . . . . . . . 63
8.1.7 Règles de visibilité . . . . . . . . . . . . . . . . . . . . . 64
8.1.8 Déclarations externes . . . . . . . . . . . . . . . . . . . . 65
8.1.9 La bibliothèque standard . . . . . . . . . . . . . . . . . . 65
8.2 Écriture d’un programme modulaire . . . . . . . . . . . . . . . 66
8.3 Génération du code exécutable d’un programme . . . . . . . . 68
8.3.1 Compilation des fichiers sources . . . . . . . . . . . . . . 68
8.3.2 Génération du code exécutable . . . . . . . . . . . . . . . 68
8.4 Utilitaire make . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
61
62CHAPITRE 8. PROGRAMMATION MODULAIRE ET COMPILATION SÉPARÉE
rassemble des fonctions d’intérêt général (des fonctions mathématiques, par exemple)
qui pourront être utilisées soit par les autres modules du programme, soit par d’autres
programmes.
Le préprocesseur écrit dans le fichier à compiler le texte contenu dans le fichier dont
le nom est spécifié.
Cette directive est principalement utilisée pour inclure dans un fichier source le contenu
d’un fichier d’en-têtes. Elle a alors l’une des deux formes suivantes :
#include <nom module.h>
qui est à utiliser pour l’inclusion de fichiers d’en-têtes associés à la bibliothèque stan-
dard. Sinon, il faut utiliser la seconde forme suivante
#include <[chemin d’accès/]nom module.h>
Le nom de fichier fourni doit être un nom complet précisant le chemin d’accès sauf si
ce fichier réside dans le répertoire courant, auquel cas son nom module suffit.
10 {
11 f l o a t m;
12 m = min ( x , y ) ;
13 return 0;
14 }
Les variables x et y définies à la ligne 1 sont des variables globales. Elles sont visibles
dans le corps de la fonction main (bloc 9-14) mais pas dans celui de la fonction min
(bloc 2-8) car elles sont masquées par les arguments formels x et y qui sont assimilés
à des variables locales au bloc 3-8. Ces deux variables sont visibles dans tout ce bloc.
La variable m définie à la ligne 11 est locale au bloc 9-14, elle est visible dans tout ce
bloc. Après l’exécution de l’instruction 12, la variable m aura la valeur 5,5
int est_pair(int);
1. ctype.h contient les déclarations des fonctions qui testent à quelle catégorie ap-
partient un caractère : alphabétique, numérique, alphanumérique, etc. et celles
des fonctions de conversion d’une lettre en minuscule ou en majuscule ;
– classification de caractères
– int isupper(int C) : retourne une valeur différente de zéro, si C est
une majuscule
– int islower(int C) : retourne une valeur différente de zéro, si C est
une minuscule
– int isdigit(int C) : retourne une valeur différente de zéro, si C est
un chiffre décimal
– int isalpha(int C) : retourne une valeur différente de zéro, si islo-
wer(C) ou isupper(C)
– int isalnum(int C) : retourne une valeur différente de zéro, si isal-
pha(C) ou isdigit(C)
– int isxdigit(int C) : retourne une valeur différente de zéro, si C
est un chiffre hexadécimal
– int isspace(int C) : retourne une valeur différente de zéro, si C est
un signe d’espacement
– Conversion de caractères
Elles fournissent une valeur du type int qui peut être représentée comme ca-
ractère ; la valeur originale de C reste inchangée :
– int tolower(int C) : retourne C converti en minuscule si C est une
majuscule, sinon C
– int toupper(int C) : retourne C converti en majuscule si C est une
minuscule, sinon C
2. float.h contient le plus petit et le plus grand nombre représentables ainsi que le
nombre de chiffres significatifs pour chaque type numérique flottant de C ;
3. limits.h contient notamment les valeurs minimale et maximale des instances de
chaque type numérique entier de C ;
4. math.h contient les déclarations des principales fonctions mathématiques : ex-
ponentielles, logarithmiques, trigonométriques, hyperboliques, etc. ;
5. stddef.h contient notamment la définition de la macro NULL : un pointeur qui
ne pointe sur rien ;
6. stdio.h contient les déclarations des fonctions d’entrées-sorties dont les fonc-
tions printf et scanf ;
7. stdlib.h contient les déclarations de diverses fonctions utilitaires : conversions,
allocation dynamique de mémoire, sortie d’un programme dont la fonction exit,
génération de nombres aléatoires, tri, recherche dichotomique, etc. ;
8. string.h contient les déclarations des fonctions de manipulation des chaı̂nes de
caractères (atoi, strncmp, strlen, etc).
# i f n d e f AIRE DISQUE
# d e f i n e AIRE DISQUE
# d e f i n e PI 3 . 1 4
float aire disque ( float ) ;
# endif
i n t main ( v o i d )
{
float r , aire ;
p r i n t f ( ”Rayon du d i s q u e ( en cm ) ? ” ) ;
s c a n f ( ”%f ” , &r ) ;
aire disque= aire disque ( r ) ;
p r i n t f ( ” A i r e du d i s q u e = %.0 f cm2\n” , a i r e ) ;
return 0;
}
B Remarque 8.2.1. Il se peut que l’inclusion d’un même fichier d’en-têtes soit de-
mandée dans plusieurs fichiers sources d’un même programme. Il faut empêcher que
ce fichier soit inclus plusieurs fois afin d’éviter les erreurs dues aux définitions mul-
tiples d’une même entité. Cela peut être fait en utilisant la directive de compilation
conditionnelle :
68CHAPITRE 8. PROGRAMMATION MODULAIRE ET COMPILATION SÉPARÉE
#ifndef MACRO
Cette directive indique au préprocesseur que si la macro verb MACRO a été définie,
il ne doit pas recopier dans le fichier à compiler les lignes qui suivent cette directive
jusqu’à celle, comprise, qui précède la directive : #endif
Chaque fichier d’en-têtes sera donc composé de la façon suivante :
#ifndef NOM_MACRO
#define NOM_MACRO
contenu du fichier d’en-têtes
#endif
lance la compilation du fichier source fic.c avec l’option -Wall qui fournie une liste
très complète d’avertissements (des erreurs ou des oublis qui, bien que n’empêchant
pas la compilation, peuvent être la source d’une exécution incorrecte du programme).
Par exemple, la commande : gcc -Wall -c progm_aire_disque.c génère
le fichier objet progm_aire_disque.o.
Notons que la possibilité de compiler séparément les fichiers sources permet, en
cas de mise à jour d’un programme, de ne pas avoir à recompiler les fichiers qui ne
sont pas touchés par cette mise à jour.
compile parmi les fichiers fic1 ... ficm et avec l’option -Wall ceux qui sont des fi-
chiers sources (.c), réalise l’édition de liens et génère, si aucune erreur n’est détectée,
le fichier exécutable fic (ou [Link] si l’option -o n’est pas présente).
L’exécution du programme pourra alors être lancée par la commande ./fic (ou
./[Link]).
Lorsqu’un programme utilise des fonctions de bibliothèque, l’éditeur de liens doit
connaitre le nom du fichier qui contient les codes objet de ces fonctions.
Pour les fonctions de la bibliothèque standard, autres que les fonctions mathématiques,
il n’est pas nécessaire de fournir ce nom : l’éditeur de liens le connait.
8.4. UTILITAIRE MAKE 69
Pour les autres fonctions de bibliothèque, dont notamment les fonctions mathématiques
de la bibliothèque standard, il faut indiquer le nom du fichier qui contient leur code
objet. Cela se fait de la façon suivante. Un fichier de bibliothèque est stocké dans un
répertoire (en général /usr/lib) connu de l’éditeur de liens et a un nom de la forme
libnom (le préfixe lib est toujours présent). Si un programme utilise une fonction
d’une bibliothèque nommée libnom, il faut ajouter l’option lnom à la commande de
génération de son code exécutable.
Le fichier de bibliothèque qui contient le code objet des fonctions mathématiques
de la bibliothèque standard a pour nom libm. Si un programme fait appel à certaines
des fonctions mathématiques de la bibliothèque standard, il faut ajouter l’option -lm
dans la commande de génération de son code exécutable.
make -f fic
Par exemple, si les règles ci-dessus ont été enregistrées dans le fichier progm aire [Link],
la génération du fichier exécutable progm_aire_disque sera réalisée par la com-
mande :
make -f progm aire [Link]
Chapitre 9
Entrées/Sorties
Contents
9.1 Saisie et affichage de données dans les E/S standards . . . . . . 72
9.1.1 Lecture et écriture d’un caractère . . . . . . . . . . . . . . 72
9.1.2 Lecture et écriture d’une chaı̂ne de caractères . . . . . . . . 73
9.2 Lecture et écriture dans des fichiers texte . . . . . . . . . . . . . 74
9.3 Opérations sur un ficher . . . . . . . . . . . . . . . . . . . . . . 75
9.3.1 Création, ouverture et fermeture d’un fichier . . . . . . . . 76
9.3.2 Test de fin de fichier ou d’erreur . . . . . . . . . . . . . . 76
9.3.3 Lecture et écriture d’un caractère . . . . . . . . . . . . . . 77
9.3.4 Lecture et écriture d’une ligne . . . . . . . . . . . . . . . 78
9.3.5 Lecture et écriture avec format . . . . . . . . . . . . . . . 80
9.3.6 Lecture avec format . . . . . . . . . . . . . . . . . . . . . 80
9.3.7 Écriture avec format . . . . . . . . . . . . . . . . . . . . . 80
9.4 Positionnement dans un fichier . . . . . . . . . . . . . . . . . . 81
71
72 CHAPITRE 9. ENTRÉES/SORTIES
i n t main ( v o i d )
{
int c;
c = getchar () ;
w h i l e ( c != ’ \ n ’ )
{
putchar ( c ) ;
c = getchar () ;
}
return 0;
}
équivalent à
# i n c l u d e < s t d i o . h>
i n t main ( v o i d )
{
int c;
w h i l e ( ( c = g e t c h a r ( ) ) != ’ \ n ’ )
9.1. SAISIE ET AFFICHAGE DE DONNÉES DANS LES E/S STANDARDS 73
putchar ( c ) ;
return 0;
}
Le format est une chaı̂ne de caractères qui doit contenir autant de spécifications de
conversion qu’il y a de valeurs à écrire. Les valeurs à écrire sont celles des expressions
expr1,..., expn. Une spécification de conversion spécifie la façon de convertir une valeur
en une suite de caractères.
La première spécification de conversion correspondant à la première valeur à écrire
et ainsi de suite. Cet appel retourne le nombre de valeurs écrites si l’écriture s’est
déroulée correctement ou un entier négatif sinon.
Comme exemples de spécifications de conversion offertes par C, on a :
– %d pour écrire un nombre de type char, short ou int, signé ou non signé, sous
forme décimale signée,
– %hd pour écrire un nombre de type short, signé ou non signé, sous forme décimale
signée ;
– %ld pour écrire un nombre de type long, signé ou non signé, sous forme décimale
signée ;
– %f pour écrire un nombre de type float ou double en virgule fixe ;
– %e pour écrire un nombre de type float ou double en virgule flottante ;
74 CHAPITRE 9. ENTRÉES/SORTIES
par tous les langages de programmation. Ils sont manipulables par un éditeur de texte
et sont imprimables.
Un fichier texte est affecté à une unité d’entrée-sortie : disque, imprimante, clavier,
etc.
L’échange de données entre la mémoire d’un programme et un fichier texte se fait
au travers d’un tampon ( buffer en anglais) qui est une zone de la mémoire qui
contient une copie d’un morceau du fichier.
Quand un programme lit ou écrit une suite de caractères, il le fait dans le tampon.
Parallèlement, un processus est mis en œuvre de façon désynchronisée qui transfère,
lorsque cela est nécessaire les caractères à lire ou à écrire de l’unité d’E/S vers le
tampon ou inversement.
Une opération de lecture ou d’écriture dans un fichier est réalisée à partir de la
position courante dans le fichier, que nous appellerons le curseur. Ce curseur est po-
sitionné automatiquement lors de l’ouverture du fichier, soit au début, soit à la fin de
celui-ci. Il est déplacé automatiquement lors des opérations de lecture ou d’écriture ou
peut l’être à la demande. Un programme peut de plus tester si le curseur est positionné
sur la fin du fichier afin de savoir si celle-ci a été atteinte.
– la lecture ou l’écriture avec ou sans format : les données échangées peuvent être
des caractères ou des lignes.
et sa syntaxe est
fopen("nom-de-fichier","mode")
L’appel fopen(f) où f est le flot de type FILE* retourné par la fonction fopen
correspondant, a pour effet de transférer le contenu du tampon (on dit : de vider )
sur l’unité d’entrée/sortie supportant le fichier f et de libérer la place occupée par ce
tampon. Cet appel retourne 0 si la fermeture s’est effectuée correctement ou sinon un
entier différent de 0.
i n t main ( v o i d )
{
FILE ∗ f ;
int c;
f = f o p e n ( ”mes−c a r a c t e r e s . t x t ” , ”w” ) ;
78 CHAPITRE 9. ENTRÉES/SORTIES
i f ( f == NULL )
{
p r i n t f ( ”Le f i c h i e r mes−c a r a c t e r e s . t x t ne p e u t →
,→ p a s ê t r e o u v e r t ! ” ) ;
exit (1) ;
}
p r i n t f ( ” S a i s i r une s u i t e de c a r a c t e r e s ? ” ) ;
w h i l e ( ( c = g e t c h a r ( ) ) != ’ \ n ’ )
fputc (c , f ) ;
fclose ( f ) ;
f = f o p e n ( ”mes−c a r a c t e r e s . t x t ” , ” r ” ) ;
i f ( f == NULL )
{
p r i n t f ( ”Le f i c h i e r mes−c a r a c t e r e s . t x t ne p e u t →
,→ p a s ê t r e o u v e r t ! ” ) ;
exit (1) ;
}
p r i n t f ( ”Mes c a r a c t e r e s = ” ) ;
w h i l e ( ( c = f g e t c ( f ) ) != EOF)
putchar ( c ) ;
fclose ( f ) ;
return 0;
}
La lecture d’une ligne dans un fichier est effectuée par l’appel de la fonction
fgets d’en-tête :
L’écriture d’une ligne dans un fichier est effectuée par l’appel de la fonction fputs
d’en-tête :
# d e f i n e NBLIGNES 100
# d e f i n e NBCAR 100
i n t main ( v o i d )
{
FILE ∗ f ;
c h a r t e x t e [ NBLIGNES ] [ NBCAR + 1 ] ; / ∗ ( i l f a u t c o m p t e r →
,→ l e c a r a c t è r e de f i n de c h a ı̂ n e ) ∗ /
int i , j ;
f = f o p e n ( ” m o n t e x t e . t x t ” , ”w” ) ;
i f ( f == NULL )
{
p r i n t f ( ”Le f i c h i e r mon−t e x t e . t x t ne p e u t p a s ê t r e →
,→ o u v e r t ! ” ) ;
exit (1) ;
}
f p u t s ( ” c e c i e s t ma p r e m i è r e l i g n e \ n” , f ) ;
f p u t s ( ”ma d e u x i è m e l i g n e . . \ n” , f ) ;
f p u t s ( ”ma t r o i s i è m e l i g n e , \ n” , f ) ;
f p u t s ( ”ma q u a t r i è m e e t d e n i è r e l i g n e \n” , f ) ;
fclose ( f ) ;
L’instruction while
w h i l e ( i < NBLIGNES && f g e t s ( t e x t e [ i ] , NBCAR , f ) )
i ++;
lit les vers dans le fichier mon_texte peut-être paraphrasée de la façon suivante :
Tant que le rang de la ligne du texte à lire (i) est inférieur au nombre maximum de
lignes (NBLIGNES) que l’on peut ranger dans le tableau texte et que cette ligne de
texte a été correctement lue par la fonction fgets et rangée dans la ième ligne du
tableau texte, incrémenter le rang de la ligne à lire et recommencer.
Cette instruction pourrait être écrite de façon encore plus compacte :
w h i l e ( i < NBLIGNES && f g e t s ( t e x t e [ i ++] , NBCAR , f ) )
;
Au lieu d’écrire avec format dans un fichier, on peut le faire dans une chaı̂ne de
caractères. L’écriture avec format dans une chaı̂ne de caractères est effectuée par la
fonction sprintf d’en-tête :
int sprintf(char *s, char *format, liste d’expressions)
9.4. POSITIONNEMENT DANS UN FICHIER 81
L’appel de cette fonction a le même effet que celui de la fonction fprintf mais
elle écrit dans la chaı̂ne s au lieu d’écrire dans le fichier d’entrée standard. Cet appel
retourne le nombre de caractères écrits si l’écriture s’est bien passée, un entier négatif
sinon.
⇒ L’intérêt de cette fonction est de permettre la conversion de nombres en chaı̂nes
de caractères ou l’insertion dans une chaı̂ne de caractères de nombres convertis en
chaı̂nes de caractères.
Par exemple, si s est un tableau d’au moins 3 caractères et x est une variable entière
de valeur 5, l’appel :
sprintf(s, "%d", x * x)
rangera la chaı̂ne de caractères : 25 dans le tableau s et l’appel :
sprintf(s, "Le carré de %d est %d.", x, x * x)
y rangera la chaı̂ne de caractères : Le carré de 5 est 25. .
# d e f i n e NB 50
# d e f i n e F SORTIE ” s o r t i e ”
i n t main ( v o i d )
{
FILE ∗ f i n , ∗ f o u t ;
i n t ∗ tab ;
int i ;
t a b = ( i n t ∗ ) m a l l o c ( NB ∗ s i z e o f ( i n t ) ) ;
f o r ( i = 0 ; i < NB ; i ++)
tab [ i ] = i ;
/ ∗ e c r i t u r e du t a b l e a u d a n s F SORTIE ∗ /
i f ( ( f o u t = f o p e n ( F SORTIE , ”w” ) ) == NULL )
{
f p r i n t f ( s t d e r r , ”\ n I m p o s s i b l e d ? e c r i r e d a n s l e →
,→ f i c h i e r %s \ n” , F SORTIE ) ;
r e t u r n ( EXIT FAILURE ) ;
}
f w r i t e ( t a b , NB ∗ s i z e o f ( i n t ) , 1 , f o u t ) ;
fclose ( f out ) ;
/ ∗ l e c t u r e d a n s F SORTIE ∗ /
i f ( ( f i n = f o p e n ( F SORTIE , ” r ” ) ) == NULL )
{
f p r i n t f ( s t d e r r , ”\ n I m p o s s i b l e de l i r e d a n s l e →
,→ f i c h i e r %s \ n” , F SORTIE ) ;
r e t u r n ( EXIT FAILURE ) ;
}
/ ∗ on s e p o s i t i o n n e a l a f i n du f i c h i e r ∗ /
f s e e k ( f i n , 0 , SEEK END ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
/ ∗ d e p l a c e m e n t de 10 i n t en a r r i e r e ∗ /
f s e e k ( f i n , −10 ∗ s i z e o f ( i n t ) , SEEK END ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
f r e a d (& i , s i z e o f ( i ) , 1 , f i n ) ;
p r i n t f ( ”\ t i = %d” , i ) ;
9.4. POSITIONNEMENT DANS UN FICHIER 83
/ ∗ r e t o u r au d e b u t du f i c h i e r ∗ /
rewind ( f i n ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
f r e a d (& i , s i z e o f ( i ) , 1 , f i n ) ;
p r i n t f ( ”\ t i = %d” , i ) ;
/ ∗ d e p l a c e m e n t de 5 i n t en a v a n t ∗ /
f s e e k ( f i n , 5 ∗ s i z e o f ( i n t ) , SEEK CUR ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
f r e a d (& i , s i z e o f ( i ) , 1 , f i n ) ;
p r i n t f ( ”\ t i = %d \n” , i ) ;
fclose ( f in ) ;
r e t u r n ( EXIT SUCCESS ) ;
}
position 200
position 160 i = 40
position 0 i = 0
position 24 i = 6
t a b = ( i n t ∗ ) m a l l o c ( NB ∗ s i z e o f ( i n t ) ) ;
f o r ( i = 0 ; i < NB ; i ++)
tab [ i ] = i ;
/ ∗ e c r i t u r e du t a b l e a u d a n s F SORTIE ∗ /
i f ( ( f o u t = f o p e n ( F SORTIE , ”w” ) ) == NULL )
{
f p r i n t f ( s t d e r r , ”\ n I m p o s s i b l e d ? e c r i r e d a n s l e →
,→ f i c h i e r %s \ n” , F SORTIE ) ;
r e t u r n ( EXIT FAILURE ) ;
}
/ ∗ c a s d ’ un f i c h i e r t e x t e ∗ /
f o r ( i = 0 ; i < NB ; i ++){
f p r i n t f ( f o u t , ”%d ” , t a b [ i ] ) ;
}
84 CHAPITRE 9. ENTRÉES/SORTIES
fclose ( f out ) ;
/ ∗ l e c t u r e d a n s F SORTIE ∗ /
i f ( ( f i n = f o p e n ( F SORTIE , ” r ” ) ) == NULL )
{
f p r i n t f ( s t d e r r , ”\ n I m p o s s i b l e de l i r e d a n s l e →
,→ f i c h i e r %s \ n” , F SORTIE ) ;
r e t u r n ( EXIT FAILURE ) ;
}
/ ∗ on s e p o s i t i o n n e a l a f i n du f i c h i e r ∗ /
f s e e k ( f i n , 0 , SEEK END ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
/ ∗ d e p l a c e m e n t de 10 c a r a c t è r e s en a r r i e r e ∗ /
f s e e k ( f i n , −10 , SEEK END ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
f s c a n f ( f i n , ”%d” , &i ) ; / ∗ Cas d ’ un f i c h i e r t e x t e →
,→ ∗ /
p r i n t f ( ”\ t i = %d” , i ) ;
/ ∗ r e t o u r au d e b u t du f i c h i e r ∗ /
rewind ( f i n ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
f s c a n f ( f i n , ”%d” , &i ) ; / ∗ Cas d ’ un f i c h i e r t e x t e →
,→ ∗ /
p r i n t f ( ”\ t i = %d” , i ) ;
/ ∗ d e p l a c e m e n t de 5 c a r a c t è r e s en a v a n t ∗ /
f s e e k ( f i n , 5 , SEEK CUR ) ;
p r i n t f ( ”\ n p o s i t i o n %l d ” , f t e l l ( f i n ) ) ;
f s c a n f ( f i n , ”%d” , &i ) ; / ∗ Cas d ’ un f i c h i e r t e x t e →
,→ ∗ /
p r i n t f ( ”\ t i = %d \ n” , i ) ;
fclose ( f in ) ;
r e t u r n ( EXIT SUCCESS ) ;
}
position 140
position 130 i = 47
position 0 i = 0
position 6 i = 3
Contents
10.1 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
10.1.1 Définition d’un tableau monodimensionnel . . . . . . . . . 86
10.1.2 Définition d’un tableau multidimensionnel . . . . . . . . . 87
10.2 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
10.2.1 Déclaration d’un type structure . . . . . . . . . . . . . . . 88
10.2.2 Définition de variables de type structure . . . . . . . . . . 89
10.3 Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
10.4 Définition de types composés avec typedef . . . . . . . . . . . . 92
10.1 Tableaux
En C, un tableau est une suite de variables ou de tableaux du même type qui consti-
tuent les éléments de ce tableau. Dans le premier cas, ce tableau est monodimensionnel
et dans le second cas, il est multidimensionnel.
Par la suite, nous appellerons type de base : un type numérique, un type structure,
un type union ou un nom de type introduit par une déclaration typedef.
85
86 CHAPITRE 10. TABLEAUX, STRUCTURES ET UNIONS
type tab[n];
où tab est un identificateur, n est une expression constante de type entier représente
le nombre des éléments du tableau.
La définition d’un tableau monodimensionnel de variables de type de base type a
la forme suivante :
B Remarque 10.1.2. Attention ! Un tableau n’est pas une valeur gauche : il ne peut
pas être affecté à une variable, ni retourné par une fonction (il n’est pas une lvalue) .
Comme nous le verrons par la suite, c’est l’adresse de son premier élément qui identifie
un tableau.
Un tableau correspond en fait à un pointeur vers le premier élément du tableau. Ce
pointeur est constant. Cela implique en particulier qu’aucune opération globale n’est
autorisée sur un tableau. Notamment, un tableau ne peut pas figurer à gauche d’un
opérateur d’affectation.
# i n c l u d e <s t d i o . h>
# define N 5
i n t main ( v o i d )
{
i n t t a b [N] = { 1 , 2 , 3 , 4 , 5 } ;
int i ;
f o r ( i = 0 ; i < N ; i ++)
p r i n t f ( ” t a b [%d ] = %d \ n” , i , t a b [ i ] ) ;
return 0;
}
10.1. TABLEAUX 87
Les m premiers éléments terminaux ont pour valeurs initiales respectives les valeurs
des expressions exp0,..., expm. La liste des valeurs initiales est facultative. Si
elle est omise, la définition d’un tableau se réduit à :
type tab[n1][n2]...[nd];
. Exemple 10.1.2. Une matrice M à 2 lignes et 3 colonnes peut être représentée par
le tableau M à 2 dimensions défini par : int M[2][3] Le tableau M[0] représente la
1ère ligne de la matrice m dont les éléments sont M[0][0], M[0][1] et M[0][2]
et le tableau M[1] représente sa 2e ligne dont les éléments sont M[1][0], M[1][1]
et M[1][2]. Si les éléments de cette matrice sont connus lors de sa définition, ils
peuvent être spécifiés comme valeurs initiales. Par exemple, la matrice :
5 4 3
M=
6 2 1
ou mieux par :
{
i n t i , j , s = 0;
f o r ( i = 0 ; i < 2 ; i ++)
f o r ( j = 0 ; j < 3 ; j ++)
s += m[ i ] [ j ] ;
p r i n t f ( ”Somme = %d \ n” , s ) ;
}
. Exemple 10.1.4. Si m1 et m2 sont deux tableaux définis par : int m1[2][3], m2[2][3] ;
l’instruction suivante a pour effet de recopier le contenu du tableau m1 dans le tableau
m2 :
f o r ( i = 0 ; i < 2 ; i ++)
f o r ( j = 0 ; j < 3 ; j ++)
m2[ i ] [ j ] = m1[ i ] [ j ] ;
m2 = m1;
10.2 Structures
Une structure est une valeur composée d’une suite de variables ou de tableaux qui
constituent les champs de cette structure. Contrairement aux éléments d’un tableau,
les champs d’une structure peuvent être de types différents. Toute structure a un type
formé des déclarations de ses champs.
struct s
{
type-1 champs-1;
type-2 champs-2;
...
type-n champs-n;
};
sont les déclarations des variables ou des tableaux qui constituent les champs des
structures de ce type, déclare un nouveau type : le type struct s qui est un type
structure.
⇒ Les types structure sont des types de base au même titre que les types numériques.
. Exemple 10.2.1. Un point du plan décrit par son nom (une lettre), son abscisse et
son ordonnée (des réels) dans un repère orthonormé, pourra être représenté par une
structure de type :
struct point
{
char nom;
float x, y;
};
{
s t r u c t p o i n t a = { ’A ’ , 3 , 2 } , b = { ’B ’ , 7 , 4 } ;
p r i n t f ( ” C o o r d o n n e e s du m i l i e u du s e g m e n t AB = (%.2 f ; →
,→ %.2 f ) ” , ( a . x + b . x ) / 2 , ( a . y + b . y ) / 2 ) ;
}
90 CHAPITRE 10. TABLEAUX, STRUCTURES ET UNIONS
On notera qu’il n’y a pas conflit de nom entre la variable nom et le champ nom, car
ils n’appartiennent pas à la même catégorie de noms.
10.3 Unions
Une union est une valeur composée d’un ensemble de variables ou de tableaux qui
constituent les champs de cette union et qui ont la même adresse. Une variable de type
union ne peut donc avoir comme ”valeur” que l’un des champs qui la composent.
. Exemple 10.3.1. Supposons que nous voulions définir des items dont chacun est soit
un caractère, soit un nombre entier, soit un nombre flottant. En C, un tel item pourra
être représenté par une union dont le type est déclaré de la façon suivante :
union item
{
char c ;
int e;
float f;
};
Cette déclaration spécifie qu’une variable i de type union item est composée
de trois variables i.c de type char, i.e de type int et i.f de type float qui
constituent ses champs. Si la variable i a pour valeur un caractère, il sera affecté au
champ c, si elle a pour valeur un entier, il sera affecté au champ e et si elle a pour va-
leur un flottant, il sera affecté au champ f. Puisqu’une variable de type union item
ne peut avoir que l’une de ces trois valeurs, il est possible d’implanter ses champs à
partir de la même adresse et c’est ce qui est fait. Une variable i de type union item
sera définie par :
10.3. UNIONS 91
union item i;
Cette définition provoquera la création des variables suivantes dans la mémoire : Cette
i.c
i i.e
i.f
représentation fait clairement apparaı̂tre que i est une variable composée des 3 va-
riables : i.c, i.e et i.f qui ont la même adresse. Si la taille d’une valeur de type char est
de 1 octet et celle d’une valeur de type int ou float est de 4 octets, la taille de la case
mémoire d’une valeur de type union item sera de 4 octets.
Si l’on souhaite affecter à la variable i : le caractère ’z’, on l’affectera au champ
c : i.c = ’z’ : (122 est le code ASCII de z ). La valeur entière 50, on l’affectera au
i.c
i i.e 122
i.f
⇒ Les unions sont définies et manipulées de la même façon que les structures.
. Exemple 10.3.2. Reprenons les items (caractère, entier ou flottant) de l’exemple
précèdent. Ils pourront être représentés par des structures de type struct item est
déclaré de la façon suivante :
s t r u c t item
{
char t y p e ;
union
{
char c ;
int e;
float f ;
} u;
};
Si l’item a pour valeur un caractère c, le champ type aura pour valeur le caractère
C et le champ u.c aura la valeur c. Si l’item est un nombre entier e, le champ
type aura pour valeur le caractère E et le champ u.e aura la valeur e. Si l’item est
un nombre flottant f, le champ type aura pour valeur le caractère F et le champ
u.f aura la valeur f. L’affichage d’un item pourra être réalisé en lui appliquant la
fonction afficher_item dont la définition est la suivante :
void a f f i c h e r i t e m ( s t r u c t item i )
{
switch ( i . type )
{
c a s e ’C ’ :
p r i n t f ( ”%c \ n” , i . u . c ) ;
break ;
92 CHAPITRE 10. TABLEAUX, STRUCTURES ET UNIONS
c a s e ’E ’ :
p r i n t f ( ”%d \n” , i . u . e ) ;
break ;
c a s e ’F ’ :
p r i n t f ( ”%f \n” , i . u . f ) ;
break ;
}
}
la déclaration suivante :
typedef s t r u c t point Point ;
déclare que Point est un synonyme de l’expression de type struct point. Ainsi,
une variable p dont les instances sont des structures de type struct point pourra
être déclarée indifféremment struct point p ou Point p.
De même, la déclaration suivante :
typedef s t r u c t point Point ;
typedef Point Triangle [3];
Adresses et pointeurs
Contents
11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
11.2 Notion d’adresses (pointeurs) . . . . . . . . . . . . . . . . . . . 94
11.2.1 Définition d’un pointeur . . . . . . . . . . . . . . . . . . . 94
11.2.2 Description d’un type adresse (pointeur) . . . . . . . . . . 94
11.3 Adresses de variables, de tableaux et de fonctions . . . . . . . . 95
11.3.1 Adresses de variables . . . . . . . . . . . . . . . . . . . . 95
11.3.2 Adresses de structures : pointeurs et structures . . . . . . . 95
11.3.3 Adresse et valeur d’une variable . . . . . . . . . . . . . . 96
11.3.4 Adresses de tableaux : pointeurs et tableaux . . . . . . . . 97
11.3.5 Arithmétique des pointeurs . . . . . . . . . . . . . . . . . 98
11.3.6 Passage d’arguments par adresse . . . . . . . . . . . . . . 99
11.4 Allocation dynamique de variables ou de tableaux . . . . . . . . 101
11.4.1 Taille des instances d’un type . . . . . . . . . . . . . . . . 102
11.4.2 Allocation dynamique d’une variable . . . . . . . . . . . . 102
11.4.3 Allocation dynamique d’un tableau . . . . . . . . . . . . . 103
11.4.4 Libération d’un emplacement mémoire alloué dynamique-
ment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
11.1 Introduction
Dans les programmes que nous avons étudiés jusqu’à présent, l’accès aux va-
riables, aux tableaux et aux fonctions s’est fait par leur nom. Mais il y a des cas où
il est nécessaire de le faire par leur adresse pour, entre autres, accéder aux éléments
d’un tableau, définir des fonctions qui accèdent directement à la valeur d’une variable
définie dans le corps de la fonction appelante, passer un tableau ou une fonction en
argument d’une fonction, accéder à des variables anonymes définies dynamiquement
pendant l’exécution du programme.
Rappelons :
93
94 CHAPITRE 11. ADRESSES ET POINTEURS
– qu’une variable est une case de la mémoire dans laquelle est rangée une valeur.
L’adresse d’une variable est l’adresse de cette case.
– qu’un tableau est constitué d’éléments qui sont des tableaux ou des variables et
qui sont rangés de façon contiguë en mémoire. L’adresse d’un tableau est celle
de son premier élément (l’élément de rang 0 de ce tableau).
– que le code exécutable d’un programme est lui-même rangé dans la mémoire.
A chaque fonction de ce programme, il correspond un point d’entrée dans ce
code qui est l’adresse de la première instruction machine à exécuter quand cette
fonction est appelée. Cette adresse est l’adresse de la fonction.
En C, les adresses des variables, des tableaux et des fonctions sont des valeurs au
même titre que les nombres, les structures ou les unions. Elles peuvent être la valeur
d’un argument ou la valeur de retour d’un opérateur ou d’une fonction. Il est possible
d’obtenir l’adresse d’une variable, d’un tableau ou d’une fonction à partir de son
nom, d’accéder à la valeur d’une variable ou à un élément de tableau dont l’adresse
est donnée, d’appeler une fonction en indiquant son adresse.
B Remarque 11.1.1. Attention ! Si les adresses des fonctions sont des valeurs, les
fonctions elles ne le sont pas. Il n’est pas possible d’écrire un programme qui crée une
nouvelle fonction ou modifie le code d’une fonction.
Type *nom;
où type Type est le type de l’”objet” pointé et nom est le nom du pointeur. Cette
déclaration déclare un identificateur, nom, dont la valeur est l’adresse de l’objet pointé
qui est de type Type. L’identificateur nom est une Lvalue, sa valeur est modifiable.
Par exemple :
int ∗ pi ;
c h a r ∗ pc ;
s t r u c t p e r s o n n e ∗ pp1 ;
P e r s o n n e ∗ pp2 ;
int i , ∗ pi ;
est la déclaration d’une variable i de type int et d’un pointeur pi sur une variable
de type int.
Un élément d’un tableau ou un champ d’une structure peut être de type adresse.
déclare un tableau personnes dont les éléments pointent sur une variable de type
struct personne et :
96 CHAPITRE 11. ADRESSES ET POINTEURS
struct livre
{
char t i t r e [ 3 0 ] ;
s t r u c t personne ∗ auteur ;
}
déclare un type struct livre dont le champ auteur pointe sur une variable de
type struct personne.
B Remarque 11.3.1. Un type structure T ype est récursif s’il possède un champ qui
pointe directement ou indirectement sur une variable de type T ype. Par exemple :
s t r u c t personne
{
c h a r nom [ 3 0 ] ;
s t r u c t p e r s o n n e ∗ mere ;
s t r u c t personne ∗ pere ;
}
Une telle déclaration est possible car un nom de structure peut être utilisé avant ou
dans sa propre déclaration.
s t r u c t personne
{
c h a r nom [ 3 0 ] ;
P e r s o n n e ∗ mere ;
Personne ∗ pere ;
}
Si le père ou la mère d’une personne est inconnu le champ pere ou le champ mere
auront pour valeur l’adresse nulle (NULL) qui est une instance de tout type adresse.
&exp
B Remarque 11.3.2. Attention ! &exp n’est pas une valeur gauche. Par exemple, si
i est une variable de type int, on ne pourra pas écrire &i = 12 en pensant affecter
la valeur 12 à i. Il suffit, évidemment, d’écrire i = 12.
11.3. ADRESSES DE VARIABLES, DE TABLEAUX ET DE FONCTIONS 97
L’opérateur * permet d’obtenir la valeur d’une variable à partir de son adresse. On dit
que l’on déréférence cette adresse. Si expr est une expression de type adresse
d’une variable de type Type alors :
*exp
est une expression qui a les propriétés suivantes :
– type(*expr) = Type
– val(*expr) = valeur de la variable d’adresse val(expr)
– c’est une valeur gauche.
i aura la valeur 5.
expr->c
est équivalente à :
(*expr).c
int tab[12];
int m[2][3];
Dans une expression :
– tab est une constante de type adresse d’une variable de type int qui a pour
valeur l’adresse de la variable tab[0] ;
– m est une constante de type adresse d’un tableau de 3 éléments de type int
qui a pour valeur l’adresse du sous-tableau m[0].
déclare une variable ptf de type adresse d’un tableau de 3 éléments de type float
.
*expr
est une expression qui a les propriétés suivantes :
– type(*exp) = adresse d’un élément (tableau ou variable) de type Type
– val(*exp) = adresse de l’élément de rang 0 du tableau pointé par expr
– ce n’est pas une valeur gauche.
Soit tab un tableau. Si expr1 est une expression dont la valeur a est l’adresse d’un
élément du tableau tab et si expr2 est une expression dont la valeur i est un nombre
entier, alors :
– val(expr1 + expr2 ) = adresse du ie élément suivant l’élément d’adresse a
– val(expr1 − expr2 ) = adresse du ie élément précédant l’élément d’adresse a
Si expr1 et expr2 sont des expressions dont les valeurs a1 et a2 sont les adresses
d’un élément du tableau tab, alors :
– val(expr1 − expr2 ) = i, si a1 + i = a2
L’opérateur [] d’accès à un élément de tableau est défini en fonction de l’addition
d’une adresse et d’un nombre. On a l’équivalence :
{
int tab[12] = {68, 25, 20, 22, 21, 0, 1, 4, 130, 57, 68, 97};
int *a, *s;
a = tab + 3;
s = tab + 8;
printf("val(*a) = %d, val(*s) = %d, val(s - a) = %d", *a, *s, s-a);
}
affichera :
v a l ( ∗ a ) = 2 2 , v a l ( ∗ s )= 1 3 0 , v a l ( s − a )= 5
{
int m[2][3] = {{5, 4, 3}, {6, 2, 1}};
int (*r)[3];
/* adresse du sous tableau {5,4,3} : 1er elmnt de la matrice m */
int *e;
r = m + 1;
/* adresse du sous tableau {6, 2, 1} : 2eme elmnt de la matrice m */
e = (*r) + 2;
/* adresse du 3eme elment du sous tableau {6, 2, 1} */
où la variable r est de type adresse d’un tableau de 3 éléments de type int à laquelle
est affecté l’adresse du sous-tableau de rang 1 du tableau m, affichera :
v a l ( ∗ e ) = 1 , v a l ( ( ∗ r ) [ 2 ] ) = 1 , v a l (m − r ) = −1
– une fonction ne peut pas modifier la valeur d’une variable de la fonction appe-
lante car elle n’a accès qu’à une copie de la valeur de cette variable ;
– si l’argument effectif est une structure volumineuse, sa copie peut être coûteuse
en temps et en mémoire.
La solution, dans ces deux cas, est de passer en argument l’adresse de cette variable
au lieu de sa valeur. On dit alors que cet argument est passé par adresse .
v o i d s o m m e e t p r o d u i t ( i n t x , i n t y , i n t ∗s , i n t ∗p )
{
∗s = x + y ;
∗p = x ∗ y ;
}
i n t main ( v o i d )
{
i n t somme , p r o d u i t ;
s o m m e e t p r o d u i t ( 2 , 3 , &somme , &p r o d u i t ) ;
return 0;
}
. Exemple 11.3.6.
s t r u c t employe
{
c h a r nom [ 3 0 ] ;
float salaire ;
}
La fonction :
v o i d a u g m e n t e r s a l a i r e ( s t r u c t e m p l o y e ∗ pe , f l o a t t a u x )
{
pe−>s a l a i r e ∗= ( 1 + t a u x ) ;
}
augmente de taux% le salaire d’un employé pe où pe est un pointeur sur la structure
qui décrit cet employé.
11.4. ALLOCATION DYNAMIQUE DE VARIABLES OU DE TABLEAUX 101
cas. Considérons, par exemple, un programme qui gère des objets dont le nombre varie
au cours de l’exécution du programme. Une première solution est de fixer dans le texte
du programme un nombre maximum N d’objets et de réserver en mémoire une place
pour ces N objets. Mais, si N a été a été sous-évalué, il faudra le modifier et recom-
piler le programme pour pouvoir gérer un plus grand nombre d’objets et si N a été
surévalué, de la place mémoire aura été réservée pour rien.
Une meilleure solution serait, lors de la création d’un nouvel objet, de pouvoir
allouer la place mémoire nécessaire à sa description et lors de la suppression d’un
objet, de pouvoir libérer la place mémoire occupée par sa description. Ceci est possible
en C, grâce à la fonction malloc qui permet d’allouer dynamiquement une variable
en mémoire et à la fonction free qui permet de libérer la place occupée par cette
variable lorsqu’elle n’est plus utilisée.
. Exemple 11.4.2. On suppose qu’un point du plan est représenté par une instance du
type Point dont la déclaration est la suivante :
typedef struct
{
c h a r nom ;
float x , y;
} Point ;
La fonction suivante :
P o i n t ∗ c r e e r p o i n t ( c h a r nom , f l o a t x , f l o a t y )
{
P o i n t ∗ pp ;
pp = ( P o i n t ∗ ) m a l l o c ( s i z e o f ( P o i n t ) ) ;
i f ( pp == NULL )
{
printf (”Allocation impossible !”) ;
exit (1) ;
}
pp−>nom = nom ;
pp−>x = x ;
pp−>y = y ;
r e t u r n pp ;
}
i f ( t a b == NULL )
{
printf (”Allocation impossible !”) ;
exit (1) ;
}
f o r ( i = 0 ; i < n ; i ++)
tab [ i ] = i ;
return tab ;
}
Elle alloue dans le tas n variables consécutives de taille t, les initialise à 0 (ce que
ne fait pas la fonction malloc) et retourne l’adresse de la première de ces variables.
L’allocation dynamique d’un tableau de n éléments de type Type sera alors réalisée
en évaluant l’expression :
qui aura pour valeur l’adresse de l’élément de rang 0 de ce tableau. En cas d’échec
de l’allocation, du à une place mémoire insuffisante, la fonction calloc retourne
l’adresse nulle.