Вы находитесь на странице: 1из 233

Eric Berthomier & Daniel Schang

Le C en 20 heures

Juin 2013 (Mj.)

Publi sous licence


Creative Commons-BY-SA
(http ://creativecommons.org/licenses/by-sa/2.0/fr)

II

Framasoft a t cr en novembre 2001 par Alexis Kauffmann. En


janvier 2004 une association ponyme a vu le jour pour soutenir le
dveloppement du rseau. Pour plus dinformation sur Framasoft,
consulter http ://www.framasoft.net.
Se dmarquant de ldition classique, les Framabooks sont dits livres
libres parce quils sont placs sous une licence qui permet au lecteur de
disposer des mmes liberts quun utilisateur de logiciels libres. Les
Framabooks sinscrivent dans cette culture des biens communs qui,
linstar de Wikipdia, favorise la cration, le partage, la diffusion et
lappropriation collective de la connaissance.
Le projet Framabook est coordonn par Christophe Masutti. Pour plus
dinformation, consultez http://framabook.org.

Copyright 2010 : Eric Berthomier, Daniel Schang,


Framasoft (coll. Framabook)
Le C en 20 heures est plac sous licence
Creative Commons By-SA (3.0).
ISBN : 978-2-9539187-7-9
Prix : 15 euros
Dpt lgal : Fvrier 2012, Framasoft
5, avenue Stephen Pichon 75013 Paris
Pingouins : LL de Mars, Licence Art Libre
Couverture : cration par Nadge Dauvergne, Licence CC By
Mise en page avec LATEX

Imprim en France, en partenariat avec Atramenta,


chez un imprimeur certifi ImprimVert
(www.atramenta.net)

Avant de commencer

Louvrage que vous tenez dans les mains ou que vous consultez sur votre
cran a pour objectif de vous faire dcouvrir, par la pratique, la programmation en langage C.
Il a t test par de nombreux tudiants qui navaient aucune connaissance pralable de ce langage. En 20 30 heures de travail, ils sont tous
parvenus au terme de leur apprentissage. Si vous ne connaissez encore
rien la programmation et que vous dsirez apprendre, vous serez donc
probablement trs intress(e) par le contenu de cet ouvrage : il est trs
facile daccs et destin aux grands dbutants.
Il existe une multitude de faons de programmer un ordinateur, qui
dpendent du matriel dont vous disposez, du systme dexploitation
que vous utilisez et du langage de programmation que vous choisirez.
Nous avons fait le choix dun systme dexploitation libre : G NU/Linux
et du langage C, trs rpandu, largement enseign, et finalement assez
simple dans ses constructions. Nanmoins, mme si vous nutilisez pas
G NU/Linux, vous pouvez sans risque vous lancer dans la lecture de cet

VI

ouvrage. Plus de quatre-vingt-quinze pour cent de ce que vous y trouverez


est utilisable sans modification avec dautres systmes dexploitation 1 .
Ce livre nest pas un ouvrage de rfrence, que vous garderez sur une
tagre pour vous y reporter en cas de doute ou doubli. Il a t crit pour
tre lu dun bout lautre, dans lordre : il vous guidera dans votre apprentissage et vous suggrera de programmer telle chose, de tester telle autre.
En ce sens, il est orient vers la pratique de la programmation et lenseigne sans doute la manire dont les auteurs lont apprise : devant un
ordinateur, essayer de programmer quelque chose. Vous ne pourrez donc
pas profiter pleinement de cet ouvrage sans essayer de faire les nombreux
exercices quil contient. Et lorsque vous aurez fait ces exercices, vous
pourrez comparer vos solutions avec celles indiques la fin de chaque
chapitre : vous apprendrez en crivant du code, et en lisant du code. Vous
pourrez aussi travailler votre vitesse. Vous irez peut tre vite au dbut
et vous trouverez tout ceci trs facile. Il sera nanmoins ncessaire de
prendre le temps de ne pas aller trop vite : cest pourquoi nous vous encourageons ne pas ncessairement faire des copier/coller du code, mais
le saisir nouveau, afin de lassimiler, et aussi de commettre des erreurs
que vous devrez ensuite corriger.
Les premires briques de cet ouvrage ont pour origine un cours de Turbo
Pascal 2 quric Berthomier dispensait au sein de lassociation Fac Info
lUniversit de Poitiers. La seconde range de briques fut pose avec lassociation Les Mulots Chasseneuil du Poitou o Eric donna des cours
bnvoles de C sous Turbo C 2.0 et MS/DOS. Grce cette association,
ric rencontra le GULP (Groupement des Utilisateurs de Linux de Poitiers) qui lui fit dcouvrir G NU/Linux : la troisime range de briques pouvait commencer. Accompagn par dautres membres du G ULP, ric donna
des cours de C au sein de cette association lEspace Mends France de
Poitiers.
Le contenu de louvrage alors disponible sous forme de fichiers Postscript a stagn quelques annes avant dtre rcupr et adapt par Daniel
1. Les auteurs vous encouragent nanmoins trs vivement franchir le pas, et dans le
cas o vous ne voudriez pas supprimer tout simplement votre systme dexploitation, rien
ne vous empche den avoir plusieurs sur le mme ordinateur.
2. Dont quelques lignes directrices avaient elles mmes t dfinies par une autre personne.

VII

Schang, qui la utilis et enrichi dune quatrime range de briques dans


un cadre plus acadmique lESEO dAngers.
Il ne nous sera pas possible de dire combien de versions de ce cours
ont exist mais l nest pas le plus important, ce qui compte cest que
vous ayez maintenant ce livre entre les mains et ceci grce lassociation
Framasoft.

CHAPITRE

Premiers pas

Ce premier chapitre va vous permettre de raliser


vos premires compilations, donc vos premiers programmes. Bienvenue, newbie !

1. Systme dexploitation et C
Pour pouvoir raliser des programmes en C, il est ncessaire de sappuyer sur un systme dexploitation. Le systme dexploitation utilis est
ici G NU/Linux. Nanmoins, la quasi-totalit de ce qui est dcrit ici peut
tre ralis en utilisant dautres systmes dexploitation.
Cet ouvrage nest pas une introduction lutilisation de G NU/Linux.
Nous nvoquerons donc que les outils ncessaires la programmation
en C.

Le C en 20 heures

2. Utiliser un diteur sous G NU/Linux


Nous allons dans cette section tenter de dfinir une manipulation pour
lancer un diteur 1 . Il existe une multitude dditeurs de texte qui permettent de saisir des programmes : Emacs (que jutilise en ce moment
mme), Kate, Bluefish, Gedit. . .
Souvent, les diteurs sont accessibles quelque part dans un menu. En ce
qui nous concerne, nous allons lancer lditeur de texte depuis la ligne
de commande du shell. Pour cela, nous allons excuter un terminal. Selon
la distribution que vous utilisez le terminal nest pas forcment rang au
mme endroit. Voici quelques pistes qui pourront vous aider :
sous Ubuntu, faites Applications / Accessoires / Terminal ;
sous Xfce (et avec une Debian), faites clic droit, puis Terminal ;
dans un autre environnement, recherchez quelque chose qui pourrait
sappeler Terminal, Console ou Xterm.
Dans cette nouvelle fentre qui ressemble celle de la figure 1.1, excutez lditeur Scite en tapant la commande scite puis en validant.

FIGURE 1.1 - Une fentre de terminal

Lditeur doit souvrir.


1. Un diteur est un programme (comme le bloc-notes de Windows) qui nous servira
crire nos programmes.

Premiers pas

FIGURE 1.2 - Une fentre de terminal et lditeur Scite

Scite est un diteur trs simple dutilisation. Il est de plus disponible


pour plusieurs systmes dexploitation, est lger, peut tre personnalis,
offre la coloration syntaxique, permet de plier les fonctions. . .
Si en essayant de lancer la commande scite vous obtenez un message derreur comme : Commande inconnue ou Command not found, cest
que Scite nest probablement pas install sur votre machine. Vous devrez
alors regarder du ct des outils de gestion des paquetages pour ajouter
ce logiciel (peut tre disposez-vous des outils de gestion des paquetages :
Synaptic, Aptitude, Rpmdrake, Gurpmi, Yast . . . ).

Le C en 20 heures

3. Exemple de programme
Voici un premier programme. Il est fonctionnel, mme sil nest pas normalis 1 . Il affiche le mot Bonjour lcran. laide de votre diteur de
texte (dans la fentre Scite donc), tapez le texte qui se trouve lintrieur
du cadre suivant :
main () {
puts ("Bonjour");
getchar ();
}

Puis, sauvegardez ce fichier (raccourci clavier : CTRL + S ) sous le


nom suivant : programme1.c
Une fois le texte du programme tap, il faut le compiler, cest dire le
transformer en programme excutable (nous reviendrons sur la compilation plus tard). Nous allons donc ouvrir une seconde fentre dans laquelle
nous allons compiler notre programme : comme tout lheure lancez un
terminal (figure 1.1).
La compilation se fait avec le compilateur gcc. Tapez dans cette nouvelle fentre 2 :
gcc -o programme1 programme1.c

De la mme faon que vous pourriez ne pas disposer de lditeur Scite, il


se peut que vous nayez pas les outils de dveloppement. L aussi, selon
votre distribution, recherchez loutil de gestion des logiciels installs, et
installez le compilateur G CC. Il est probable que son installation induise
linstallation dautres outils de dveloppement ncessaires (la plupart des
outils dinstallation de paquets grent les dpendances entre logiciels).
Si vous navez pas fait derreur, la ligne prcdente ne provoquera aucun
affichage (pas de nouvelle, bonne nouvelle. . . ) La commande entre vient
de crer un fichier nomm programme1. Vous pouvez le vrifier en tapant
ls -l (attention, tapez bien `s -`) qui devrait vous renvoyer quelque
chose du type :
1. Nous verrons par la suite quun programme crit en langage C doit respecter certaines
normes. . .
2. Il faut se placer dans le rpertoire contenant programme1.c. Vous pouvez consulter le contenu du rpertoire courant en entrant la commande ls (la lettre ` pas le chiffre 1).
Vous pouvez vous dplacer dans un rpertoire particulier en entrant cd <nom repertoire>.

Premiers pas

-rw-r--r-- 1 dschang dschang 44 2008-10-14 11:10 programme1.c


-rwxr-xr-x 1 dschang dschang 6525 2008-10-14 11:11 programme1

En y regardant de plus prs, nous pouvons voir le fichier intitul


programme1.c qui a t sauvegard 11h10 et qui contient 44 octets.
En dessous se trouve le fichier programme1 qui vient dtre cr et qui
contient 6525 octets. Ce dernier fichier contient le code machine (code
qui est comprhensible par la machine).
Le fichier programme1 est un fichier excutable . Le fichier
programme1.c est un fichier source (source de tous les bonheurs de
G NU/Linux. . . ). Un fichier source dsigne un fichier quun tre humain
peut comprendre par opposition un excutable que seule la machine arrive comprendre. Il ne reste plus qu excuter le programme :
./programme1

La machine affichera alors Bonjour et attendra que vous appuyiez sur


ENTREE .
Nous reviendrons par la suite sur le fait quil faille taper
./programme1 et pas simplement programme1.

Par ailleurs, vous avez pu constater que la fin des lignes


se terminait par un ; sauf la premire ligne (celle qui
contient le mot main). Nous reviendrons l-dessus. . . Disons pour linstant que cest un peu comme en franais o
chaque phrase se termine par un . sauf le titre.

4. Normalisation du programme
Jusqu prsent, nous avons fait un peu dans le sale . Nous nous en
rendrons compte en demandant au compilateur dtre plus bavard. Lancez
la commande :
gcc -o programme1 programme1.c -Wall

Le C en 20 heures

Observez les insultes :


programme1.c:5: warning: return-type defaults to int
programme1.c: In function main:
programme1.c:6: warning: implicit declaration of function puts
programme1.c:9: warning: control reaches end of non-void function

Les remarques du compilateur vous paraissent peut-tre peu comprhensibles (voire offensantes) et cest normal.
Loption de compilation -Wall permet de dclencher la production de
messages soulignant toute technique autorise mais discutable , en deux
mots : viter.
Nous allons donc normaliser ce programme.
Fondamentalement, le langage C nest quun nombre restreint dinstructions et un ensemble de bibliothques. Dans ces dernires, le compilateur trouve les fonctions et les applications qui lui permettent de crer
un programme excutable. Cest un peu ce que vous faites lorsque vous
recherchez dans une encyclopdie pour raliser un expos.
Certaines bibliothques (les plus courantes) sont incluses par dfaut ce
qui permet notre programme de se compiler 1 .
La fonction puts est stocke dans la bibliothque standard dentressorties, incluse par dfaut. Nanmoins, lutilisation dune bibliothque ncessite que nous informions le compilateur de notre souhait de lutiliser : il
suffit dajouter #include <fichier en-tte bibliothque> en
dbut de programme 2 .
Ainsi, puisque nous utilisons la fonction puts, qui est dans la librairie
standard dentres/sorties, nous indiquerons en dbut de programme 3 :
#include <stdio.h>

Un autre point corriger est lajout de la ligne return 0. Tout programme doit renvoyer une valeur de retour, tout la fin. Cette valeur de
retour permet de savoir si le programme que lon excute sest correctement termin. En gnral 0 signifie une terminaison sans erreur. Enfin, il
1. Pour tre exact, cest ldition de liens qui a ici le mrite de fonctionner.
2. Le nom ajout ici nest pas exactement celui de la bibliothque, mais celui du fichier
den-tte (lextension .h est mis pour le mot anglais header qui signifie en-tte) qui correspond la bibliothque.
3. stdio vient de STanDard Input Output.

Premiers pas

faut transformer la ligne main () en int main(). Ce point sera dtaill


par la suite lorsque nous parlerons des fonctions. . .
En rajoutant ces quelques correctifs nous obtenons donc :
#include <stdio.h>
int main () {
puts ("Bonjour");
getchar (); /* Permet dattendre la frappe dune touche */
return 0;
}

5. Petit mot sur ce quest une bibliothque


linstar de ltudiant qui recherche dans des livres, nous pouvons dire
que le .h reprsente lindex du livre et le .c le contenu du chapitre
concern.
Aprs avoir lu (et retenu) le contenu des fichiers .h inclus, si le compilateur rencontre lappel la fonction puts,
il est en mesure de vrifier si la fonction figurait dans un
des include. Si ce nest pas le cas, il mettra un avertissement.

6. Un exemple de fichier en-tte


Vous trouverez ci-dessous, un extrait du fichier en-tte stdio.h. On y
retrouve notamment la dclaration de puts (en dernire ligne de lextrait)
que nous venons de mentionner et la dclaration de printf que nous
verrons dans les chapitres suivants. Cest assez compliqu. . . on y jette
juste un oeil, pas plus.)
/* Write formatted output to STREAM. */
extern int fprintf __P ((FILE *__restrict __stream,
__const char *__restrict __format, ...));
/* Write formatted output to stdout. */
extern int printf __P ((__const char *__restrict __format, ...));
/* Write formatted output to S. */
extern int sprintf __P ((char *__restrict __s,
__const char *__restrict __format, ...));

Le C en 20 heures

/* Write formatted output to S from argument list


extern int vfprintf __P ((FILE *__restrict __s,
__const char *__restrict __format,
_G_va_list __arg));
/* Write formatted output to stdout from argument
extern int vprintf __P ((__const char *__restrict
_G_va_list __arg));
/* Write formatted output to S from argument list
extern int vsprintf __P ((char *__restrict __s,
__const char *__restrict __format,
_G_va_list __arg));

ARG. */

list ARG. */
__format,
ARG. */

/* Write a string, followed by a newline, to stdout. */


extern int puts __P ((__const char *__s));

7. Complments
Explicitons notre programme :
#include <stdio.h>
int main () {
puts ("Bonjour");
getchar (); /* Permet dattendre la frappe dune touche */
return 0;
}

puts : permet dafficher du texte suivi dun retour la ligne.


getchar : permet dattendre la frappe dune touche suivie dune validation par la touche ENTREE , ou un simple appui sur la touche
ENTREE .
/* Commentaire */ : met en commentaire tout le texte compris
entre /* et */ 1 . On trouvera aussi // qui permet de mettre le reste
de la ligne courante en commentaire 2 .
Notre programme affiche donc Bonjour et attend que lon appuie sur
la touche Entre ou sur une autre touche puis la touche Entre.
1. Un commentaire est une portion de texte que le compilateur ignore mais qui peut aider
la comprhension dun lecteur humain.
2. Cette dernire faon de noter les commentaires provient du C++, mais elle est supporte par la plupart des compilateurs C.

Premiers pas

8. Squelette de programme
On peut dfinir le squelette dun programme C de la faon suivante :
/* Dclaration des fichiers denttes de bibliothques */
int main () {
/* Dclaration des variables (cf. chapitres suivants...) */
/* Corps du programme */
getchar(); /* Facultatif mais permet
dattendre lappui dune touche */
return 0; /* Aucune erreur renvoye */
}

9. Blocs
La partie de programme situe entre deux accolades est appele un bloc.
On conseille de prendre lhabitude de faire une tabulation 1 aprs laccolade. Puis retirer cette tabulation au niveau de laccolade fermante du bloc.
Ainsi, on obtient :
int main () {
Tabulation
Tout le code est frapp cette hauteur
}
Retrait de la tabulation
Tout le texte est maintenant frapp cette hauteur.

Cette mthode permet de contrler visuellement la fermeture des accolades et leurs correspondances 2 .

10. Commentaires
Bien commenter un programme signifie quune personne ne connaissant
pas votre code doit pouvoir le lire et le comprendre. Les commentaires
1. La touche de tabulation TAB est la touche du clavier gauche de la touche A .
Cette touche sert dcaler le texte.
2. Sous Scite, CTRL + E permet didentifier laccolade associe celle pointe.

10

Le C en 20 heures

sont indispensables dans tout bon programme. Ils peuvent tre placs
nimporte quel endroit. Ils commencent par /* et se terminent par */ :
/* Commentaire */

Comme nous lavons dj mentionn, vous trouverez aussi parfois des


commentaires C++ :
// Le reste de la ligne est un commentaire

11. Exercice dapplication


crivez un programme qui :
affiche Salut toi, appuie sur une touche sil te plat ;
attend lappui dune touche ;
affiche : Merci davoir appuy sur une touche .
Une fois que vous serez satisfait de votre solution, vous pourrez la comparer avec la solution qui apparat un peu plus loin.
Sous Linux, il est possible dviter de retaper chaque
fois les commandes :
Pour cela, il suffit dappuyer plusieurs fois sur la flche
vers le haut , ce qui fera rapparatre les dernires
commandes valides. Les flches haut et bas permettent ainsi de circuler dans lhistorique des commandes
entres.

12. Corrig de lexercice du chapitre


#include <stdio.h>
int main () {
/* Affiche premier message */
puts ("Salut toi, appuie sur une touche sil te plat");
getchar (); /* Attend la frappe dune touche */

Premiers pas

11

/* Affiche le second message */


puts ("Merci davoir appuy sur une touche");
return 0;
}

13. retenir
lissue de ce chapitre, il serait souhaitable de :
Se souvenir que lditeur que lon utilise dans cet ouvrage sappelle
Scite ;
Connatre les fonctions puts et getchar qui apparaissent dans le
programme suivant :
#include <stdio.h>
int main () {
puts ("Bonjour");
getchar (); /* Permet dattendre la frappe dune touche */
return 0;
}

Savoir compiler un code source de programme : gcc -o


programme1 programme1.c

Savoir excuter un programme : ./programme1

CHAPITRE

Variables (partie 1)

Les programmes prcdemment raliss taient statiques sans aucune variation et ne peuvent tre donc
dune grande utilit. Il nous faut donc maintenant voir
comment rendre ces programmes dynamiques, en introduisant la notion de variable.
Allez votre rythme, limportant est de comprendre. . .

1. Objectif
Afin de stocker des valeurs, calculer, effectuer un traitement quelconque, il est ncessaire denregistrer de manire temporaire des donnes.
Cet enregistrement ncessite la dclaration dun lieu de la mmoire qui
servira ce stockage : une variable.

13

14

Le C en 20 heures

2. Affichage : la fonction printf


#include <stdio.h>
int main () {
/* Affiche Coucou cest moi lcran puis saute une ligne */
printf ("Coucou cest moi\n");
return 0;
}

La fonction printf, tout comme puts vue prcdemment, permet


dafficher une chane de caractres. Elle est cependant beaucoup plus puissante.
La syntaxe de printf est trs complexe et pourrait elle
seule faire lobjet dun chapitre, nous nen verrons donc
que des applications au fur et mesure des besoins.

3. Notion de variable
Comme son nom lindique, une variable est quelque chose qui varie.
Cest vrai mais ce nest pas suffisant. Une variable peut tre considre
comme une bote dans laquelle on met des donnes. Il est possible de lire
le contenu de cette bote ou dcrire des donnes dans celle-ci.
La manire la plus immdiate de lire le contenu dune variable est de
simplement mentionner son nom.
La faon la plus simple daffecter une valeur une variable est loprateur daffectation =.
Essayer dutiliser une variable laquelle nous navons
encore affect aucune valeur peut donner nimporte quel
rsultat (si on affiche le contenu dune variable non initialise par exemple, on pourra obtenir nimporte quelle
valeur lcran).

15

Variables (partie 1)

Affecter une valeur une variable ayant dj une valeur


revient la modifier. En effet, une variable ne peut contenir
quune seule chose la fois. Si vous mettez une seconde
donne dans la variable, la prcdente est efface.

4. Dclaration dune variable


La dclaration dune variable se fait en utilisant la syntaxe suivante :
<son type> <son nom> ;

Comme le montre le programme qui suit, il ne faut


pas mettre les < et > comme cela apparaissait dans <son
type> <son nom> ;.
#include <stdio.h>
int main () {
int i; /* dclare un entier de nom i */
char c; /* dclare un caractre de nom c */
}

5. Application : exemples
Premier exemple, avec des variables du type entier
Lisez le programme, ainsi que les explications associes.
#include <stdio.h>
int main () {
int i; /* i : variable de type entier */
int j; /* j : variable de type entier */
i=22; /* i vaut 22 */
j=i; /* on recopie la valeur de i dans j */
/* donc j vaut aussi 22 prsent */
printf ("i vaut %d\n", i);

/* Affiche la valeur de i */

16

Le C en 20 heures

printf ("i+j vaut %d\n", i+j); /* Affiche la valeur de i+j */


return 0;
}

printf ("i vaut %d\n", i) ; : %d signifie que


lon attend une valeur entire et quil faut lafficher en
dcimal (base 10). Le %d sera remplac par la valeur
de i. Cette ligne provoquera donc laffichage suivant :
i vaut 22

printf ("i+j vaut %d\n", i+j) ; : dans ce


cas, %d est remplac par la valeur de lexpression
i+j. Nous obtiendrons laffichage suivant : i+j
vaut 44

Lexcution complte de ce programme donne donc :


i vaut 22
i+j vaut 44

Second exemple, avec des variables du type caractre


nouveau, lisez le programme ainsi que les explications associes.
#include <stdio.h>
int main () {
char car; /* car: variable de type caractre */
char f; /* f: variable de type caractre */
car=E;
f=e;
printf("car=%c f=%c\n",car,f);
return 0;
}

car=E : nous mettons dans la variable car la valeur


(le code A SCII) du caractre E.
f=e : nous mettons dans la variable f la valeur (le
code A SCII) du caractre e. Notons au passage que, f=e

17

Variables (partie 1)

signifierait affecter la valeur de la variable e (qui nexiste


pas) f. En oubliant les quotes (guillemets simples). . . .
nous aurions donc obtenu une erreur de compilation (variable inexistante).
Lexcution complte de ce programme affiche donc :
car=E f=e

En informatique, tout nest que nombre ; nous parlons


donc de la valeur de E plutt que de E car cest le code
A SCII du caractre E qui est affect la variable. Nous
reviendrons sur ce point un peu plus tard.

6. Utilisation de % dans printf


lintrieur du premier paramtre dun printf (appel le format),
lemploi de %y signifie qu lexcution, %y doit tre remplac par la
valeur correspondante qui figure dans les paramtres suivants, aprs transformation de ce paramtre dans le type puis le format dsign par y. Nous
avons notre disposition plusieurs formats daffichage, comme : %d, %x,
%c. . .
Exemple :
int i;
i=65;
printf ("Le caractre %d est %c",i,i);

nous donnera laffichage suivant :


Le caractre 65 est A

le %d est remplac par la valeur numrique de i cest dire 65.


le %c est remplac par la valeur alphanumrique (A SCII) de i cest
dire le caractre A (cf. table A SCII en annexe). Cette table est trs
utile car lordinateur ne comprend que des nombres. Elle donne

18

Le C en 20 heures

une correspondance entre les lettres (que nous autres, tres humains,
comprenons) et leur codage par la machine. En rsum, chaque fois
que nous manipulerons la lettre A, pour lordinateur, il sagira de la
valeur numrique 65. . .

7. Exercices
Dans les exercices qui suivent, vous devez utiliser ce que nous venons
de voir sur les variables, les caractres, et sur printf.
Exercice n1 Dclarer, afficher (a)
Dclarez des variables avec les valeurs suivantes 70, 82,
185 et 30 puis affichez le contenu de ces variables.
Exercice n2 Dclarer, afficher (b)
Faites la mme chose avec les caractres c, o, u, C, O, U.
Une fois que vous avez votre propre solution, comparez avec celle qui
se trouve la fin du chapitre. . .

8. Rutilisation dune variable


Il est possible de rutiliser une variable autant de fois que lon veut. La
prcdente valeur tant alors efface :
i = 3;
i = 5;
i = 7; Maintenant, i contient 7, les autres valeurs ont disparu.
car = E ;
car = G ;
car = h ; La variable car contient maintenant le caractre h.

Variables (partie 1)

19

9. Caractres spciaux
Certains caractres ( % par exemple) ncessitent dtre prcds par
le caractre \ pour pouvoir tre affichs ou utiliss.
Pour afficher un % avec printf, nous cririons :
printf("La rduction tait de 20\%");

Pour dsigner le caractre quote on crira :


char car;
car = \;

En effet, le compilateur serait perdu avec une expression


du type :
char car;
car = ;

Exercice n3 Erreur volontaire


Essayez dcrire un programme contenant car= et
constatez lerreur obtenue la compilation.

20

Le C en 20 heures

10. Exercices
Exercice n4 Okay !
Ralisez un programme qui permet dobtenir laffichage
suivant :
C

e
s
t
Ok i vaut : 1

Pour pouvoir utiliser un caractre rserv la syntaxe du


C, on utilise le caractre \ comme prfixe ce caractre (on
dit que \ est un caractre dchappement).
pour obtenir un ", on utilise donc \"
pour obtenir un , on utilise donc \

11. Corrigs des exercices du chapitre


Corrig de lexercice n1 Dclarer, afficher (a)
#include <stdio.h>
int main () {
int i,a,b,c;
i=70;
a=82;
b=185;
c=30;
printf
printf
printf
printf

("i
("a
("b
("c

return 0;
}

vaut
vaut
vaut
vaut

%d.\n",i);
%d.\n",a);
%d.\n",b);
%d.\n",c);

21

Variables (partie 1)

Corrig de lexercice n2 Dclarer, afficher (b)


#include <stdio.h>
int main () {
char a,b,c,d,e,f;
a=c;
b=o;
c=u;
d=C;
e=O;
f=U;
printf
printf
printf
printf
printf
printf

("a
("b
("c
("d
("e
("f

vaut
vaut
vaut
vaut
vaut
vaut

%c.\n",a);
%c.\n",b);
%c.\n",c);
%c.\n",d);
%c.\n",e);
%c.\n",f);

return 0;
}

Corrig de lexercice n3 Erreur volontaire


#include <stdio.h>
int main () {
char car;
car=; // erreur volontaire !!!
return 0;
}

gcc -o essai essai.c


essai.c:5:6: error: empty character constant
essai.c: In function main:
essai.c:5: error: missing terminating character
essai.c:6: error: expected ; before return

Corrig de lexercice n4 Okay !


#include <stdio.h>
int main () {
char car;
int i;
i = 1;
car = C;
printf ("\n%c",car);
car = \;
printf ("\n%c",car);

22

car = e;
printf ("\n%c",car);
car = s;
printf ("\n%c",car);
car = t;
printf ("\n%c",car);
printf ("\nOk i vaut : %d\n",i);
return 0;
}

12. retenir
Exemples de types de variables :
char permet de dfinir une variable de type caractre.
int permet de dfinir une variable de type entier.

Exemple de programme avec variables :


#include <stdio.h>
int main () {
char caractere;
int entier;
caractere = c;
entier = 1;
printf ("caractere vaut : %c\n",caractere);
printf ("entier vaut : %d\n",entier);
return 0;
}

Le C en 20 heures

CHAPITRE

Variables (partie 2)

Les quelques dfinitions de variables vues prcdemment nous ont permis de dfinir des caractres et des
nombres. Mais que seraient les nombres sans les mathmatiques et sans une bonne calculatrice ? quoi
nous serviraient ces programmes si nous devions saisir les variables dans nos codes ?

1. Objectif
Dans ce chapitre nous allons utiliser le langage C comme une simple
calculatrice et faire des additions, des multiplications. . .
Nous allons voir quafin de pouvoir utiliser la bibliothque mathmatique du langage C (#include <math.h>), il est ncessaire dajouter au
moment de la compilation 1 : -lm (lisez bien `m) ; ce qui nous donne :
1. Pour tre prcis, cest plutt ltape ddition de liens qui ncessite cette option, mais
nous y reviendrons. . .

23

24

Le C en 20 heures

gcc -o monprog monprog.c -lm

2. Exercice de mise en bouche

Exercice n1 Introduction une calculatrice


crivez un programme qui :

crit Calculatrice : et saute 2 lignes. . .


crit Valeur de a : et saute 1 ligne
attend lappui dune touche
crit Valeur de b : et saute 1 ligne
attend lappui dune touche
crit Valeur de a + b :

Normalement, vous naurez pas de soucis pour


lcrire. . . comparez ensuite avec la solution en fin de chapitre. . .
Pas pas, nous allons maintenant raliser notre petit programme de calculatrice.

3. Dclaration des variables

Exercice n2 Somme
Compltez le programme prcdent en :
dclarant 2 variables a et b de type int (entier) ;
assignant ces deux variables les valeurs 36 et 54 ;
faisant afficher le rsultat de la somme de a+b (attention, ncrivez pas le rsultat 90 dans votre programme !).
Pour faire afficher le rsultat, il est possible dutiliser la fonction
printf en utilisant une troisime variable. Mais pour tre plus concis,

nous afficherons directement de la faon suivante :

Variables (partie 2)

25

printf ("Valeur de a + b : %d",a+b) ;

%d sera remplac par la valeur de lexpression a+b.

4. Saisie des variables


Si une calculatrice lectronique se limitait calculer la somme de deux
nombres fixes, le boulier serait encore trs rpandu.
Pour saisir une variable 1 , il est possible dutiliser la fonction scanf. La
fonction scanf sutilise de la faon suivante :
scanf ("%d", &a); // saisie de la valeur a

Comme pour printf, nous reconnaissons le %d pour la saisie dun


nombre entier. Le & devant le a signifie que nous allons crire dans la
variable a.
Ae. . . En fait &a signifie ladresse mmoire de a . La fonction scanf
va donc crire dans lemplacement mmoire (la petite bote contenant la
variable) de a. Si nous oublions le &, nous crirons chez quelquun dautre,
une autre adresse. Et l a peut faire mal, trs mal. . . Mais ceci est
une autre histoire sur laquelle nous reviendrons longuement par la suite. . .
Pour linstant, noubliez pas le &.
Nous allons maintenant saisir les variables a et b. Pour exemple, voici
le code pour la saisie de a, la saisie de b reste faire par vos soins titre
dexercice.
/* Saisie de la valeur de a */
printf ("Valeur de a :\n");
scanf ("%d", &a);

Initialiser les variables


Il est conseill dinitialiser les variables avant de les utiliser. Au dbut
du programme, aprs leur dclaration, assignez la valeur 0 a et b.
1. Par saisir , nous voulons dire que lordinateur va attendre que lutilisateur entre une
valeur au clavier puis quil appuie sur la touche entre.

26

Le C en 20 heures

Une fois complt, compil, nous allons tester votre programme.


Entrez une valeur pour a puis appuyez sur ENTREE .
Renouvelez ensuite lopration pour donner la valeur de b.
Vrifiez que le rsultat est correct.
Pour aller plus rapidement, il est possible dinitialiser
une variable en mme temps que nous la dclarons. Pour
cela, rien de plus simple : ajoutez la fin de la dclaration
le symbole daffectation = suivi de la valeur dinitialisation :
int i = 10 ;

Votre programme devrait ressembler ceci (lisez le programme puis la


remarque importante qui se trouve en dessous) :
#include <stdio.h>
#include <math.h>
int main () {
int a=0;
int b=0;
printf("Calculatrice :\n\n");
printf("Valeur de a : \n");
scanf("%d",&a);
printf("\n");
printf("Valeur de b : \n");
scanf("%d",&b);
printf("\nValeur de a+b : %d\n",a+b); /*Affichage de la somme*/
getchar ();
return 0;
}

Vous pouvez tester ce programme et vous vous apercevrez que curieusement, le programme se finit alors que
vous navez mme pas eu le temps dappuyer sur la

Variables (partie 2)

27

touche ENTREE . Cest comme si le getchar() de la fin


tait purement et simplement oubli ? ! En fait, il sagit
dune petite sournoiserie du langage C ; en effet le
scanf("%d",&b) attend que vous entriez une valeur au
clavier.
Pour fixer les ides, supposons que vous entriez la valeur
1234.

La chose bien comprendre est que pour entrer cette


valeur, vous appuyez galement sur la touche ENTREE .
La subtilit tient au fait que le scanf("%d",&b) veut
juste une valeur entire. Il laissera donc la valeur de la
touche ENTREE disponible pour la prochaine instruction
qui ira lire quelque chose au clavier (en fait, lappui sur la
touche ENTREE reste disponible sur lentre standard) ;
cest prcisment le getchar() qui va le rcuprer et qui
permettra donc la sortie du programme.
Ae ae ae, dur dur dtre programmeur.
Notre ide simple de dpart commence se compliquer, et tout a pour
faire quelques oprations basiques. . .
Exercice n3 Initialisation
Dclarez une troisime valeur de type int (pensez
linitialiser 0) que nous nommerons simplement s
comme somme. Une fois les valeurs de a et b saisies, initialisez s avec la valeur de a+b. Affichez la valeur de s.
Nous devrions avoir les mmes rsultats quauparavant,
bien sr.
Exercice n4 Obtenir des rsultats
Ralisez deux petits programmes qui font :
la soustraction de deux nombres ;
la multiplication de deux nombres.
Une fois que vous avez votre solution, comparez avec la
correction propose plus loin.

28

Le C en 20 heures

5. Les types flottants


Nous allons tudier un nouveau type de donnes : les nombres virgule
flottante ou simplement flottants (float), qui permettent de reprsenter
des nombres virgule. Le type float permet de dclarer un tel nombre.
Transformez les trois programmes prcdents en utilisant le type float
au lieu du type int. Enfin, si pour les int, nous utilisions le format %d
au sein des printf et des scanf, prsent, nous allons utiliser le format
%f pour les flottants.
Pour vous aider, voici un petit morceau de programme qui permet la
saisie de la valeur de a et laffiche :
float a;
printf("Saisie de a :");
scanf("%f",&a);
printf("\n a vaut : %f\n",a);

Pour un affichage plus agrable il est possible de fixer le


nombre de chiffres aprs la virgule de la faon suivante :
%.[nombre de chiffres aprs la virgule]f

Voici un exemple : printf ("%.2f",a) ;

Exercice n5 Ouah, les 4 oprations !


Crez un quatrime programme qui ralise la division de
deux nombres. Vous pourrez vous amuser le tester avec
une division par 0 ! La solution ce petit problme sera
vu dans le chapitre sur les conditions (if).

6. Dautres fonctions utiles


La fonction abs permet dobtenir la valeur absolue dun nombre entier.
La fonction fabsf permet dobtenir la valeur absolue dun float.

Variables (partie 2)

Notez que pour utiliser ces deux fonctions mathmatiques, il faut ajouter #include <math.h> dans le source
et ajouter loption -lm dans la commande de compilation.

Exercice n6 Absolument !
Utilisez cette dernire fonction pour calculer la valeur
absolue de (a-b).

Exercice n7 Arrondissez
La fonction ceilf permet dobtenir larrondi entier suprieur dun flottant. Utilisez cette fonction pour calculer
larrondi suprieur de (a/b).

7. Corrigs des exercices du chapitre


Corrig de lexercice n1 Introduction une calculatrice
#include <stdio.h>
#include <math.h>
int main () {
printf("Calculatrice :\n\n");
printf("Valeur de a : \n");
getchar();
printf("Valeur de b : \n");
getchar();
printf("Valeur de a+b :\n");
return 0;
}

29

30
Corrig de lexercice n2 Somme
#include <stdio.h>
#include <math.h>
int main () {
int a,b;
a=36;
b=54;
printf("Valeur de a+b : %d\n",a+b);
getchar();
return 0;
}

Corrig de lexercice n3 Initialisation


#include <stdio.h>
#include <math.h>
int main () {
int a,b;
int s;
a=0;
b=0;
printf("Calculatrice :\n\n");
printf("Valeur de a : ");
scanf("%d",&a);
printf("\n");
printf("Valeur de b : ");
scanf("%d",&b);
s=a+b;
printf("Valeur de a+b : %d\n",s);
getchar ();
return 0;
}

Corrig de lexercice n4 Obtenir des rsultats


...
int d;/* Rsultat de la diffrence */
int m;/* Rsultat de la multiplication */
d = a-b;
printf("Valeur de a-b : %d\n", d);
m = a*b;

Le C en 20 heures

Variables (partie 2)

31

printf("Valeur de a*b : %d\n", m);


...

Corrig de lexercice n5 Ouah, les 4 oprations !


...
int d;/* Rsultat de la division */
d = a/b;
printf("Valeur de a/b : %d\n", d);
getchar ();
...

Corrig de lexercice n6 Absolument !


Calculez la valeur absolue de a-b
float r;
...
r=fabsf(a-b);
printf("Valeur de r : %f\n",r);

Corrig de lexercice n7 Arrondissez


Calculez larrondi de a+b
float arrondi;
...
arrondi=ceilf(a/b);/* Calcul de larrondi */
printf("Valeur arrondie : %f\n",arrondi);

Remarque : Il aurait t possible dutiliser %d du fait que larrondi est


un nombre entier !

32

Le C en 20 heures

8. retenir
#include <stdio.h>
// ne pas oublier pour lutilisation des fonctions mathmatiques
#include <math.h>
int main () {
float pi=3.14159;
int i=10;// dclaration + initialisation
int j;// dclaration seule (pas dinitialisation)
// Attention, bien mettre %f et non pas %d
printf ("pi vaut environ: %f",pi);
printf("\n i vaut:%d\n",i);
printf("\n entrez une valeur:");
scanf("%d",&j);// Attention ne pas oublier le &
printf("\n vous venez dentrer %d",j);
return 0;
}

Nous compilerons ce programme comme ceci 1 : gcc -o


programme1 programme1.c -lm

Nous le lancerons comme ceci : ./programme1

1. Mme si, en loccurence nous nutilisons pas de fonctions de la librairie mathmatique.

CHAPITRE

Conditions

Nous y sommes, nous savons saisir des nombres, des


caractres, faire des calculs. . . Nous savons donc transformer notre ordinateur en une calculatrice. Comme la
calculatrice, nous allons faire un gros BEEP, lors de la
ralisation dune division par 0. Il nous faut donc voir
comment viter ce schma en apportant une rponse
claire ce problme. . .

1. Objectif
Dans ce chapitre, nous allons voir comment introduire des conditions
dans nos programmes, de manire ce que selon les circonstances, telle
ou telle partie du code soit excute.

33

34

Le C en 20 heures

2. Exercice de mise en bouche


crivez un programme qui met en application le thorme de Pythagore
pour calculer la longueur de lhypotnuse dun triangle rectangle.
Rappelons que dans un triangle rectangle, la longueur
de lhypotnuse (le plus grand ct) peut se calculer en
appliquant la formule suivante :
Longueur hypotnuse =

a2 + b2

o a et b sont les longueurs des deux autres cts.

La racine carre sobtient par lutilisation de la fonction


sqrt(valeur) contenue dans la bibliothque de mathmatiques (#include <math.h>)

a2 peut sobtenir par a*a.


Exercice n1 Pythagore
1. Recherchez les variables ncessaires et dclarez-les
dans le programme.
2. Faites saisir a au clavier.
3. Faites saisir b au clavier.
4. Appliquez la formule et affichez le rsultat.

Rappelons que, afin de pouvoir utiliser la bibliothque


mathmatique du C (#include <math.h>), il est ncessaire dajouter au moment de la compilation -lm (un
tiret, la lettre `, la lettre m) ce qui nous donne :
gcc -o monprog monprog.c -lm

Conditions

35

Une fois que vous tes satisfait(e) de votre solution, vous pourrez comparer avec la solution qui se trouve la fin de ce chapitre.

3. Condition : Si Alors Sinon


En franais, nous pourrions dire quelque chose de ce genre :
si (je travaille)
alors je progresserai
sinon je stagnerai

En se rapprochant un peu du langage C, on traduirait (toujours en franais) ce premier programme ainsi :


si (je travaille) {
alors je progresserai
}
sinon {
je stagnerai
}

Enfin, le programme en langage C ressemblera ceci :


if (je travaille) {
je progresserai
}
else {
je stagnerai
}

Les conditions sexpriment avec des oprateurs logiques dont nous allons expliquer tout de suite la signification et lutilisation.

36

Le C en 20 heures

4. Oprateurs de comparaison
Les oprateurs de comparaison servent comparer deux nombres entre
eux :
Signification
Infrieur
Suprieur
gal
Diffrent
Infrieur ou gal
Suprieur ou gal

Oprateur
<
>
==
!=
<=
>=

TABLE 4.1 - Oprateurs de comparaison

Voici un exemple de programme qui demande lutilisateur de saisir


deux valeurs puis affiche la plus grande :
#include <stdio.h>
int main () {
int valeur1;
int valeur2;
/* Saisie de valeur1 */
printf ("Entrez une 1ere valeur : ");
scanf ("%d",&valeur1);
/* Saisie de valeur2 */
printf ("Entrez 2eme valeur : ");
scanf ("%d",&valeur2);
if (valeur1<valeur2)
printf("La plus grande valeur est: %d\n",valeur2);
else
printf("La plus grande valeur est: %d\n",valeur1);
return 0;
}

37

Conditions

5. Oprateurs logiques
Les oprateurs logiques permettent de combiner des expressions logiques.
Libell
Et (and)
Ou (or)
Non (not)

Oprateur
&&
||
!

TABLE 4.2 - Oprateurs logiques

| se nomme en anglais un pipe (prononcer pape). Des exemples


suivront bientt. . .

6. Vrai ou faux
La valeur Vrai peut tre assimile la valeur numrique 1. En programmation C, la valeur Vrai est associe toute valeur non nulle.
La valeur Faux peut tre assimile la valeur numrique 0.
Loprateur Ou (||) correspond alors une addition :
Ou
Vrai
Faux

Vrai
Vrai
Vrai

Faux
Vrai
Faux

TABLE 4.3 - Loprateur ou

+
1
0

1
2
1

0
1
0

TABLE 4.4 - Loprateur ou : analogie avec laddition

38

Le C en 20 heures

En lisant la valeur donne lintersection de la ligne et de la colonne,


on obtient donc le rsultat de notre opration. Par exemple :
Ligne 1 / Colonne 1, VRAI ou VRAI donne VRAI, 1+1=2 (soit Vrai)
En logique pure, on crira plus 1+1=1 du fait que toute valeur non nulle
est considre comme Vrai.
Loprateur Et (&&) correspond alors une multiplication 1 :
Et
Vrai
Faux

Vrai
Vrai
Faux

Faux
Faux
Faux

TABLE 4.5 - Loprateur et

*
1
0

1
1
0

0
0
0

TABLE 4.6 - Loprateur et : analogie avec la multiplication

En lisant la valeur donne lintersection de la ligne et de la colonne,


on obtient donc le rsultat de notre opration. Par exemple :
Ligne 1 / Colonne 1, VRAI et VRAI donne VRAI, 1*1=1 (soit Vrai)

Loprateur Not ( !) permet dobtenir la ngation, ainsi :


!(Vrai) = Faux
!(Faux) = Vrai
1. Lanalogie entre les oprateurs logiques dune part et les oprations daddition et de
multiplication dautre part nest pas parfaite. Elle permet nanmoins de se faire une ide plus
calculatoire du fonctionnement des oprateurs logiques.

39

Conditions

Par exemple :
int i1=1;
int i2=0;
printf("i1 || i2 = %d",i1||i2);
/* affichera 1 car : vrai||faux=vrai et vrai vaut 1 */
printf("i1 && i2 = %d",i1&&i2);
/* affichera 0 car : vrai&&faux=faux et faux vaut 0 */
printf("contraire(1)=%d",!(1));
/* affichera 0 car : !(vrai)=faux et faux vaut 0 */

7. Combinaison
Toutes les oprations logiques peuvent se combiner entre elles. Il faut
nanmoins veiller aux diffrentes priorits des oprateurs et il faut que la
condition dans sa totalit soit entoure de ().
Les exemples suivants montrent ce qui est possible et ce qui ne lest
pas :
Correct

if (car == a)

Incorrect

if car == a

Correct

if ( car == a || car == A)

Incorrect

if (car == a) || (car == A)

Correct

if ((c == a || c == A) && (c2 ==


G))

Incorrect

if (c == a || c == A) && (c2 == G)

TABLE 4.7 - Oprateurs : formulations correctes et incorrectes

40

Le C en 20 heures

Vous verrez souvent ce type de code crit :


if (err) {
/* Alors faire quelque chose */
}

En appliquant ce qui a t vu prcdemment, on en dduit que ce code signifie que


si (err != 0) /* si er diffrent de 0 */
{
/* Alors faire quelque chose */
}

Ce qui donne en langage C :


if (err != 0) { /* si er diffrent de 0 */
/* Alors faire quelque chose */
}

Dans

limmdiat,
(er) ! ! !

prfrez

if (err !=0)

if

8. Accolades
Les accolades entourant les blocs dinstructions dune condition
peuvent tre omises si le bloc nest constitu que dune seule instruction.
Voici une version lourde :
/* VERSION LOURDE : */
if (car == b) {
printf ("car vaut b.");
}
else {
printf ("car est diffrent de b.");
}

Voici une version plus lgre :


/* VERSION LEGERE : */
if (car == b)
printf ("car vaut b.");
else
printf ("car est diffrent de b.");

Conditions

Au contraire, dans lexemple ci-dessous, il faut imprativement mettre des accolades !


if (car == b) { /* il y a 2 instructions, il faut
, donc mettre des { } */
printf ("car vaut "); /* 1re instruction */
printf(" %c",car); /* 2me instruction */
}
else
printf ("car est diffrent de b.");

9. Exercices

Exercice n2 Variable positive, ngative ou nulle


Faites saisir une variable de type entier et indiquez
lutilisateur si celle-ci est strictement positive, strictement
ngative ou nulle. Votre code contiendra quelque chose
comme ceci :
if (a > 0)
printf ("Valeur positive");
else
printf ("Valeur ngative");

Exercice n3 Voyelles, consonnes


Faites saisir une variable de type caractre et indiquez
lutilisateur si celle-ci est une voyelle ou une consonne.
On considrera que le caractre saisi est en minuscule.
Notez que si le caractre saisi est une lettre et nest pas
une voyelle, cest ncessairement une consonne.

41

42

Le C en 20 heures

10. Corrections des exercices du chapitre


Corrig de lexercice n1 Pythagore
#include <stdio.h>
#include <math.h>
int main () {
float h; /* valeur de lhypotnuse */
float a; /* a,b les deux autres cts */
float b;
/* Initialisation des variables par prcaution */
a = 0;
b = 0;
/* Saisie de a */
printf ("Valeur du premier petit ct : ");
scanf ("%f",&a);
/* Saisie de b */
printf ("Valeur du second petit ct : ");
scanf ("%f",&b);
/* Calcul de la longueur de lhypotnuse */
h = sqrt (a*a + b*b);
/* Affichage du rsultat */
printf ("Lhypotnuse mesure : %.2f\n",h);
/* Attendre avant de sortir */
getchar ();
return 0;
}

Corrig de lexercice n2 Variable positive, ngative ou nulle


#include <stdio.h>
int main () {
/* Variable pour stocker la valeur saisie */
int a = 0;
/* Saisie de a */
printf("Saisie de a : ");
scanf("%d",&a);
/* Strictement ngative ? */
if (a < 0)
printf("la variable a est ngative.\n");
else {
/* Strictement positive ? */

Conditions

43

if (a > 0)
printf("la variable a est positive\n");
/* Sinon a est nulle */
else
printf("la variable a est nulle\n");
}
getchar ();
return 0;
}

Corrig de lexercice n3 Voyelles, consonnes


#include <stdio.h>
int main () {
/* Variable pour stocker la valeur saisie */
char car;
/* Saisie du caractre a */
printf("Saisie du caractre : ");
scanf("%c",&car);
/* Test condition car voyelle minuscule */
if ((car == a) || (car == e) || (car == i) || (car == o)
, || (car ==u) || (car == y))
printf("la variable car est une voyelle.\n");
else
printf("la variable car est une consonne.\n");
getchar ();
return 0;
}

11. retenir
La valeur Vrai peut tre assimile la valeur numrique 1 ou toute
valeur non nulle.
La valeur Faux peut tre assimile la valeur numrique 0.
Ne pas oublier les parenthses lorsquil y a un if :
if a > 0 // ne sera pas compil !!!
printf ("Valeur positive");
else
printf ("Valeur ngative");

au contraire, il faudrait crire :

44

if (a > 0)
printf ("Valeur positive");
else
printf ("Valeur ngative");

Diffrencier loprateur daffectation =


et loprateur de comparaison ==.

Le C en 20 heures

CHAPITRE

Mise au point

Si nous avons bien travaill est une condition


ncessaire et suffisante la ralisation du chapitre qui
va suivre.
Nous avons en effet maintenant tous les lments en
main pour raliser des programmes simples et fonctionnels. Par la mme occasion, nous allons voir comment excuter un programme autant de fois quon le
souhaite.

1. Objectif
Lobjet de ce chapitre est de raliser une pause dans lapprentissage du
C et de sattacher ce que lon est capable de raliser avec le peu de
moyens que lon a.
Ce chapitre est constitu de trois exercices de difficult croissante. Ils
nous permettront dapprendre utiliser une nouvelle fonction et de raliser un exercice complet de programmation.
45

46

Le C en 20 heures

2. Plus petit ou plus grand

Exercice n1 Plus grand ou plus petit que. . .


Ralisez un programme qui saisit un nombre puis indique lutilisateur si le nombre saisi est plus grand ou
plus petit quun autre nombre dfini lavance dans le programme.

Par exemple :
si (nbre_saisi < 10)
alors crire "plus petit"

Vous pouvez reprendre lexercice du chapitre sur les


conditions qui indiquait si un nombre est strictement positif, strictement ngatif ou nul.

3. Retour sur getchar()


La fonction getchar() permet dattendre la frappe dun caractre au
clavier, de le lire et de le renvoyer. Deux utilisations peuvent donc tre
faites de getchar(),
la premire est celle permettant dattendre la frappe dune touche sans
se soucier de sa valeur, simplement pour marquer une pause.
la seconde est celle permettant de lire un caractre au clavier.

getchar();

char car;
car = getchar();

47

Mise au point

chaque fois, getchar() effectue le mme traitement :


attendre la frappe dune touche au clavier suivie de la touche
ENTREE

renvoyer le caractre frapp.


Dans le premier exemple, ce caractre est simplement ignor.

4. Boucle : Faire . . . Tant que (condition)


do ... while (traduisez par Faire . . . Tant que) permet de raliser
une suite dinstructions tant quune ou plusieurs conditions sont remplies.

1. lisez le programme suivant


2. lisez les explications qui figurent en dessous du
programme
3. excutez, testez, comprenez ce programme. . .
#include <stdio.h>
int main () {
char car= ;
int sortie=0;
do {
printf ("Appuyez sur S pour sortir !\n");
car = getchar ();
/* On le compare pour savoir si lon peut
, sortir: */
sortie = ((car == s) || (car == S));
}
while (sortie==0);
return 0;
}

Notez que la touche ENTREE utilise pour la saisie du


caractre est elle-mme traite comme un caractre saisi
et provoque donc aussi laffichage de la phrase Appuyez
sur S pour sortir. . . qui saffiche donc deux fois.

48

Le C en 20 heures

un nombre entier vaut la valeur logique vraie si ce


nombre est diffrent de 0.
un nombre entier vaut la valeur logique faux si ce
nombre est gal 0.
|| est un ou logique , donc au niveau de la ligne :
sortie=((car==s)||(car==S)) ;
lorsque car vaudra s ou S, sortie vaudra 1
cest--dire Vrai. Dans tous les autres cas, sortie
vaudra 0, soit la valeur Faux.

Ds lors, les deux extraits de programme suivants sont


quivalents :
sortie = ((car == s) || (car == S));

if ((car == s) || (car == S))


sortie=1;
else
sortie=0;

Pour stopper un programme qui boucle, il faut presser simultanment


sur les touches CTRL + C (break).
Vous pouvez tester cette possibilit en excutant le programme suivant :
#include <stdio.h>
int main () {
int i=1;
do {
printf(" i=%d \n",i);
i=i+1;
}
while (i>0); /* Test toujours vrai ! */
return 0;
}

Exercice n2 Sortir de la boucle


Transformez lexemple prcdent afin que lon sorte de
la boucle uniquement quand lutilisateur a saisi le nombre
10.

49

Mise au point

La saisie dun nombre ne se fait pas par getchar mais


par scanf.

5. Oprateur modulo
Loprateur qui donne le reste de la division entire (oprateur modulo)
est not % en C. Ainsi, 10%2 vaut 0 car la division entire de 10 par 2
donne 5 et il ny a pas de reste. En revanche, 10%3 vaut 1 car la division
entire de 10 par 3 donne 3 et il reste 1.
Par exemple :
int z;
z=10%2;
printf("10 modulo 2=%d\n",z);
z=10%3;
printf("10 modulo 3=%d\n",z);

. . . va nous afficher :
10 modulo 2=0
10 modulo 3=1

6. Nombres pseudo-alatoires
Voici un petit exemple de programme qui permet dobtenir des nombres
pseudo-alatoires entre 0 et 99 :

#include <stdio.h>
#include <stdlib.h> // sert pour les fonctions
, srand et rand
#include <time.h>
int main() {
int nb_alea=0;
/* Initialisation du gnrateur de nombres
base sur la date et lheure */
srand (time (NULL));

50

Le C en 20 heures

nb_alea = rand() % 100;


printf ("Nombre alatoire : %d\n",nb_alea);
return 0;
}

srand (time (NULL)) permet dinitialiser le gnrateur de nombres pseudo-alatoire. Nous reviendrons
sur ce point par la suite.
rand() renvoie un nombre entier compris entre 0 et
RAND_MAX.
rand()%100 est donc le reste de la division entire
dun nombre pseudo-alatoire (ventuellement trs
grand) par 100, cest dire un nombre compris entre
0 et 99. . .
#include <stdio.h>
#include <stdlib.h>
int main() {
/* Pour notre information */
printf ("RAND_MAX : %ld\n", RAND_MAX);
return 0;
}

Exercice n3 Deviner un nombre


En vous aidant de ce qui a t fait prcdemment, ralisez un petit jeu qui :
1. Initialise un nombre entre 0 et 99.
2. Tente de faire deviner ce nombre lutilisateur en
lui indiquant si le nombre trouver est plus petit ou
plus grand que sa proposition.

Voici un exemple de dialogue avec lordinateur :


Entrez votre nombre: 50

Mise au point

Cest plus !
Entrez votre nombre: 25
Cest moins !
...
Gagn !!!

7. Corrigs des exercices du chapitre


Corrig de lexercice n1 Plus grand ou plus petit que. . .
#include <stdio.h>
int main () {
int nb_choisi = 33;
int nb_saisi = 0;
printf ("Votre nombre : ");
scanf ("%d",&nb_saisi);
if (nb_choisi < nb_saisi)
printf ("Mon nombre est plus petit.\n");
else {
if (nb_choisi == nb_saisi)
printf ("Cest exactement mon nombre.\n");
else
printf ("Mon nombre est plus grand.\n");
}
return 0;
}

Corrig de lexercice n2 Sortir de la boucle


#include <stdio.h>
#include <stdlib.h>
int main () {
int valeur=0;
do {
printf ("Votre nombre : ");
scanf ("%d",&valeur);

51

52

Le C en 20 heures

}
while (valeur != 10);
return 0;
}

Corrig de lexercice n3 Deviner un nombre


#include <stdio.h>
#include <stdlib.h> /* pour les valeurs alatoires */
#include <time.h>
int main () {
int nb_hasard = 0;
int votre_nb = 0;
srand (time (NULL));
nb_hasard = rand () % 100 ;/* Nombre entre 0 et 100 */
do {
printf("Votre nombre : ");
scanf("%d",&votre_nb);
if (nb_hasard < votre_nb)
printf ("\nMon nombre est plus petit\n");
else {
if (nb_hasard > votre_nb)
printf ("\nVotre nombre est plus grand\n");
}
}
while (votre_nb != nb_hasard);
printf ("Trouv\n");
return 0;
}

CHAPITRE

Et les Shadoks pompaient :


je pompe donc je suis

Nous savons maintenant raliser de nombreux programmes, or malgr tout, les outils nous manquent
pour raliser toutes les tches que nous souhaiterions.

Les Shadoks est une srie tlvise danimation franaise en 208


pisodes de 2 3 minutes, cre par Jacques Rouxel, produite par la
socit AAA (Animation Art-graphique Audiovisuel) et diffuse entre le
29 avril 1968 et 1973 (trois premires saisons) et partir de janvier 2000
(quatrime saison) sur Canal+ et rediffuse sur Cartoon Network. . . 1
1. Source : http://fr.wikipedia.org/wiki/Les_Shadoks

53

54

Le C en 20 heures

1. Objectifs
Vous trouverez beaucoup dexercices dans ce chapitre o plusieurs
petites aides de programmation seront donnes notamment en ce qui
concerne lincrmentation et la dcrmentation dune variable.

2. Boucle While
De la mme faon que :
do {...} while(condition);

il est possible dutiliser :


while(condition) {}

char car = ;
while ((car != s) && (car != S)) {
car = getchar ();
}

Donne seule, la ligne : while(condition); signifie tant que condition est vraie, on revient
la mme ligne soit sur soi mme.
Si la condition est toujours vraie, on tombe alors dans un
puits (le programme reste bloqu). Sinon, on passe immdiatement la ligne/instruction suivante.

3. Et les Shadoks apprenaient que reprendre quivaut


apprendre
Ce nest quen essayant continuellement que lon finit par russir. . . En
dautres termes. . . Plus a rate et plus on a de chances que a marche. . .
(http://www.lesshadoks.com).

Et les Shadoks pompaient : je pompe donc je suis

55

Exercice n1 Touche-touche
Traduisez en langage C, compltez avec les variables ncessaires, compilez, excutez, comprenez :
Faire
Saisir une touche
Tant Que (touche != S) et (touche != s)

Exercice n2 Saisir un nombre


Traduisez en langage C, compltez avec les variables ncessaires, compilez, excutez, comprenez :
Faire
Saisir un nombre
Tant Que (nombre != 10)

Notez que la saisie dun nombre ne se fait pas par


la fonction getchar() mais par la fonction scanf
(reportez-vous la section 3.4 pour plus de prcisions).

4. Fonction toupper()
Le problme de la comparaison de la minuscule et de la majuscule de
lexercice 1 peut tre contourn par lutilisation de la fonction toupper
qui transforme un caractre minuscule en majuscule. Pour lutiliser, il faut
inclure le fichier den-tte ctype.h par : #include <ctype.h>.
La fonction toupper sutilise de la faon suivante :
#include <stdio.h>
#include <ctype.h>
int main () {
char car;
char car_min;
car_min = a;
car = toupper (car_min);
printf ("%c",car);

56

Le C en 20 heures

return 0;
}

Ce programme affichera : A

5. tant quen emporte le Shadok

Exercice n3 Recommencez ! (a)


crivez le programme : Tant que je ne tape pas un
nombre impair compris entre 1 et 10 je recommence la
saisie dun nombre.
Exercice n4 Recommencez ! (b)
crivez le programme : Tant que je ne tape pas une
voyelle je recommence la saisie dune touche.

6. Et les Shadoks continuaient pomper pour obtenir le


rsultat
Dans les exercices qui suivent, la notion de compteur intervient. Un
compteur est une variable numrique que lon dcrmente (-1) ou incrmente (+1) suivant nos besoins.
Par exemple :

int i;
i=1;
i=i+1;
printf("i vaut: %d",i); // affichera 2

En effet, lorsque nous crivons i=1, lordinateur range la valeur 1 dans


la case mmoire dsigne par i. Lorsque nous demandons : i=i+1, lordinateur commence par prendre connaissance de la valeur de i en mmoire

Et les Shadoks pompaient : je pompe donc je suis

57

(ici 1) et lui ajoute 1. Le rsultat est stock dans la case mmoire associe
i. Finalement, i vaudra 2.
Pour gagner du temps, le langage C nous permet de remplacer une expression comme : i=i+1 par lexpression suivante : i++ qui fera exactement la mme chose.
int i;
i++; /* Incrmente le compteur i */
i--; /* Dcrmente le compteur i */

Dans les exemples prcdents, le nombre de caractres entrs peut donc


tre comptabilis en ajoutant 1 une variable chaque fois quune touche
est frappe.
Exercice n5 Recommencez ! (c)
crivez le programme : Tant que je nai pas saisi 10
nombres, je recommence la saisie dun nombre.
Exercice n6 Recommencez ! (d)
crivez le programme : Tant que je nai pas saisi 10 caractres, je recommence la saisie dun caractre.
Dans les exercices qui prcdent, de petites difficults peuvent surgir. . .
Nous vous invitons donc vous pencher plus rapidement sur la solution.

7.

Dans le clan des Shadoks, on trie, voyelles, chiffres


premiers

Exercice n7 Recommencez ! (e)


crivez le programme : Tant que je nai pas saisi 10
voyelles, je recommence la saisie dune touche.
Vous prendrez soin dindiquer lutilisateur combien de
voyelles il lui reste entrer.

58

Le C en 20 heures

Exercice n8 Recommencez ! (f)


crivez le programme : Tant que je nai pas saisi 10
chiffres premiers (2,3,5 ou 7), je recommence la saisie dun
chiffre.
Vous prendrez soin dindiquer lutilisateur combien de
chiffres premiers il lui reste entrer.

8. Incrmentations, pr-incrmentations. . .
Nous avons vu quincrmenter dsignait la mme chose quaugmenter
la valeur dune variable de 1 :
i++; i=i+1;

Il y a cependant une nuance subtile.


Pour bien comprendre, tudions le programme suivant :
#include <stdio.h>
int main () {
int n=5;
int x;
x=n++;
printf ("x: %d n: %d\n",x,n);
return 0;
}

Celui-ci retourne le rsultat suivant :


x: 5 n: 6

Analysons ce qui se passe lors de lapplication de la ligne (x=n++) :


1. on affecte n x, donc x va contenir la valeur 5
2. on augmente n de 1, donc n vaudra 6
On parlera dans ce cas dincrmentation post-fixe. . .

Et les Shadoks pompaient : je pompe donc je suis

Voici un autre exemple :


#include <stdio.h>
int main () {
int n=5;
int x=0;
x=++n;
printf ("x: %d n: %d\n",x,n);
return 0;
}

Celui-ci retourne le rsultat suivant :


x: 6 n: 6

Analysons ce qui se passe lors de lapplication de la ligne(x=++n) :


1. on augmente n de 1, donc n vaudra 6
2. on affecte n x, donc x va contenir la valeur 6
On parlera dans ce cas dincrmentation pr-fixe. . .
x=i++
x=i-x=++i
x=--i

Copie dabord la valeur de i dans x et incrmente i aprs


Copie dabord la valeur de i dans x et dcrmente i aprs
Incrmente i dabord puis copie le contenu de i dans x
Dcrmente i dabord puis copie le contenu de i dans x
TABLE 6.1 - Incrmentation / Dcrmentation

59

60

Le C en 20 heures

9. Corrigs des exercices du chapitre


Corrig de lexercice n1 Touche-touche
#include <stdio.h>
int main () {
char car = \0;
printf("Tapez s ou S pour arrter ...\n");
do {
car = getchar ();
}while ((car != s) && (car != S));
return 0;
}

Corrig de lexercice n2 Saisir un nombre


#include <stdio.h>
int main () {
int nbre = 0;
printf("Tapez 10 pour arrter ...\n");
do {
scanf ("%d", &nbre);
}while (nbre != 10);
return 0;
}

Corrig de lexercice n3 Recommencez ! (a)


#include <stdio.h>
int main () {
int nbre = 0;
printf("Tapez un chiffre impair pour arrter ...\n");
while ((nbre!=1) && (nbre!=3) && (nbre!=5) && (nbre!=7) && (nbre
, !=9))
scanf("%d", &nbre);
return 0;
}

ou bien :
#include <stdio.h>
int main () {
int nbre = 0;
printf("Tapez un chiffre impair pour arrter ...\n");

Et les Shadoks pompaient : je pompe donc je suis

61

while ((nbre < 0) || (nbre >= 10) || (nbre%2==0))


scanf("%d", &nbre);
return 0;
}

Corrig de lexercice n4 Recommencez ! (b)


#include <stdio.h>
#include <ctype.h>
int main () {
char car = \0;
printf("Tapez une voyelle pour arrter ...\n");
while ((car != A) && (car != E) && (car != I) &&
(car != O) && (car != U) && (car != Y)){
car = getchar ();
car = toupper (car);
}
return 0;
}

Corrig de lexercice n5 Recommencez ! (c)


Il conviendrait dans cet exercice de vrifier que lutilisateur a bien entr
uniquement des nombres et pas aussi des caractres. Le lecteur intress
pourra consulter la documentation de scanf et essayer dutiliser la valeur que renvoie cette fonction pour rgler ce problme.
#include <stdio.h>
int main () {
int nbre = 0;
int nb_nbre = 0;
printf("Tapez 10 nombres pour arrter ...");
do {
scanf("%d",&nbre);
nb_nbre ++;
}while (nb_nbre != 10);
return 0;
}

62

Le C en 20 heures

Corrig de lexercice n6 Recommencez ! (d)


Prenez soin de lire lexemple dexcution et son explication :
#include <stdio.h>
int main () {
char car = \0;
int nbre = 0;
printf("Tapez 10 caractres pour arrter ...");
do {
car = getchar ();
nbre ++;
printf("jai lu (%c) nbre=%d\n",car,nbre);
}while (nbre != 10);
return 0;
}

Voici un exemple dexcution :


Tapez 10 caractres pour arrter ... 123456789
jai lu (1) nbre=1
jai lu (2) nbre=2
jai lu (3) nbre=3
jai lu (4) nbre=4
jai lu (5) nbre=5
jai lu (6) nbre=6
jai lu (7) nbre=7
jai lu (8) nbre=8
jai lu (9) nbre=9
jai lu (
) nbre=10

Lutilisateur a donc tap 123456789 suivi de la touche entre, il a donc


bien tap 10 caractres. Ce que montre laffichage de la dernire ligne
avec la parenthse ouvrante sur une ligne et la parenthse fermante sur la
ligne suivante.
Vous pouvez faire dautres essais. . .
Corrig de lexercice n7 Recommencez ! (e)
#include <stdio.h>
#include <ctype.h>
int main () {
char car;
int nb_nbre = 10;

Et les Shadoks pompaient : je pompe donc je suis

63

printf("Tapez encore %d voyelles pour arrter...\n",nb_nbre);


do {
car=getchar();
car=toupper(car);
if (car==A || car==E || car==I || car==O || car==U)
, {
nb_nbre--;
printf("Tapez encore %d voyelles pour arrter...\n",nb_nbre);
}
}
while (nb_nbre != 0);
return 0;
}

Corrig de lexercice n8 Recommencez ! (f)


#include <stdio.h>
#include <ctype.h>
int main () {
int nb_nbre = 10;
int nbre;
printf("Tapez encore %d chiffres premiers...\n",nb_nbre);
do {
scanf("%d",&nbre);
if (nbre==2 || nbre==3 || nbre==5 || nbre==7) {
nb_nbre--;
printf("Tapez encore %d chiffre(s) premier(s) pour arrter
, ...\n",nb_nbre);
}
}
while (nb_nbre != 0);
return 0;
}

64

Le C en 20 heures

10. retenir
Voici un exemple de programme qui rsume ce qui a t vu dans ce
chapitre. Ce programme doit afficher lcran tous les nombres pairs infrieurs 100 :
#include <stdio.h>
int main () {
int i=0;
while (i != 100) {
if (i%2==0) /* reste de la division de i par 2 */
printf("%d ",i);
/* pas de else ni de {} ici, cest inutile...*/
i++;
}
return 0;
}

Voici une autre version (meilleure) :


#include <stdio.h>
int main () {
int i=0;
while (i!=100) {
printf("%d ",i);
i+=2;
}
return 0;
}

Enfin, noubliez pas le tableau :


x=i++
x=i-x=++i
x=--i

Copie dabord la valeur de i dans x et incrmente i aprs


Copie dabord la valeur de i dans x et dcrmente i aprs
Incrmente i dabord puis copie le contenu de i dans x
Dcrmente i dabord puis copie le contenu de i dans x
TABLE 6.2 - Incrmentation / Dcrmentation (bis)

CHAPITRE

Boucles

Dsormais, nos programmes sarrtent maintenant


quand nous le dsirons, nous saisissons des chiffres,
des lettres et savons comment les reconnatre. Ces mthodes sont suffisantes, mais parfois dautres peuvent
tre utiles.

1. Objectifs
Dans ce chapitre, nous tudierons la possibilit de raliser des actions
un certain nombre de fois.

2. Et les Shadoks pdalrent pendant 15 tours


Afin deffectuer un certain nombre de fois une tche, nous utilisons
linstruction for de la faon suivante (avec i, une variable de type entier (int par exemple)) :
65

66

Le C en 20 heures

for (i=point de dpart; i<point darrive; i=i+pas) {


instruction(s) rptes(s);
}

Ceci est rigoureusement quivalent :


i=point de dpart;
while (i<point darrive) {
instruction(s) rptes(s);
i=i+pas;
}

Par souci de simplicit, nous dirons simplement que la suite dinstructions suivante :
for (i=0; i<15; i++) {
instr;
}

signifie que lon va excuter instr pour i variant de 0 14 inclus


(<15) cest dire 15 fois.
Par exemple :
#include <stdio.h>
int main () {
int i;
for (i=0; i<15; i++){
printf("Je me rpte pour i valant %d\n",i);
}
printf("Linstruction a t rpte... 15 fois\n"
, );
return 0;
}

3. Syntaxe
De la mme faon que le if, le for ne ncessite pas daccolades sil
ny a quune instruction rpter.

67

Boucles

Nous pouvons utiliser cette fonctionnalit dans le programme prcdent en remplaant :


for (i=0; i<15; i++) {
printf("Je me rpte pour i valant %d\n",i);
}

par :
for (i=0; i<15; i++)
printf ("Je me rpte pour i valant %d\n",i);

4. Notion de double boucle


Il est possible de remplacer les instructions contenues dans une boucle
par une autre boucle afin de raliser une double boucle. Nous obtenons
donc :
Pour i allant de ... ... {
...
Pour j allant de ... ... {
...
}
}

Par exemple :
#include <stdio.h>
int main () {
int i;
int j;
for (i=0; i<5; i++){
printf("\nJe suis dans la boucle i, i vaut %d\n"
, ,i);
for (j=3; j>0; j--)
printf("Je suis dans la boucle j, j vaut %d\n"
, ,j);
}
return 0;
}

68

Le C en 20 heures

Exercice n1 toiles
En utilisant une double boucle, crire un programme qui
affiche une toile, passe la ligne, affiche deux toiles,
passe la ligne, affiche trois toiles. . . jusqu cinq toiles
afin dobtenir ceci :
*
**
***
****
*****

5. Et les Shadoks ftrent Nol. . .


Affichage des ramures du sapin
Exercice n2 Sapin
laide dune double boucle, ralisez un cne pour dessiner le haut du sapin sur 10 lignes.
Vous devriez obtenir ceci :
*
***
*****
*******
*********
***********
*************
***************
*****************
*******************

69

Boucles

Instructions :

sur la ligne no 1, afficher 9 espaces puis 1 toile ;


sur la ligne no 2, afficher 8 espaces puis 3 toiles ;
sur la ligne no 3, afficher 7 espaces puis 5 toiles ;
sur la ligne no i, afficher 10-i espaces puis 2*i-1
toiles.

Nous obtenons donc par exemple pour laffichage des


toiles sur la ligne i :
for (j=0; j<((2*i)-1); j++) {
printf ("*");
}

Affichage du tronc
Exercice n3 Tronc
Pour poursuivre le sapin, il nous faut maintenant dessiner le tronc. crivez la suite du programme en dessinant
le tronc laide du caractre @. Vous devriez obtenir ceci
(juste pour le tronc) :
@@@
@@@
@@@

6. Table Ascii
Les codes Ascii (cest--dire les nombres qui reprsentent les caractres
en informatique) vont de 0 255.
Exercice n4 Code Ascii
crivez un programme qui fait afficher, sur des lignes
successives, les codes A SCII avec les caractres qui leur

70

Le C en 20 heures

correspondent (vous pouvez commencer laffichage partir du code 32 et vous arrter au code 127).
Pour faire afficher le caractre associ un code A SCII,
vous utiliserez linstruction ci-dessous (le %3d signifie que
le nombre va tre affich en utilisant un emplacement qui
fait trois caractres de long) :
printf ("%3d : %c", code_ascii,
code_ascii) ;

Par exemple :
int i = 65;
printf("%3d : %c", i, i); // affichera 65 : A

Exercice n5 Un beau tableau


Mme chose, mais avec un joli affichage, par exemple
sous la forme dun tableau sur huit colonnes. . .

7. Corrigs des exercices du chapitre


Corrig de lexercice n1 toiles
#include <stdio.h>
#include <stdlib.h>
int main () {
int i;
int j;
for (i=1; i<=5; i++){
for (j=1; j<=i; j++){
printf ("*");
}
printf ("\n");
}
return 0;
}

Boucles

Nous aurions aussi pu crire :


#include <stdio.h>
#include <stdlib.h>
int main () {
int i;
int j;
for (i=1; i<=5; i++){
for (j=1; j<=i; j++) // pas daccolades ncessaires...
printf("*");
printf("\n");
}
return 0;
}

Corrig de lexercice n2 Sapin


#include <stdio.h>
#include <stdlib.h>
int main () {
int i;
int j;
for (i=1; i<=10; i++) {
for (j=0;j<10-i;j++) // les espaces...
printf(" ");
for (j=0; j<(i*2-1); j++)
printf("*");
printf("\n");
}
return 0;
}

Corrig de lexercice n3 Tronc


Vous ajoutez ceci :
...
for (i=1; i<=3; i++)
printf(" @@@\n");
...

71

72
Corrig de lexercice n4 Code Ascii
#include <stdio.h>
#include <stdlib.h>
int main () {
int i;
int j;
for (i=32; i<128; i++)
printf("%3d %c\n",i,i);
return 0;
}

Corrig de lexercice n5 Un beau tableau


#include <stdio.h>
#include <stdlib.h>
int main () {
int i;
int j;
for (i=4; i<16; i++) {
for(j=0; j<8; j++) {
printf("%3d %c ",i*8+j,i*8+j);
}
printf("\n");
}
return 0;
}

Le C en 20 heures

Boucles

8. retenir
Retenez lquivalence entre les deux programmes suivants :
for (i=1; i<100; i=i+2) {
printf("i vaut:%d",i);
}

i=1;
while (i<100) {
printf("i vaut:%d",i);
i=i+2;
}

73

CHAPITRE

Pointeurs et fonctions

Vous voici arrivs la fin de la programmation basique


(en rfrence au langage BASIC a ). Nous allons de ce
pas nous rapprocher de la puissance du C et de la machine.

a. Beginners All-purpose Symbolic Instruction


Code, cette programmation qui permettait de faire des
petits programmes dans les annes 70/80

1. Objectifs
Dans ce chapitre, nous manipulerons deux notions essentielles du langage C : les pointeurs et les fonctions. . .

75

76

Le C en 20 heures

2. Binaire, octets. . .
Binaire
Lordinateur ne comprend que des 0 et des 1. On dit quil travaille
en base 2 ou binaire (tout simplement parce que ce systme de numration est particulirement adapt la technologie lectronique utilise pour
construire les ordinateurs). Au contraire des machines, les humains prfrent gnralement la base 10, cest dire le systme dcimal.
La table ci-dessous donne lcriture binaire et dcimale de quelques
nombres.
Binaire

Dcimal

0
1
10
11
100
101
110
111
1000
1001
1010

0
1
2
3
4
5
6
7
8
9
10
...
255

...
11111111

TABLE 8.1 - quivalence entre lcriture binaire et lcriture dcimale

Nous voyons dans cette table que le nombre 7 est donc reprsent par
trois symboles 1 qui se suivent : 111.
Chacun de ces 1 et 0 sappelle un bit (contraction
de langlais binary digit : chiffre binaire). Par exemple, le
nombre 1010001 est compos de 7 bits.

77

Pointeurs et fonctions

Un ensemble de huit bits conscutifs (chacun pouvant


prendre la valeur 0 ou 1), sappelle un octet (byte en anglais).

11110000 reprsente un octet


10101010 reprsente un octet
100011101 ne reprsente pas un octet
Compter en base 2, 3, 10 ou 16
Vous avez lhabitude de compter en base 10 (dcimal). Pour cela, vous
utilisez dix chiffres de 0 9.
Pour compter en base 2 (binaire), on utilisera deux chiffres : 0 et 1.
Pour compter en base 3, on utilisera 3 chiffres : 0, 1 et 2.
La base 16 (base hexadcimale) est trs utilise en informatique et notamment en langage assembleur. Mais compter en hexadcimal ncessite
davoir 16 chiffres ! La solution est de complter les chiffres habituels
de la base 10 par des lettres de lalphabet comme le montre le tableau
ci-dessous.
Base 16
0
2
4
6
8
A
C
E

Base 10
0
2
4
6
8
10
12
14

Base 16
1
3
5
7
9
B
D
F

Base 10
1
3
5
7
9
11
13
15

TABLE 8.2 - Base 16 et base 10

78

Le C en 20 heures

Dans la pratique, afin de ne pas faire de confusion entre le nombre 10


(en dcimal) et ce mme nombre en hexadcimal, on prcde ce dernier
par le prfixe 0x :
int i1=10;
int i2=0x10;
printf("i1=%d i2=%d",i1,i2);

Ce programme affichera i1=10 i2=16.

3. Variables : pointeurs et valeurs


Variables et mmoire
Une variable est une zone mmoire disponible pour y ranger des informations. Par exemple, dans le cas dune variable car dclare par char
car ;, la zone mmoire disponible sera de 1 octet (exactement une case
mmoire). En revanche, une variable de type int utilisera au moins 4
octets (la valeur exacte dpend du compilateur), cest dire 4 cases mmoires.
prsent, reprsentons-nous la mmoire comme une unique et longue
rue remplie de maisons. Chaque case mmoire est une maison. Chaque
maison porte un numro, cest ce qui permet de dfinir son adresse postale. Cette adresse est unique pour chaque maison dans la rue. De manire
analogue, pour une case mmoire, on parlera dadresse mmoire.
Par exemple, ce petit programme va afficher ladresse mmoire dune
variable de type caractre.
#include <stdio.h>
int main () {
char c=A;
printf ("c contient %c et est stock %p.\n",c,&c);
return 0;
}

Lexcution nous donne :


c contient A et est stock 0xbf8288a3.

Pointeurs et fonctions

79

Le format %p permet dafficher une adresse en hexadcimal.


Vous pouvez essayer le programme prcdent, mais vous
naurez probablement pas la mme adresse mmoire que
dans lexemple.
Pointeurs
Pour manipuler les adresses mmoire des variables, on utilise des variables dun type spcial : le type pointeur . Une variable de type pointeur est dclare ainsi :
type* variable;

Lespace aprs * peut tre omis ou plac avant, peu importe.


Cest le signe * qui indique que nous avons affaire un pointeur. Lindication qui prcde * renseigne le compilateur sur le type de case pointe,
et comment se comporteront certaines oprations arithmtiques 1 . Si v est
un pointeur vers un int, et que nous dsirons modifier lentier point,
nous crirons :
*v = valeur;

*v peut tre interprt comme contenu de ladresse mmoire pointe


par v .

Lisez le programme suivant ainsi que les explications associes.


#include <stdio.h>
int main () {
char car=C;
char * ptr_car; /* Variable de type pointeur */

1. Ajouter 1 un pointeur sur un int revient ajouter 4 ladresse, alors quajouter 1


un pointeur sur un char revient najouter que 1 ladresse.

80

Le C en 20 heures

printf("Avant, le caractre est : %c\n",car);


ptr_car = &car; /* ptr_car = adresse de car */
*ptr_car = E; /* on modifie le contenu de l
, adresse mmoire */
printf("Aprs, le caractre est : %c\n\n",car);
printf("Cette modification est due :\n");
printf("Adresse de car : %p\n",&car);
printf("Valeur de ptr_car : %p\n",ptr_car);
return 0;
}

Voici ce que cela donne avec notre rue borde de maisons :

ptr_car = &car ;

ptr_car contient ladresse postale de


Monsieur car

*ptr_car = E ;

On entre chez Monsieur car et on y


dpose le caractre E

printf ("%c",car) ;

On va regarder ce quil y a chez


Monsieur car pour lafficher lcran

TABLE 8.3 - Pointeurs et valeurs


On supposera que la variable car est stocke ladresse 0x0100 ; la
variable ptr_car est stocke ladresse 0x0110.
Reprenons les deux premires lignes dexcution du programme :
char car=C;
char* ptr_car;

La mmoire ressemble alors ceci :


Nom de la variable
Contenu mmoire
Adresse mmoire (0x)

car

ptr_car

C
100

?
110

TABLE 8.4 - Stockage de variables (a)

81

Pointeurs et fonctions

Nous mettons ? comme contenu de ptr_car car cette variable na


pas encore t initialise. Sa valeur est donc indtermine.
Le programme se poursuit par :
printf("Avant, le caractre est : %c\n",car);
ptr_car = &car; /* ptr_car = adresse de car */

Il y aura donc affichage lcran de


Avant, le caractre est : C

puis la mmoire sera modifie comme ceci :


Nom de la variable
Contenu mmoire
Adresse mmoire (0x)

car

ptr_car

C
100

100
110

TABLE 8.5 - Stockage de variables (b)

ptr_car nest donc quune variable, elle contient la valeur 100


(ladresse de la variable car).

Finalement, la ligne :
*ptr_car = E; /* on modifie le contenu de ladresse mmoire */

conduit modifier la mmoire comme suit :


Nom de la variable
Contenu mmoire
Adresse mmoire (0x)

car

ptr_car

E
100

100
110

TABLE 8.6 - Stockage de variables (c)

Prenez le temps de comprendre tout ce qui prcde avant de passer la


suite.

82

Le C en 20 heures

Exercice dapplication
Exercice n1 Intelligent
Ralisez un programme quivalent qui change une valeur numrique (int) de 10 35.

Notons que, dans la pratique, lorsque lon dclare un


pointeur, il est plus prudent de linitialiser :
#include <stdio.h>
int main () {
char car=C;
char * ptr_car=NULL;
printf("Avant, le caractre est : %c\n",car);
ptr_car = &car; /* ptr_car = adresse de car */
*ptr_car = E; /* on modifie le contenu de l
, adresse mmoire */
printf("\nAprs le caractre est : %c\n",car);/*
, on a modifi car*/
return 0;
}

La constante NULL vaut 0. Or un programme na pas le droit daccder


ladresse 0. Dans la pratique, crire ptr_car=NULL signifie que ladresse
mmoire pointe est pour linstant invalide.
On peut se demander quoi servent les pointeurs ? ! En effet, ceci a
lair compliqu alors que lon pourrait faire bien plus simple.
Ainsi tout le travail effectu par le programme prcdent se rsume au
code suivant :
#include <stdio.h>
int main () {
char car=C;
printf("Avant, le caractre est : %c\n",car);
car=E;
printf("\nAprs le caractre est : %c\n",car);/*on a modifi car
, */

Pointeurs et fonctions

83

return 0;
}

Nous verrons par la suite que dans certains cas, on ne peut pas se passer des pointeurs, notamment pour certaines manipulations au sein des
fonctions. . .

4. Fonctions
Une fonction est un petit bloc de programme qui limage dune industrie va crer, faire ou modifier quelque chose. Un bloc de programme est
mis sous la forme dune fonction si celui-ci est utilis plusieurs fois dans
un programme ou simplement pour une question de clart. De la mme
manire que nous avions dfini la fonction main, une fonction se dfinit
de la faon suivante :
<type de sortie> <nom de la fonction> (<paramtres dappels>) {
Dclaration des variables internes la fonction
Corps de la fonction
Retour
}

Comme indiqu prcdemment, il ne faut pas mettre les < et >, qui ne sont
l que pour faciliter la lecture.
Voici un exemple de fonction qui renvoie le maximum de deux
nombres :
int maximum (int valeur1, int valeur2) {
int max;
if (valeur1<valeur2)
max=valeur2;
else
max=valeur1;
return max;
}

Le programme complet pourrait tre le suivant :


01. #include <stdio.h>
02.
03. int maximum (int valeur1, int valeur2) {
04. int max;

84

Le C en 20 heures

05. if (valeur1<valeur2)
06.
max=valeur2;
07. else
08.
max=valeur1;
09. return max;
10. }
11.
12. int main () {
13. int i1,i2;
14. printf("entrez 1 valeur:");
15. scanf("%d",&i1);
16. printf("entrez 1 valeur:");
17. scanf("%d",&i2);
18. printf("max des 2 valeurs :%d\n",maximum(i1,i2));
19. return 0;
20. }

Dans la pratique (et en rsumant un peu), quand vous


tapez ./programme pour lancer votre programme,
lordinateur excute la fonction main qui se trouve
la ligne 12. Lordinateur demande donc ensuite dentrer deux valeurs qui seront stockes dans i1 et i2.
Supposons que lutilisateur ait entr les valeurs 111 et
222.
Arriv lexcution de la ligne 18, la machine doit
appeler la fonction printf, et pour cela, chacun des
paramtres de la fonction doit tre valu.
Lexcution passe alors la fonction maximum, ligne
3. ce niveau, on peut comprendre intuitivement
que la variable valeur1 prend la valeur de i1
(cest--dire 111) et la valeur valeur2 prend la valeur de i2 (cest--dire 222), du fait de lappel de
maximum(i1,i2).
La fonction maximum se droule. Aprs avoir pass
les lignes 4 8, la valeur de max sera de 222.
la ligne 9 on sort donc de la fonction maximum
pour revenir lappel fait par printf de la ligne 18.
Tous les paramtres de la fonction ayant t valus
(maximum(i1,i2) a t valu 222), printf est
excut et dans le format, %d est remplac par 222.
Le programme se termine.

Pointeurs et fonctions

85

Type void
Le mot void signifie vide. Le type void est notamment utilis comme
type de sortie pour les fonctions qui ne retournent aucun rsultat (quon
appelle aussi procdures).
Exemple :
/* Fonction affichant un caractre */
void affiche_car (char car) {
printf ("%c",car);
}

Exemple de programme complet :


#include <stdio.h>
/* Fonction affichant un caractre */
void affiche_car (char car) {
printf ("%c",car);
}
int main () {
char c;
int i;
printf("Veuillez entrer un caractre:");
scanf("%c",&c);
for (i=0; i<100; i++)
affiche_car(c);
return 0;
}

Variables globales et locales


Les variables dclares dans les fonctions sont dites locales. Il existe aussi les variables dites globales qui sont dclares en-dehors de toute fonction (y compris le main()).
Les variables globales sont modifiables et accessibles
par toutes les fonctions sans avoir besoin de les passer en
paramtres. Il est de ce fait extrmement dangereux dutiliser des variables globales.

86

Le C en 20 heures

Les variables locales ne sont modifiables et accessibles


que dans la fonction o elles sont dclares. Pour les modifier ou les utiliser dans une autre fonction, il est ncessaire
de les passer en paramtres.
Variables locales
#include <stdio.h>
int carre (int val) {
int v = 0; /* Variable locale */
v = val * val;
return v;
}
int main () {
int val_retour = 0; /* Variable locale */
val_retour = carre (2);
printf("Le carr de 2 est : %d\n", val_retour);
return 0;
}

La variable val_retour de main et la variable v de carre sont toutes


deux des variables locales. Le passage des valeurs se fait par copie. Lors
du return v, on peut imaginer que cest le contenu de v qui est renvoy,
puis stock dans val_retour. La variable v est dtruite lorsquon revient
dans main.
Variables globales
#include <stdio.h>
int val = 0;
int val_retour = 0;
void carre () {
val_retour = val * val;
}
int main () {
val = 2;
carre ();
printf("Le carr de 2 est %d\n", val_retour);

87

Pointeurs et fonctions

return 0;
}

Les variables val et val_retour sont des variables globales. On


constate que le programme devient rapidement illisible et difficile vrifier. . .
Le conseil retenir est de ne pas utiliser de variables globales lorsquil
est possible de sen passer.
Utilisation et modification de donnes dans les fonctions
Lappel dune fonction peut seffectuer laide de paramtres.
Ces paramtres figurent dans les parenthses de la ligne de titre de la
fonction.
Par exemple :
#include <stdio.h>
void affiche_Nombre (int no) {
no=no+1;
printf ("Le nombre no est : %d\n",no);
}
int main (){
int a=12;
affiche_Nombre (a);
printf("Le nombre a est : %d\n",a);
return 0;
}

Dans le cas qui prcde, lors de lappel de fonction, cest le contenu


de a (cest dire 12) qui est envoy dans la fonction, et qui se retrouve
donc stock dans no. Le nombre 12 existe donc en deux exemplaires :
une fois dans la variable a de main et une fois dans la variable no de
affiche_Nombre. Puis on ajoute 1 dans no (mais pas dans a), et on
laffiche. On voit donc apparatre :
Le nombre no est : 13

88

Le C en 20 heures

La procdure se termine alors et la variable no est dtruite. On revient


dans la fonction main qui affiche le contenu de a qui vaut encore 12. . .
On voit donc apparatre :
Le nombre a est : 12

Il faut donc retenir que les paramtres dune fonction sont passs par
valeur et que modifier ces paramtres ne modifie que des copies des variables dorigine.
Pour pouvoir modifier les variables dorigine, il ne faut plus passer les
paramtres par valeur, mais par adresse, en utilisant les pointeurs, comme
cest le cas dans lexemple qui suit.

#include <stdio.h>
void avance_Position (int* pointeur_int) {
*pointeur_int = *pointeur_int + 2;
}
int main () {
int x=0;
printf("Position de dpart : %d\n",x);
avance_Position (&x);
printf("Nouvelle position : %d\n",x);
return 0;
}

Imaginons que pointeur_int se trouve en mmoire ladresse 20 et


x ladresse 10 :

Nom de la variable
Contenu mmoire
Adresse mmoire (0x)

pointeur_int

10

20

TABLE 8.7 - Stockage de variables (d)

89

Pointeurs et fonctions

prsent, droulons le programme principal :


1. int x=0 ; : dclaration et initialisation de x 0
2. Avance_Position (&x) ;

appel

de

la

fonction

Avance_Position (&x) ; avec la valeur 10 (ladresse de


x) en paramtre

Nom de la variable
Contenu mmoire
Adresse mmoire (0x)

x
0
10

pointeur_int
10
20

TABLE 8.8 - Stockage de variables (e)

3. void Avance_Position (int* pointeur_int) : on lance


cette fonction et pointeur_int vaut donc 10
4. *pointeur_int = (*pointeur_int) + 2 ; :
(*pointeur_int) pointe sur la variable x. Lancienne valeur de x
va tre crase par sa nouvelle valeur : 0+2
5. printf("Nouvelle position :%d",x) ; : on se retrouve avec
2 comme valeur.
Nous devons utiliser des pointeurs pour pouvoir modifier certaines variables en dehors de lendroit o elles sont dclares. On dit gnralement
quune fonction nest pas capable de modifier ses arguments. Lusage des
pointeurs devient dans ce cas ncessaire. . . Ainsi, le programme suivant
affichera 2 et non 4.
#include <stdio.h>
void calcule_double (int x){
x=x+x ;
}
int main () {
int i=2;
calcule_double(i);
printf("i vaut prsent :%d",i); /* il vaut toujours 2 !!! */
return 0;
}

90

Le C en 20 heures

Pour rendre le programme fonctionnel en utilisant les pointeurs, il faudrait crire :


#include <stdio.h>
void calcule_double (int * p_i){
*p_i=*p_i+*p_i;
}
int main () {
int i=2;
calcule_double(&i);
printf("i vaut prsent :%d",i);
return 0;
}

Nanmoins, la bonne pratique (celle qui donne les programmes les plus
simples comprendre et les plus faciles dboguer) serait dcrire :
#include <stdio.h>
int calcule_double (int a){
a=a+a;
return a;
}
int main () {
int i=2;
i=calcule_double(i);
printf("i vaut prsent :%d",i);
return 0;
}

Observez attentivement les trois exemples qui prcdent


et soyez sr davoir bien compris pourquoi le premier affiche 2 alors que les deux suivants affichent 4.

Exercice n2 Encore un
Modifiez le programme prcdent afin davoir disposition une fonction qui prend en paramtre un pointeur vers
un entier et modifie lentier point en lui ajoutant 1.

Pointeurs et fonctions

91

Ce type dopration sur les pointeurs est dangereux :


int* x;
*x++;

augmentera ladresse de x (on va chez le voisin) et non sa


valeur. Pour cela il faut crire :
(*x)++;

Ceci est une faute courante, prenez garde . . .


Exercice n3 Mon beau sapin
Reprenez le programme du sapin de Nol du chapitre
prcdent. crivez deux fonctions :
ramure (int clignes) qui dessine la ramure du
sapin sur clignes de hauteur,
tronc (int pos_t) qui dessine le tronc en position pos_t (pos_t blancs avant le tronc).
Puis utilisez ces deux fonctions pour dessiner le sapin
sur n lignes.
Prototypes des fonctions
Dans la pratique, lorsquon souhaite crire un programme qui contient
de nombreuses fonctions, on essaie de prsenter le code de manire propre
et structure. Ainsi, plutt que cette version :
#include <stdio.h>
int max (int x, int y) {
if (x<y)
return y;
else
return x;
}
int min (int x, int y) {
if (x<y)
return x;
else

92

Le C en 20 heures

return y;
}
int main () {
int i1,i2;
i1=123;
i2=1267;
printf("max : %d\n",max(i1,i2));
printf("min : %d\n",min(i1,i2));
return 0;
}

on lui prfrera la version suivante o les prototypes des fonctions disponibles figurent en dbut de code (un peu comme une table des matires) :
#include <stdio.h>
/* PROTOTYPES : */
int max (int x, int y);
int min (int x, int y);
int max (int x, int y) {
if (x<y)
return y;
else
return x;
}
int min (int x, int y) {
if (x<y)
return x;
else
return y;
}
int main () {
int i1,i2;
i1=123;
i2=1267;
printf("max : %d\n",max(i1,i2));
printf("min : %d\n",min(i1,i2));
return 0;
}

Lintrt de faire figurer les prototypes en dbut de programme, en plus


daugmenter la clart du code, sera vu par la suite.

Pointeurs et fonctions

5. Corrigs des exercices du chapitre


Corrig de lexercice n1 Intelligent
#include <stdio.h>
int main () {
int val = 10;
int * ptr_val;
printf ("Avant le nombre est : %d\n",val);
ptr_val = &val;
*ptr_val = 35;
printf ("Aprs le nombre est : %d\n",val);
return 0;
}

Corrig de lexercice n2 Encore un


#include <stdio.h>
void ajoute_un (int* pointeur_int) {
*pointeur_int = *pointeur_int + 1;
}
int main () {
int i=10;
printf ("i=%d\n",i);
ajoute_un (&i);
printf ("i=%d\n",i);
return 0;
}

Corrig de lexercice n3 Mon beau sapin


#include <stdio.h>
#include <stdlib.h>
// Dessin de la ramure du sapin
void ramure (int clignes) {
int i=0, j=0;
for (i=1; i<=clignes; i++) {
for (j=0; j<clignes-i; j++)
printf("*");
for (j=1; j<= (i*2-1); j++)
printf(" ");
printf("\n");
}

93

94

Le C en 20 heures

}
// Dessin du tronc du sapin
void tronc (int pos_t) {
int i=0, j=0;
for (j=1; j<=3; j++) {
for (i=1; i<=pos_t; i++)
printf (" ");
printf ("@@@\n");
}
}
int main () {
int nb_lig = 15;
ramure (nb_lig);
tronc (nb_lig - 2);
return 0;
}

6. retenir
Les diffrentes bases de numration
Il est souhaitable de retenir que :

compter en base 2 (binaire) revient nutiliser que des 0 et des 1 ;


un octet est form de 8 bits : 11110101 ;
la base 10 est celle quon emploie tous les jours ;
en base 16 (base hexadcimale), des lettres sont utilises en plus des
10 chiffres (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F).

Pointeur
Un pointeur est une variable faite pour contenir une adresse mmoire,
souvent ladresse dune autre variable.
Structure dun programme C
Pour finir, voici deux programmes qui reprennent lessentiel de ce qui a
t vu :

Pointeurs et fonctions

#include <stdio.h>
int main () {
int i=100;
int *pointeur_sur_i=NULL;
pointeur_sur_i=&i;
*pointeur_sur_i=200;
printf ("i vaut prsent %d\n",i);
return 0;
}

Le programme prcdent revient stocker 200 dans la variable i.


#include <stdio.h>
/* Prototypage */
void avance_Position (int* x);
void avance_Position (int* x) {
(*x)+=2;
}
int main () {
int i=0;
int x=0;
printf("Position de dpart : %d\n",x);
for (i = 0; i<5; i++) {
avance_Position (&x);
printf ("Nouvelle position : %d\n",x);
}
return 0;
}

95

CHAPITRE

Tableaux et chanes de caractres

Vous tes dornavant capable de dcouper un problme en morceaux de manire le rendre plus simple
et plus facilement transformable en langage C. Par
ailleurs, les pointeurs vous ont permis de toucher de
loctet et le fonctionnement intrinsque de la mmoire.
Pour autant, nous pouvons remarquer que nous avons
laiss jusqualors une structure de ct, et que nous
utilisons tous les jours : les mots ou les phrases. Eh
bien ! les voici.

1. Objectifs
En C, une chane de caractres est quivalente un tableau de caractres.
Ce chapitre introduit ces deux notions (chanes et tableaux) tout en vous
faisant approcher de la gestion de la mmoire.

97

98

Le C en 20 heures

2. Tableaux
Dfinition
Un tableau est un ensemble dlments rangs en mmoire dans des
cases conscutives.
Numro de case
Contenu

0
A

1
B

2
C

3
D

4
E

5
F

6
G

7
H

TABLE 9.1 - Tableau de caractres

Notez que les cases dun tableau sont numrotes partir


de 0 en langage C.
Un tableau peut tre constitu de plusieurs lignes et colonnes. Nous
nutiliserons dans un premier temps que les tableaux une seule ligne.
Dclaration
Un tableau se dclare de la manire suivante :
<type> <nom du tableau> [<taille du tableau>] ;

/* Dclaration dun tableau de 10 caractres */


char tab_char [10];
/* Dclaration dun tableau de 10 entiers */
int tab_int [10];

Utilisation
On accde une case du tableau en mettant le nom du tableau, suivi
dun crochet ouvrant [ puis un numro de case et un crochet fermant :
] . Cela donne, par exemple :

99

Tableaux et chanes de caractres

/* dclarations */
int tab_int[10]; /* tableau de 10 cases (0 9)
dentiers */
char tab_char[10]; /* tableau de 10 cases (0 9)
de caractres */
/* utilisation */
tab_char[3]=C; /* Initialisation de la case 3
(la quatrime) de tab_char */
tab_int[6]=10; /* Initialisation de la case 6
(la septime) de tab_int */
tab_int[7]=tab_int[6] * 2; /* La case 7 (la huitime)
contiendra donc 20 (10*2) */

Noubliez pas que le compilateur ne vrifie pas que vous


utilisez le tableau dans ses limites. Il vous est donc possible dcrire lextrieur de votre tableau, donc chez le
voisin. Cest lun des bugs les plus courants de la programmation en C.

3. Chanes de caractres
Les chanes de caractres sont des tableaux de caractres suivis du 0
(zro ; ne pas confondre avec le caractre O de Oscar par exemple. . . )
qui est considr lui aussi comme un caractre. Une chane scrit donc :
contenu utile de la chane + valeur 0.
Eric scrit dans un tableau de 5 caractres de la faon suivante (lusage du \0 sera expliqu par la suite) :
Caractre
Code A SCII
Case

\0

69

114

105

99

TABLE 9.2 - Chanes de caractres

100

Le C en 20 heures

Dclaration dune chane de caractres


Une chane de caractres se dclare sous la forme dun tableau de caractres de longueur fixe. Attention, comme signal auparavant, si vous
dpassez la longueur de tableau, vous crivez chez le voisin.
Ainsi :
char m_chaine [20] ;

permettra denregistrer des chanes de 19 caractres maximum (20-1 pour


le 0 de fin de chane).
Il est possible de dclarer une chane de caractres sans en spcifier la
longueur de dpart de la faon suivante :
char chaine [] = "Eric" ;

De cette faon, la chane fera exactement la longueur ncessaire pour stocker Eric et le 0 final soit 4+1=5 octets.
Affichage dune chane de caractres
Une chane de caractres saffiche grce la commande printf et le
format %s.
Ainsi :
printf("%s",chaine);

affichera le contenu de chaine.


Longueur dune chane de caractres
La longueur dune chane de caractres sobtient par la fonction strlen
(disponible au travers de la bibliothque string). Le 0 de fin de chane
nest pas compt dans cette longueur.

Tableaux et chanes de caractres

101

#include <stdio.h>
#include <string.h>
int main () {
char ch [] = "toto" ;
printf("La longueur de %s est : %d",ch,strlen(ch));
return 0;
}

Affichera : La longueur de toto est : 4 lcran.


Initialisation dune chane de caractres
Le format %p, que nous avons dj vu, permet laffichage
dun void * (pointeur sur un type void) et va nous servir
par la suite afficher les adresses mmoires. . .
Le programme suivant :
char tab[10];
printf("adresse o commence tab=%p",&tab[0]);

affichera la mme chose que ce programme :


char tab[10];
printf("adresse o commence tab=%p",tab);

On voit donc que tab et &tab[0] sont gaux. En revanche, le programme suivant est incorrect :
char tab[10];
tab="coucou";

En effet, tab dsigne ladresse o dbute le tableau en mmoire. Dans


laffectation tab="coucou" ; le membre de gauche dsigne une adresse
alors que le membre de droite dsigne une chane de caractres ; les deux
ntant pas du mme type, le compilateur C le refuse. . .
Pour initialiser une chane de caractres, il est possible dutiliser la fonction strcpy. Cette fonction nous impose une nouvelle fois dajouter le
fichier den-tte string.h :

102

Le C en 20 heures

#include <stdio.h>
#include <string.h>
int main(void) {
char line[80];
strcpy(line,"un exemple de chaine initialise...");
printf ("%s\n",line);
return 0;
}

Une

recopie

de
la
chane
un exemple de chaine
initialise... caractre par caractre est effectue en dmarrant ladresse o line se trouve stocke en mmoire (le \0 final est
copi lui aussi).
Enfin, si lon souhaite lire une chane directement au clavier, on peut
utiliser la fonction scanf :
#include <stdio.h>
int main(void) {
char line[80];
printf("veuillez entrer votre chaine:");
scanf("%s",line);
/* scanf("%s",&line[0]) ferait la mme chose */
printf("la chaine saisie vaut :%s",line);
return 0;
}

Notons que les chanes de caractres saisies de cette manire ne peuvent comporter ni espaces, ni tabulations.
Exercice
Exercice n1 Affichez une chaine de caractres
Lisez lintgralit de lexercice avant de dmarrer. . .
En utilisant une boucle for, remplissez un tableau de
10 caractres avec les lettres de lalphabet en commenant par A (code A SCII 65) ; le tableau devra donc
contenir ceci :

103

Tableaux et chanes de caractres

Case

9 10

Contenu A B C D E F G H I J 0

TABLE 9.3 - Remplissez un tableau. . .


Faites afficher la chane de caractres ainsi obtenue
Faites afficher chaque caractre du tableau sous la
forme
Caractre n0 : A .
Caractre n1 : B .
...
Il est possible dcrire tab_car [i] = code_ascii ; o
code_ascii est un entier reprsentant le code A SCII du caractre
dsign.
Pour faire afficher un seul caractre, on utilisera la syntaxe suivante :
int pos=0; /* Position dans le tableau */
printf ("Caractre numro %d : %c",pos, tab_car [pos]);

La valeur 0 peut tre assigne un lment de tableau de la faon suivante :


tab_car [la bonne position] = 0;

ou encore (tant donn que le caractre \0 dsigne la mme chose


que 0) par :
tab_car [la bonne position] = \0;

On prfrera gnralement cette dernire solution qui est plus explicite


et montre bien que lon travaille avec des chanes de caractres.
Enfin,
voici
un
exemple
de
programme
qui
permet dinitialiser lgamment une chane de caractres
ABCDEFGHIJKLMNOPQRSTUVWXYZ puis de lafficher
lcran :
#include <stdio.h>
int main () {
int i=0;
int pos_tab=0;
char tab_alpha[27];

104

Le C en 20 heures

for (i=A;i<=Z;i++){
tab_alpha[pos_tab]=i;
pos_tab++;
}
tab_alpha[26]=0;
printf("%s\n",tab_alpha);
return 0;
}

La fonction gets : saisie dune chane de caractres


La fonction gets permet de saisir une chane de caractres valide par
la touche ENTREE . Attention, la touche ENTREE elle-mme nest pas
enregistre dans le tableau de caractres.
Lorsque vous compilez votre programme par :
gcc -o essai essai.c

vous verrez peut tre un avertissement du type :


warning : gets may be dangerous

Si cest le cas, ne vous inquitez pas. Le compilateur


craint simplement qu lexcution, lutilisateur saisisse
une chane de caractres qui soit plus longue que lespace qui a t rserv pour la stocker (80 caractres dans
lexemple ci-dessous), auquel cas des problmes risquent
dapparatre. Lutilisation de gets est donc proscrire
dans un cadre professionnel .
#include <stdio.h>
int main(void) {
char line[81];
/* 81 : taille arbitraire suppose suffisante
Une ligne cran = 80 caractres + 1 lment
pour le \0 de fin de chane */
printf( "Saisissez une chane de caractre :\n" );

Tableaux et chanes de caractres

105

gets( line );
/* La frappe de lutilisateur sera enregistre dans
line, on suppose quil ne frappera pas plus de
80 caractres, sinon les problmes vont commencer */
printf( "\nLa chane saisie est : \n%s\n", line );
return 0;
}

Voici un exemple dexcution :


Saisissez une chane de caractres :
Bonjour !
La chane saisie est :
Bonjour !

Notons quil ny a quun seul passage la ligne (celui affich par la fonction printf).
Passage dune chane de caractres en paramtres
Pour passer un tableau (et donc une chane de caractres) en paramtre
une fonction, nous devons simplement donner ladresse du dbut du
tableau. Les deux fonctions suivantes sont donc quivalentes :
int ma_saisie (char chaine[]) {
/* ...Faire ce quil faut... */
return 0;
}
int main () {
char ma_chaine [30];
ma_saisie (ma_chaine);
return 0;
}

int ma_saisie (char* chaine) {


/* ...Faire ce quil faut... */
return 0;
}
int main () {
char ma_chaine [30];
ma_saisie (ma_chaine);
return 0;
}

106

Le C en 20 heures

En fait, dans la pratique, lcriture suivante :


int ma_saisie (char chaine[]) {

est quivalente celle-ci :


int ma_saisie (char * chaine) {

Nous reviendrons longuement, par la suite, sur cette quasi-quivalence


pointeurs/tableaux. . . ne vous inquitez pas si vous ne comprenez pas tout
pour linstant. . .

4. Quelques fonctions utiles


La fonction strcat
La fonction strcat (<s>, <t>) ajoute la chane de caractres <t>
la fin de <s> (on appelle cela une concatnation).
Le programme suivant affichera la chane Bonjour Paul lcran :
#include <stdio.h>
#include <string.h>
int main () {
char chaine1[20]="Bonjour ";
char chaine2[20]="Paul";
strcat(chaine1,chaine2); /* ajoute chaine2
la fin de chaine1 */
printf("%s\n",chaine1);
return 0;
}

On remarquera quil est important de dimensionner chaine1 une


taille suffisante, sans quoi on pourrait avoir des difficults pour stocker
la chane Bonjour Paul dans chaine1.

Tableaux et chanes de caractres

107

La fonction strncpy
La fonction strncpy (<s>, <t>, <n>) est presque similaire
strcpy mais copie au plus <n> caractres de la chane <t> au dbut
de <s>.
#include <stdio.h>
#include <string.h>
int main () {
char chaine1[20]="Bonjour ";
char chaine2[20]="Edouard";
strncpy(chaine1,chaine2,2); /* recopie 2 caractres de chaine2
ladresse de chaine1 */
printf("%s\n",chaine1);
return 0;
}

. . . affichera :
Ednjour

La fonction strncat
La fonction strncat (<s>, <t>, <n>) ajoute au plus <n> caractres de la chane <t> la fin de <s>.
#include <stdio.h>
#include <string.h>
int main () {
char chaine1[20]="Bonjour ";
char chaine2[20]="Edouard";
strncat(chaine1,chaine2,2); /* ajoute les 2 premiers caractres
de chane2 la fin de chaine1 */
printf("%s\n",chaine1);
return 0;
}

. . . affichera :
Bonjour Ed

108

Le C en 20 heures

La fonction strcmp
La fonction strcmp (<s>, <t>) compare les chanes de caractres
<s> et <t> de manire lexicographique et fournit un rsultat :
nul (0) si <s> est gale <t>
ngatif si <s> prcde <t>.
Par exemple, strcmp("AAAA","BBBB") renverrait -1
positif si <s> suit <t>.
Par exemple, strcmp("BBBB","AAAA") renverrait +1
Les fonctions sprintf et sscanf
Nous terminerons par deux fonctions trs utiles : sprintf et sscanf.
sprintf(<chaine cible>,<chaine de
formatage>,<expr1>,<expr2>,...)

La fonction sprintf renvoie une valeur ngative en cas derreur et le


nombre de caractres stocks dans la chane cible sinon.
Dans lexemple suivant, la fonction va convertir lentier i en chane de
caractres et la stocker dans la variable s. larrive, s contiendra 15 .
char s[200];
int i=15;
int code;
code=sprintf(s,"%d",i);

La fonction sscanf fait le contraire.


char s[]="12.5 12.3 11.6";
float a,b,c;
int code;
code=sscanf(s,"%f%f%f",&a,&b,&c);

Les variables numriques a, b et c contiendront respectivement : 12.5


12.3 et 11.6.
En cas derreur, sscanf renvoie une valeur ngative. Sil ny a pas eu
derreur, cest le nombre de variables affectes qui est renvoy.

Tableaux et chanes de caractres

109

5. Tableaux 2 dimensions
Un tableau 2 dimensions se dclare de la faon suivante :
<type> <nom du tableau> [<taille dimension 1>]
[<taille dimension 2>] ;

Par exemple :
int table[5][5]; /* reprsente un tableau dentiers de 5
lignes et 5 colonnes.*/

Ou bien :
float tab[3][2]= {{ 1.2, -1.3 },
{ 8.5, 12.4 },
{ -123.0, 4.0 }};

Voici prsent un programme qui affiche le contenu dun tableau deux


dimensions :
#include <stdio.h>
int main () {
int tab[5][10];
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<5; i++){
/* ... considrer chaque case */
for (j=0; j<10; j++)
printf("%d ", tab[i][j]);
/* Retour la ligne */
printf("\n");
}
return 0;
}

Si on souhaite initialiser ce tableau avec des valeurs lues au clavier, voici


comment faire :
#include <stdio.h>
int main () {
int tab[5][10];
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<5; i++){
/* ... considrer chaque case */

110

Le C en 20 heures

for (j=0; j<10; j++)


scanf("%d", &tab[i][j]);
/* Retour la ligne */
printf("\n");
}
return 0;
}

Nous aurions pu crire ce programme sous la forme suivante :


#include <stdio.h>
#define LIGNES 5
#define COLONNES 10
int main () {
int tab[LIGNES][COLONNES];
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<LIGNES; i++){
/* Considrer chaque case */
for (j=0; j<COLONNES; j++)
scanf("%d", &tab[i][j]);
/* Retour la ligne */
printf("\n");
}
return 0;
}

Lusage de #define LIGNES 5 demande au compilateur de parcourir


tout le programme et de faire une sorte de chercher-remplacer : chercher la chane LIGNES et remplacer par 5. Il fera ensuite pareil pour
COLONNES.
Il ne faut pas mettre des ; la fin des #define.
#include <stdio.h>
#define LIGNES 5;
#define COLONNES 10;

En effet, les rechercher-remplacer aboutiraient au programme suivant


qui ne se compilerait pas :
int main () {
int tab[5;][10;];/* ne compile pas !!! */
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<5;; i++){/* ne compile pas !!! */
/* ... considrer chaque case */

Tableaux et chanes de caractres

111

for (j=0; j<10;; j++)/* ne compile pas !!! */


scanf("%d", & tab[i][j]);
/* Retour la ligne */
printf("\n");
}
return 0;
}

6. Zro
Nous en avons souvent parl de cette valeur 0 et nous allons y consacrer
un petit paragraphe tellement il peut nous jouer des tours.
Quels sont les zros que nous avons rencontrs ?
Le caractre "0"
Il y a le caractre zro, caractre au code ASCII dfini par la valeur
dcimale 48.
#include <stdio.h>
int main () {
char car=0;
printf ("Code ascii de 0 : %d (0x%x)\n", car, car);
return 0 ;
}

Une fois le code compil puis excut, nous obtenons :


Code ascii de 0 : 48 (0x30)

Fin de chane 0
Zro nous permet aussi de terminer une chane de caractres.
#include <stdio.h>
int main () {
char joke[5];
char moi[5]="eric";
joke[0]=l;

112

Le C en 20 heures

joke[1]=i;
joke[2]=n;
joke[3]=u;
joke[4]=x;
printf ("Variable joke : %s\n", joke);
printf ("Variable moi : %s\n", moi);
printf ("Fin de chane de moi : %d\n", moi [4]);
joke[5]=0;
printf ("Variable joke corrige : %s\n", joke);
return 0 ;
}

Une fois le code compil puis excut, nous obtenons :


Variable joke : linux@?EOT
Variable moi : eric
Fin de chane de moi : 0
Variable joke corrige : linux

Non, @?EOT ne sont pas des erreurs de typographie mais simplement


un exemple de ce qui peut arriver quand on ne termine pas une chane de
caractres avec \0.
Le pointeur NULL
Il y a enfin le NULL qui quivaut ladresse mmoire 0.
#include <stdio.h>
int main () {
char* ptr=NULL;
printf ("Valeur du pointeur ptr : %p %d 0x%x\n", ptr, ptr,
, ptr);
return 0 ;

}
Valeur du pointeur ptr : (nil) 0 0x0

nil, Nil ou NULL : Quelle est la diffrence ? 1


Si vous aimez vous faire du mal, lisez ce qui suit, sinon passez votre
chemin. . .
1. Source : http ://www.spikesoft.ch/ ?p=31

Tableaux et chanes de caractres

113

Si vous utilisez GCC, vous ne verrez probablement jamais la diffrence


entre ces 3 mots cls car ils sont utiliss de manire interchangeable. Si
vous utilisez un autre compilateur, si vous vous demandez ou mme, si
simplement vous vous souciez davoir une criture de code smantiquement correcte, alors continuez lire.
nil est un pointeur dobjet nul, (cast)0, cest le nombre 0 assign
au type cast.
Nil est un pointeur de classe nul, (class) 0, cest le nombre 0 assign au type Class. (C++)
NULL est une valeur constante de pointeur nulle, (void *)0, cest
la dfinition de la macro de prprocesseur C ANSI.
Conclusion sur le zro
Comme nous lavons vu le zro est utilis pour de multiples actions.
Nous devons donc vraiment diffrrencier ces usages. Cest pour cette raison que lors de lutilisation du 0 binaire on crit de manire smantique :
\0.

114

Le C en 20 heures

7. Correction de lexercice

Corrig de lexercice n1 Affichez une chaine de caractres


#include <stdio.h>
#include <stdlib.h>
int main () {
/* 10 caractres + 0 de fin de chane */
char tab [11];
int i=0; /* compteur */
/* Remplissage du tableau avec les caractres */
for (i=0; i<10; i++)
tab[i] = 65 + i; // ou tab[i]=A+i;
/* Ajout du 0 de fin de chaine*/
tab[10] = 0;
/* Affichage de la chane*/
printf("tab : %s\n",tab);
/* Saut dune autre ligne */
printf("\n");
/* Affichage de chacun des caractres */
for (i=0; i<10; i++)
printf("Caractre numero %d: %c\n",i,tab [i]);
return 0;
}

8. retenir
Voici pour finir un petit programme qui reprend lessentiel de ce qui a
t vu. Il permet de lire une chane de caractres (chaine1) au clavier
puis de recopier cette chane dans une autre (chaine2) et de lafficher
lcran :
#include <stdio.h>
#include <string.h>
int main () {
char chaine1[81]; /* 80 caractres + \0 */
char chaine2[81];

Tableaux et chanes de caractres

printf("Veuillez entrer votre chaine de caractres : ");


scanf("%s",chaine1); /* identique scanf("%s",&chaine1[0]); */
strcpy(chaine2,chaine1); /* !!! attention lordre !!! */
printf("chaine2 vaut: %s \n",chaine2);
strcpy(chaine1,""); /* et pas chaine1=""; !!!!!!! */
strcat(chaine1,"Pierre ");
printf("Veuillez entrer votre chaine de caractres");
scanf("%s",chaine2);
strcat(chaine1,chaine2);
strcat(chaine1," Paul Jacques...");
printf("chaine1 vaut: %s \n",chaine1);
return 0;
}

115

CHAPITRE

10

Structures et fichiers

Notre prose est maintenant ralise et les lignes de


codes senvolent. . . nous permettant dafficher, saisir
et modifier des textes de belle envole. quoi servirait
tant de travail, si celui-ci devait svaporer larrt du
programme ? Il nous faut donc apprendre structurer,
sauvegarder, lire et enregistrer ces donnes si difficilement acquises.

1. Les types synonymes


Cette nouvelle notion de type synonyme va nous servir dici peu.
Voyons de quoi il sagit.
Il est possible grce au mot-cl typedef de dfinir un
synonyme pour un type dj existant. Ainsi la dfinition
suivante :
117

118

Le C en 20 heures

typedef int entier ;

dfinit un nouveau type appel entier ayant les mmes


caractristiques que le type prdfini int. Une fois cette
dfinition ralise, nous pouvons utiliser ce nouveau type
pour dfinir des variables et nous pouvons mlanger les
variables de ce type avec des variables entires dans des
expressions.
typedef int entier;
entier e1=23, e2=5, te[7]={1,2,3,4,5,6,7};
int i;
i = e1 + e2;
te[3] = i - 60;

2. Structures
Une structure est un objet compos de plusieurs champs qui sert reprsenter un objet rel ou un concept. Par exemple une voiture peut tre
reprsente par les renseignements suivants : la marque, la couleur, lanne, etc.
Nous pouvons dfinir une structure ainsi :
Solution 1 :
struct nom_de_la_structure {
/* Dfinition de la structure */
} nom_du_type;

Ceci fait, le nouveau type de donnes sera struct


nom_du_type et nous pourrons dclarer une variable
ainsi :
struct nom_du_type nom_variable;

Cependant, la rptition du mot-cl struct est rapidement ennuyeuse. Nous prfrerons donc souvent la syntaxe suivante.
Solution 2 :

119

Structures et fichiers

typedef struct {
/* Dfinition de la structure */
} nom_du_type;

Cette fois-ci, le nouveau type de donnes sappelle


nom_du_type (nous avons cr la structure et en mme
temps nous avons dfini un synonyme avec typedef).

Nous dclarerons une variable ainsi :


nom_du_type nom_variable;

En pratique, cela donne :


#define LONGUEUR 40
struct personne{
char nom [LONGUEUR];
char prenom [LONGUEUR];
int age;
};
struct personne p;

#define LONGUEUR 40
typedef struct {
char nom [LONGUEUR];
char prenom [LONGUEUR];
int age;
} personne;
personne p;

La seconde solution est plus simple et plus lgante


lusage.
Laccs aux lments dune structure, que nous appelons
aussi champ, se fait selon la syntaxe :
nom_de_variable.nom_du_champ

120

Le C en 20 heures

Par exemple :
#include <stdio.h>
typedef struct {
char nom [40];
char prenom [20];
int age;
} personne;
int main () {
personne p;
printf("Veuillez entrer le nom de la personne:");
scanf("%s",p.nom);
printf("Veuillez entrer le prnom de la personne:
, ");
scanf("%s",p.prenom);
printf("Veuillez entrer lge de la personne:");
scanf("%d",&p.age); /* ne pas oublier le & !!! */
printf("Voici les caractristiques de cette
, personne:\n");
printf("nom=%s\n",p.nom);
printf("prenom=%s\n",p.prenom);
printf("age=%d\n",p.age);

return 0;
}

3. Bases sur les fichiers


Tout ce qui est enregistr sur votre disque dur ou presque est un fichier,
et porte un nom.
Il est possible de crer, de lire ou dcrire dans des fichiers. Notez que
certains fichiers peuvent tre protgs en lecture, en criture ou les deux.
Voici un programme que nous allons dtailler :
01. #include <stdio.h>
02. #include <stdlib.h>
03. int main () {
04. FILE *p_fichier; /* pointeur sur fichier*/
05. char nom_fichier[20], nom_personne[20];
06. int i, nbr_enregistrements;
07. /* 1re tape : Cration et remplissage du fichier */

Structures et fichiers

121

08. printf("Quel est le nom du fichier crer ? ");


09. scanf("%s", nom_fichier);
10.
11. /* w: write r: read a: append*/
12. p_fichier = fopen(nom_fichier, "w");
13. if (p_fichier == NULL) {
14.
printf("Erreur de cration du fichier \n");
15.
exit(-1); // Abandonner le programme
16. }
17.
18. printf("Nombre de personnes stocker ? : ");
19. scanf("%d", &nbr_enregistrements);
20.
21. for (i = 0; i<nbr_enregistrements; i++) {
22.
printf("Entrez le nom de la personne : ");
23.
scanf("%s", nom_personne);
24.
fprintf(p_fichier, "%s\n", nom_personne);
25. }
26. fclose(p_fichier);
27.
28. /* 2me tape : Lecture et affichage du fichier */
29. p_fichier = fopen(nom_fichier,"r"); /* read */
30. if (p_fichier == NULL) {
31.
printf("\aErreur douverture sur le fichier \n");
32.
exit(-2); // Abandonner le programme
33. }
34.
35. while (!feof(p_fichier)) {
36.
fscanf(p_fichier, "%s ", nom_personne);
37.
printf("Nom : %s\n", nom_personne);
38. }
39. fclose(p_fichier);
40.
41. return 0;
42. }

Explications :
Ligne 4 : une variable p_fichier est cre ; elle va pointer sur un
type FILE. Sans entrer dans les dtails, le type FILE est un type structure (vu au paragraphe prcdent) qui permet de dcrire un fichier.
Ligne 9 : lutilisateur va saisir une chane au clavier. Cette dernire
sera stocke dans la variable nom_fichier. Supposons pour fixer les
ides que lutilisateur tape au clavier familles.txt. Le fichier qui
sera par la suite cr portera ce nom.
ligne 12 : fopen va crer une sorte de lien entre le fichier du
disque dur qui sintitule familles.txt et la variable p_fichier.
Ainsi dans la suite, vous allez faire des oprations sur la variable
p_fichier et toutes ces oprations seront rpercutes au niveau du

122

Le C en 20 heures

fichier familles.txt. Dans ce cas prcis, les 3 oprations suivantes


peuvent tre ralises :
p_fichier=fopen(nom_fichier, "w"); : si le fichier
familles.txt existe dj, il est purement et simplement cras
puis rinitialis vide. Sil nexiste pas encore, le fichier est cre,
pour linstant il est vide.
p_fichier=fopen(nom_fichier, "r"); : si le fichier
familles.txt existe dj, il est simplement ouvert en lecture
(read). Lordinateur se positionne sur le premier caractre du
fichier. Si le fichier nexiste pas (typiquement, nous nous sommes
tromp de nom), la fonction fopen renvoie alors NULL.
p_fichier=fopen(nom_fichier, "a") ; : si le fichier
familles.txt existe dj, il est simplement ouvert. Ensuite,
lordinateur se positionne sur la fin de ce fichier, prt ajouter
quelque chose aprs la dernire ligne. Nous comprenons mieux le
"a" : append. Si le fichier nexiste pas, il est cr, et il est donc
vide.
Ligne 13 : il est toujours prudent de faire ce test. Le pointeur sera
NULL sil y a eu un problme lors de laccs au fichier (nom incorrect
pour louverture en lecture, accs en criture impossible. . . )
Ligne 15 : sortie catastrophe, le programme sarrte immdiatement.
La valeur -1 est renvoye au systme dexploitation. Il est noter
que lusage de la fonction exit impose dajouter la ligne #include
<stdlib.h>.
Ligne 23 : lordinateur lit au clavier le nom dune personne.
Ligne 24 : en fait, un fprintf nest pas trs diffrent dun printf.
La seule diffrence est quau lieu dtre crite sur lcran, la chane
nom_personne sera crite dans le fichier familles.txt.
Ligne 26 : on referme le fichier pour indiquer au programme C que
lon a fini de travailler sur familles.txt pour linstant. Il faut toujours penser faire cette opration.
Ligne 29 : r-ouverture du fichier, en lecture, cette fois-ci. Si le fopen
se passe bien (ce que nous pouvons supposer !), lordinateur se positionne alors au dbut de la 1re ligne du fichier.

Structures et fichiers

123

Ligne 34 : feof dsigne labrviation de file end of file. Donc


cette ligne se traduit par : tant que lon natteint pas la fin du fichier
dsign par p_fichier. . .
Enfin, voici une autre fonction qui peut se montrer trs utile :
char *fgets(char *ligne, int maxligne, FILE
*p_fichiers)

La fonction fgets lit partir du fichier au maximum maxligne -1 caractres et les stocke dans la chane de caractres ligne.
La lecture sarrte sur \n qui est alors inclus dans la chane. La chane
est complte par \0. La fonction renvoie NULL si la fin de fichier est
atteinte.
Voici un exemple de programme qui va simplement afficher le contenu du fichier essai.txt lcran (lisez-le
puis tudiez la remarque qui le suit) :
#include <stdio.h>
/* Dfinition de constante */
#define maxligne 100
char ligne[maxligne];
FILE *p_fichier;
int main() {
p_fichier=fopen("essai.txt","r");
while (! feof(p_fichier)) {
fgets(ligne,maxligne,p_fichier);
if (! feof(p_fichier))
printf("Jai lu :%s\n",ligne);
}
fclose(p_fichier);
return 0;
}

Le test suivant peut paratre curieux :


if (! feof(p_fichier))
printf("Jai lu :%s\n",ligne);

124

Le C en 20 heures

En fait, ce test est ncessaire du fait que la fonction feof(p_fichier)


renverra vrai si lindicateur de fin de fichier du flux pfichier est positionn, cest--dire sil y a dj eu une lecture infructueuse (par fgets
par exemple).
Ainsi, lorsque le fgets lit la dernire ligne du fichier, un appel, dans
la foule la fonction feof(p_fichier) renverra faux. Ce nest que
si nous refaisons un fgets (qui sera donc infructueux) que l, le test
feof(p_fichier) renverra vrai. Donc finalement, nous voyons bien le
problme : pour la toute dernire ligne, le fgets va chouer et linstruction printf("Jai lu :%s\n",lignes), si elle tait appele, pourrait bien renvoyer nimporte quoi !

4. Fichiers et structures
Voici un exemple qui mle fichiers et structures :
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char nom [40];
char prenom [20];
int age;
} personne;
int main() {
FILE *p_fichier; /* pointeur fichier */
/* Crer et remplir le fichier */
p_fichier = fopen("essai.txt","w");
if (p_fichier == NULL) {
printf("\aImpossible de crer le fichier \n");
exit(-1); // Abandonner le programme
}
personne p;
printf("Veuillez entrer le nom de la personne:");
scanf("%s",p.nom);
printf("Veuillez entrer le prnom de la personne:");
scanf("%s",p.prenom);
printf("Veuillez entrer lge de la personne:");
scanf("%d",&p.age); /* ne pas oublier le & !!! */
fprintf(p_fichier, "%s\n",p.nom);
fprintf(p_fichier, "%s\n",p.prenom);

Structures et fichiers

fprintf(p_fichier, "%d\n",p.age);
fclose(p_fichier);
return 0;
}

125

CHAPITRE

11

Dbogage dun programme

Nous voici au terme de notre premier enseignement, la


programmation na plus de secrets et vos programmes
accdent toutes les donnes que vous souhaitez. Hlas, ces derniers ne font pas toujours ce que vous dsirez, bien sr cest la faute lordinateur !

1. Objectif
Lobjectif de ce chapitre est de vous aider traquer les bugs et les
corriger ;-). . .

2. Deux types derreurs


On fera la diffrence entre les deux types derreurs suivants :
erreur de compilation ou ddition de liens
erreur dexcution
127

128

Le C en 20 heures

Erreur la compilation
Une erreur de syntaxe lors de la compilation est provoque la suite de
lexcution de la commande gcc -o :
Vous tapez gcc -o essai essai.c pour le programme suivant :
#include <stdio.h>
int main () {
floatt f ;
return 0 ;
}

Le compilateur vous renvoie un message du type :


essai.c : 3 : floatt undeclared (first
use in this function)

Une erreur ddition de liens survient lorsque, par exemple, on utilise


une fonction dune bibliothque spare, sans penser joindre la bibliothque lors de la cration de lexcutable.
Vous tapez gcc -o essai essai.c pour le programme suivant :
#include <stdio.h>
#include "malibrairie.h"
int main () {
int valeur ;
valeur = carre (3);
return 0 ;
}

Le compilateur vous renvoie un message du type :


toto.c:(.text+0x11): undefined reference to
carre
collect2: ld returned 1 exit status

Dbogage dun programme

129

Erreur dexcution
Une erreur dexcution est provoque lors de lexcution du programme, cest dire lorsque la compilation et ldition de liens se sont
bien ralises mais quil y a un problme lorsque lon teste lexcutable.

#include <stdio.h>
int main () {
float f=1/0;
return 0 ;
}

La compilation ne pose pas de problmes. Lors de lexcution du programme (./essai), lordinateur affiche un
message du type : Floating point exception. En effet, une division par zro est interdite.

3. Un phnomne surprenant. . .

Saisissez le programme suivant, compilez-le et enfin


testez-le. . .
#include <stdio.h>
int main () {
printf ("Je suis ici") ;
while (1)
;
return 0 ;
}

Rien ne saffiche ? ! En fait, pour des raisons doptimisation systme,


un printf() est conserv dans un buffer de ligne jusqu rception dun
\n ou excution dun fflush().

130

Le C en 20 heures

Essayez :
#include <stdio.h>
int main () {
printf ("Je suis ici\n") ;
while (1)
;
return 0 ;
}

4. La chasse aux bugs. . .


La dmarche pour corriger un bug 1 est la suivante.
Localiser et reproduire le bug
Prenons lexemple du programme suivant qui naffiche rien pour la raison prcdemment voque (le problme de l \n) :
#include <stdio.h>
int main () {
int i ;
for (i=0 ; i<100 ; i++)
printf("i=%d",i) ;
while (1)
;
return 0 ;
}

Le rajout de mouchards (un mouchard est ici simplement un


printf) dans le programme nous permettra de localiser le bug.
#include <stdio.h>
int main () {
int i ;
printf("1) Je suis ici\n") ; /* 1 er mouchard */
for (i=0 ; i<100 ; i++)
printf("i=%d",i) ;
printf("2) Je suis ici\n") ; /* 2 eme mouchard */

1. Un bug est un dfaut dans un programme produisant des anomalies de fonctionnement.

Dbogage dun programme

131

while (1)
;
return 0 ;
}

Corriger le bug
La phase prcdente vous a permis de savoir do venait le problme,
en revanche, celle-ci ne la pas corrig.
Prenons lexemple suivant :
#include <stdio.h>
int main () {
int i,j;
i=0;
j=0;
printf("1) Je suis ici\n") ;
if ((i==0) && (i=j)) {
printf("2) Je suis ici\n");
...
}
return 0 ;
}

Vous tes persuad que lordinateur devrait afficher 2) Je suis ici


or il nen est rien ? ! Vous en tes invoquer un bug dans le compilateur ? ! Lordinateur ne fait que ce que vous lui demandez. Vous allez
donc affiner votre traabilit en plaant des mouchards pour voir prcisment le contenu des variables.
#include <stdio.h>
int main () {
int i,j;
i=0;
j=0;
printf("1-) Je suis ici\n");
printf("i=%d j=%d\n",i,j);
if ((i==0) && (i=j)) {
/* (i=j) => i vaudra 0, et 0 est identique faux !!! */
printf("2-) Je suis ici\n");
...
}
return 0 ;
}

132

Le C en 20 heures

5. Bonne chasse. . .

Exercice n1 Erreur moyenne


Copiez le programme suivant, puis corrigez-le.
#include <stdio.h>
int main () {
int i, somme;
for (i=0 ; i<10 ; i++);
printf ("i=%d\n",i) ;
somme += i;
printf("La moyenne vaut:%d",somme/i) ;
return 0 ;
}

Ce programme est cens afficher ceci lcran :


i=0
i=1
...
i=9
La moyenne vaut: 4.50000

6. Erreurs dexcution : les erreurs de segmentation. . .


Ce type derreur apparat lorsque votre programme accde une zone de
la mmoire qui lui est interdite : vous tes sur un segment de la mmoire 1
sur lequel vous navez pas le droit de travailler.
Le programme suivant peut provoquer de telles erreurs :
#include <stdio.h>
int main () {
int i=0;
scanf("%d",i);
return 0 ;
}

1. Segment de mmoire (daprs Wikipdia) : espace dadressage indpendant dfini par


deux valeurs : ladresse o il commence et sa taille.

Dbogage dun programme

133

Soit vous voyez tout de suite lerreur. . . soit vous ajoutez des mouchards :
#include <stdio.h>
int main () {
int i=0;
printf("1-) Je suis ici\n") ;
scanf("%d",i);
printf("2-) Je suis ici\n");
return 0 ;
}

Ces mouchards vous permettront rapidement de voir que le problme provient de la ligne scanf("%d",i) car seul le message 1) Je suis
ici saffiche et pas le message 2) Je suis ici 1 .
Le problme vient donc de i qui vaut 0. . . le scanf va tenter de stocker
ce que vous venez dentrer au clavier ladresse mmoire 0 (NULL) !
Cette dernire est rserve au systme dexploitation, do lerreur. . .
Il en va de mme du programme ci-dessous qui pourrait poser des problmes du fait que lon risque de sortir des bornes du tableau.
#include <stdio.h>
#define TAILLE 10
int main () {
int tab[TAILLE];
tab[TAILLE+10]=100;
return 0 ;
}

Ce programme peut planter ou non. Ce type derreur conduit le plus


souvent des bugs alatoires, les plus difficiles corriger !

1. Noubliez pas les \n dans vos printf pour la raison voque plus haut. . .

134

Le C en 20 heures

Le debugger ddd
Ce debugger est trs efficace pour trouver les erreurs de segmentation.
Copiez, compilez, excutez le programme suivant.
#include <stdio.h>
#include <string.h>
int main () {
int * p=NULL;
*p=123;
printf("\n je suis ici...\n");
}

Le programme ne fonctionne pas et provoque une erreur de segmentation. Nous allons donc le dbugger avec ddd.
Faites :
gcc -o essai essai.c -g
ddd essai
fermez les petites fentres parasites qui apparaissent au lancement de ddd
cliquez sur le bouton run (il faut parfois chercher un
peu dans les menus). . .

Loption -g de la ligne de compilation permet de compiler le programme en y incluant les informations supplmentaires utiles au dbogage.

Dbogage dun programme

135

FIGURE 11.1 - Dbogage dun programme

Lorsque vous faites p=NULL ;, vous placez donc la valeur 0 dans cette
variable. Ceci signifie que p pointe sur un lment mmoire qui nest pas
accessible par votre programme en criture. Or, vous faites *p=123 ; qui
revient vouloir crire la valeur 123 ladresse NULL.
Le debugger ddd vous indique alors quelle ligne a provoqu lerreur de
segmentation. Sur un programme de plusieurs centaines, voire plusieurs
milliers de lignes, cette aide est particulirement apprciable.
Moralit : en cas derreur de segmentation, tentez tout
dabord un ddd. . .

136

Le C en 20 heures

Une autre chasse. . .


Exercice n2 Verlan
Soit le programme suivant qui doit afficher la chane de
caractres chaine lenvers, et ce, caractre par caractre :
#include <stdio.h>
#include <string.h>
int main () {
int i;
char chaine[]="! uggubd tse emmargorp el";
for (i=strlen(chaine); i =! 0; i--)
printf("%s",chaine[i]) ;
return 0 ;
}

Corrigez le programme prcdent.


Dernire sournoiserie. . .
Testez le programme suivant :
#include <stdio.h>
int main () {
int i;
int i1,i2 ;
char c1,c2;
printf("1) Entrez un nombre: ");
scanf("%d",&i1);
printf("2) Entrez un nombre: ");
scanf("%d",&i2);
printf("1) Entrez une lettre: ");
scanf("%c",&c1);
printf("2) Entrez une lettre: ");
scanf("%c",&c2);
printf("1) Jai rcupr lettre 1:%d\n",(int)c1);
// renverra 10 c. .d le code ascii de \n
printf("2) Jai rcupr lettre 2:%d\n",(int)c2);
}

Dbogage dun programme

137

On voit que l\n subsiste du premier caractre et pollue la variable c2.


En effet, quand vous faites un :
scanf("%c",&c1);

vous demandez lordinateur de lire un unique caractre. Du coup l\n


restera de ct (pour linstant).
Une solution pour remdier ce travers consiste utiliser systmatiquement la fonction gets(chaine) la place de chaque scanf de la faon
suivante.
/* ===== Version non corrige ===== */
int i;
char c;
printf("Entrez un caractre:");
scanf("%c",&c);
printf("Entrez un chiffre:");
scanf("%d",&i);

/* ===== Version corrige ===== */


int i;
char c;
char chaine[100];
printf("Entrez un caractre:");
gets(chaine);
c=chaine[0];
printf("Entrez un chiffre:");
gets(chaine);
sscanf(chaine,"%d",&i) ;

Cette solution nest en fait pas viable dun point de vue


scurit du code (vous obtenez dailleurs peut-tre un warning). En effet que se passera-t-il quand, au lieu de saisir un caractre, vous en saisirez 200 ? Le tampon de 100
caractres sera dpass et un problme apparatra. Nous
naborderons pas ici le problme de la saisie scurise,
mais soyez tout de mme conscient(e) du problme potentiel. . .

138

Le C en 20 heures

7. Solutions
Corrig de lexercice n1 Erreur moyenne
#include <stdio.h>
int main () {
int i, somme=0;
for (i=0 ; i<10 ; i++) {
printf("i=%d\n",i) ;
somme = somme + i;
}
printf("La moyenne vaut:%f",(float) somme/i) ;
return 0 ;
}

Corrig de lexercice n2 Verlan


#include <stdio.h>
#include <string.h>
int main () {
int i;
char chaine[]="! uggubd tse emmargorp el";
for (i=strlen(chaine); i != 0; i--)
printf("%c",chaine[i]) ;
return 0 ;
}

8. retenir
On retiendra que pour trouver un bug, la mthode est toujours la mme :
1. on tente de le reproduire tous les coups et on note la squence,
2. on cherche isoler le plus prcisment possible lendroit o le problme est apparu en injectant des mouchards (trace),
3. dans le cas dune erreur de segmentation, on tente dutiliser un debugger.

Dbogage dun programme

139

Le debugger ddd
Pour utiliser ddd :
Compilez le programme avec loption -g selon : gcc -o
programme programme.c -g

Excutez : ddd programme

CHAPITRE

12

Complments

Malgr tous les enseignements et un programme fonctionnel, il nous est encore ncessaire dapprendre,
car souvent les problmes sont plus complexes que
simples.

1. Objectif
Ce chapitre vous propose daborder quelques notions plus complexes
quil est ncessaire de comprendre mais o seule la pratique sera votre
vritable allie.

2. Conversions de type
Pour convertir un type en un autre, on ralise ce que lon appelle un
cast . Imaginons que nous souhaitions convertir un nombre du type
float en un entier du type int. Voici comment procder :
141

142

Le C en 20 heures

int main () {
float f;
int i;
f=3.1415;
i=(int) f; /* rsultat dans i : 3 */
/* donc la partie dcimale est perdue... */
return 0 ;
}

Sans la conversion explicite, le programme aurait donn


la mme chose : un float est converti en int avant dtre
stock dans une variable de type int (le compilateur pourrait nanmoins mettre un avertissement).

3. Usage trs utile des conversions de type


Considrons le programme suivant 1 :
int main () {
printf("Rsultat : %f",3/4);
return 0 ;
}

Celui-ci affichera Rsultat : 0.0 ! ! !


En effet, loprateur / ralise une division entire de lentier 3 par
lentier 4 ce qui vaut 0 (les deux oprandes tant entires, le rsultat est
converti automatiquement en entier).
Il existe au moins deux solutions pour remdier ce problme :
1. crire 3.0 la place de 3 ce qui va forcer le programme faire une
division en flottants :
#include <stdio.h>
int main () {
printf("Rsultat : %f",3.0/4);
return 0 ;
}

1. L aussi, vous pourriez avoir un avertissement du compilateur.

Complments

143

2. convertir le nombre 3 en un flottant :


#include <stdio.h>
int main () {
printf("Rsultat : %f",(float)3/4);
return 0 ;
}

La division se fera alors en flottants 1 .


On notera que la solution suivante ne fonctionnerait pas :
#include <stdio.h>
int main () {
printf("Rsultat : %f",(float)(3/4));
return 0 ;
}

En effet, nous ralisons tout dabord la division de 3 par


4 (avec pour rsultat lentier 0), puis la conversion de ce
dernier en flottant.
Le rsultat est donc :
Rsultat : 0.0

4. Fonction putchar
Le programme suivant :
#include <stdio.h>
int main () {
char c=A;
putchar(c);
return 0 ;
}

fait la mme chose que :


1. Loprateur de cast (<type>) (o <type> est un type quelconque) est prioritaire sur les oprateurs binaires. Lexpression (float)3/4 est donc value comme
((float)3)/4 et non comme (float)(3/4).

144

Le C en 20 heures

#include <stdio.h>
int main () {
char c=A;
printf("%c",c);
return 0 ;
}

Nous constatons que putchar affiche un unique caractre lcran.

5. Allocation dynamique de mmoire


Fonctions malloc et sizeof
La fonction malloc, dclare dans stdlib.h permet de rserver un
bloc mmoire au cours de lexcution dun programme.
Ainsi, malloc(N) fournit ladresse dun bloc en mmoire de N octets
libres ou la valeur NULL (cest dire 0) sil ny a pas assez de mmoire.
Supposons que nous ayons besoin dun bloc en mmoire pour un texte
de moins de 4000 caractres.
1. nous crons un pointeur tab vers un caractre (char *tab).
2. nous excutons linstruction : tab = malloc(4000) ; qui fournit
ladresse dun bloc de 4000 octets libres et laffecte tab.
Sil ny a plus assez de mmoire, cest la valeur NULL (cest dire 0)
qui est affecte tab.
Si nous souhaitons rserver de la mmoire pour des donnes dun type
dont la grandeur varie dune machine / dun systme lautre, nous aurons besoin de la grandeur effective dune donne de ce type. Il faut alors
utiliser loprateur sizeof afin de prserver la portabilit du programme :
sizeof(<var>) : fournit la taille, en octets, de la variable <var>
sizeof(<constante>) : fournit la taille de la constante <const>
sizeof(<type>) : fournit la taille pour un objet du type <type>

145

Complments

Exemple :
Soit la dclaration des variables suivantes :
short tab1[10];
char tab2[5][10];

Instructions

Valeurs

Remarques

retournes
sizeof(tab1)

20

sizeof(tab2)

50

sizeof(double)

Gnralement !

sizeof("bonjour")

Pensez au zro final des


chanes

sizeof(float)

Gnralement !

TABLE 12.1 - Dclaration de variables

Si nous souhaitons rserver de la mmoire pour x valeurs


de type int, la valeur de x tant lue au clavier :
int x;
int *pNum;
printf("Introduire le nombre de valeurs :");
scanf("%d", &x);
pNum = malloc(x*sizeof(int));

Certains compilateurs imposent dcrire


pNum = (int *) malloc(x*sizeof(int));

En effet, tant donn que malloc renvoie un pointeur quelconque


(void *), nous devons convertir cette adresse par un cast en un pointeur,
dfini ici sur un entier, do lusage de (int *).
Voici dautres exemples :

146

Le C en 20 heures

char * pointeur_sur_chaine;
char * pointeur_sur_float;
pointeur_sur_chaine = (char *) malloc(1000*sizeof(char));
pointeur_sur_float = (float *) malloc(10000*sizeof(float));

Le programme suivant lit 20 phrases au clavier, recherche des blocs de


mmoire libres assez grands pour la mmorisation et passe les adresses
aux composantes du tableau phrases[]. Sil ny a pas assez de mmoire
pour une chane, le programme affiche un message derreur et sinterrompt avec le code derreur -1. Nous devons utiliser une variable daide
phrase comme zone intermdiaire (non dynamique). Pour cette raison,
la longueur maximale dune phrase est fixe 100 caractres.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LONGUEUR_MAX_PHRASE 100 /* longueur max. dune phrase */
#define NOMBRE_MAX_PHRASES 20 /* nombre max. de phrases */
int main() {
char phrase[LONGUEUR_MAX_PHRASE];
char *phrases[NOMBRE_MAX_PHRASES];
int i;
for (i=0; i<NOMBRE_MAX_PHRASES; i++) {
printf("Entrez une phrase SVP...");
gets(phrase);
/* Rservation de la mmoire */
phrases[i] = (char *) malloc(strlen(phrase)+1);
/* S il y a assez de mmoire, ... */
if (phrases[i]!=NULL) {
/* copier la phrase ladresse renvoye par malloc, ... */
strcpy(phrases[i],phrase);
}
else {
/* sinon faire une sortie "catastrophe" */
printf("Attention! Plus assez place en mmoire !!! \n");
exit(-1);
}
}
return 0;
}

Complments

147

la fin de ce programme, nous devrions librer la mmoire comme


nous le verrons dans les paragraphes suivants.
Fonction free
Lorsque nous navons plus besoin dun bloc de mmoire que nous avons
rserv laide de malloc, nous pouvons / devons le librer laide de la
fonction free dclare dans <stdlib.h>.
Lappel free(<Pointeur>) libre le bloc de mmoire dsign par
<Pointeur>. Lappel free na pas deffet si le pointeur pass en paramtre possde la valeur zro.
La fonction free peut aboutir un dsastre si on essaie de librer de la mmoire qui na pas t alloue
par malloc.
La fonction free ne change pas le contenu du pointeur ; il est conseill daffecter la valeur NULL au pointeur immdiatement aprs avoir libr le bloc de mmoire qui y tait attach.
Si nous ne librons pas explicitement la mmoire
laide de free, celle-ci peut tre libre automatiquement la fin du programme (mais cette faon de faire
est viter). Ce processus de libration de la mmoire
dpend du systme dexploitation utilis. Une telle
fuite de mmoire sappelle Memory Leak .
Par exemple :
char * pointeur_sur_chaine;
pointeur_sur_chaine = (char *) malloc(1000*sizeof(char));
...
/* prsent, nous navons plus besoin de cette zone mmoire : */
free(pointeur_sur_chaine);
pointeur_sur_chaine=NULL;

148

Le C en 20 heures

6. Avez-vous bien compris ceci ?


Considrons le programme suivant :
1. #include <stdio.h>
2.
3. int main() {
4.
int i;
5.
int * p;
6.
printf ("%d\n",i);
7.
*p = 12;
8.
return 0;
9. }

la ligne 6, le programme affichera nimporte quoi car la variable i


na pas t initialise.
La ligne 7 a de fortes chances de faire bugger le programme !
En effet p na pas t initialis. En particulier si on faisait un
printf("%p",p), on pourrait constater que sa valeur est indfinie.
En effet, p, nest quun pointeur, cest--dire une variable contenant
une valeur. Donc p dsigne une adresse quelconque qui peut tre celle
dune autre variable. Ainsi, le bug sera d au fait que p pointe nimporte o et que vous essayez dinitialiser ce nimporte o (qui ne vous
appartient peut-tre pas).

7. Sur lutilit des pointeurs


Modifier une variable dans une fonction
Vous savez quune fonction nest pas capable de modifier ses arguments :
#include <stdio.h>
void calcule_double (int x){
x=x+x ;
}
main () {
int i=2;
calcule_double(i);
printf("i vaut prsent :%d",i);/* i vaut toujours 2 !!! */
}

149

Complments

La solution consiste donc faire (voir le droul plus loin).


#include <stdio.h>
void calcule_double (int * pointeur_int){
* pointeur_int = (* pointeur_int) + (* pointeur_int) ;
}
main () {
int i=2;
calcule_double(&i);
printf("i vaut prsent :%d",i); /* i vaut bien 4 !!! */
}

Imaginons que pointeur_int se trouve en mmoire log ladresse


20 et i ladresse 10 :

FIGURE 12.1 - Adresse mmoire (a)

prsent, droulons le programme principal :


1. int i=2 : dclaration et initialisation de i 2
2. calcule_double(&i) : appel de la fonction calcule_double, avec
la valeur 10 (ladresse de i)
3. void calcule_double (int * pointeur_int) : on lance
cette fonction et pointeur_int vaut donc 10

FIGURE 12.2 - Adresse mmoire (b)


4. * pointeur_int = (* pointeur_int)+(*
pointeur_int) : (* pointeur_int) pointe sur i. Lancienne
valeur de i va tre crase par la nouvelle valeur : 2+2
5. printf("i vaut prsent : %d", i) : on se retrouve avec
4 comme valeur de i.

150

Le C en 20 heures

Saisie dun nombre dans une fonction


tudions le programme suivant dont le but est simplement de modifier
la valeur de n :
#include <stdio.h>
void saisie (int *pointeur);
int main() {
int n;
saisie(&n);
printf("n vaut : %d",n);
return 0;
}
void saisie (int *pointeur) {
printf("Entrez un nombre :");
scanf ("%d",pointeur);
}

Imaginons que pointeur se trouve en mmoire log ladresse 100 et


n ladresse 1000 :

FIGURE 12.3 - Adresse mmoire (c)

prsent, droulons le programme :


1. int n : dclaration de n. Il vaut nimporte quoi vu quil na pas
t initialis !
2. saisie(&n) : appel de la fonction saisie, avec la valeur 1000
(ladresse de n)
3. void saisie (int *pointeur) : pointeur vaut donc 1000
4. printf("Entrez un nombre :")
5. scanf("%d",pointeur) : la fonction scanf va stocker ce que
lutilisateur tape au clavier partir de ladresse mmoire 1000
(imaginons que lon entre la valeur 42).

Complments

151

6. retour dans main, la valeur entre (42) a t stocke ladresse


1000, donc dans n. Le programme affichera n vaut : 42

8. Un mot sur les warnings


Lorsque vous compilez, vous pouvez obtenir des erreurs, des warnings
(avertissements) ou parfois les deux.
Les erreurs, vous connaissez : lorsque crivez par exemple floatt au
lieu de float, cest une erreur.
Sil y a des erreurs la compilation (gcc -o essai essai.c), le compilateur ne gnrera pas de fichier excutable (fichier essai). En revanche, sil ny a que des warnings, le fichier essai sera cr, mais le
compilateur nous alerte au sujet dune erreur possible.
Il est important de comprendre quen langage C, un warning quivaut
une erreur larve . Ainsi, vous pouvez effectivement excuter votre programme et ce malgr les warnings mais le problme est que vous risquez
de le payer par la suite.
Exemple
Observons le programme suivant :
#include <stdio.h>
#include <stdlib.h>
void affiche_matrice(int matrice[9][9]) {
int i,j;
for(i=0; i<=8; i++) {
for(j=0; j<=8; j++)
printf("|%d", matrice[i][j]);
printf("|\n");
}
}
int main() {
int i=0,j=0;
int matrice[9][9];
int valeur=0;
for ( i=0; i<9; i++)
for (j=0; j<9; j++)

152

Le C en 20 heures

{
matrice [i][j]=valeur;
valeur++;
}
affiche_matrice(matrice[3][3]);
}

La dernire ligne affiche_matrice(matrice[9][9]) vous renverra un warning. . . Pourquoi ?


Cette ligne ne passe pas tout le tableau matrice la fonction
affiche_matrice, mais uniquement la case [3][3].
La solution consisterait donc crire :
affiche_matrice(matrice);

En conclusion, vous devez considrer tous les warnings comme des erreurs et les liminer (cela ne concerne cependant pas les warnings provenant de lusage de gets mme si cette considration dpend de lusage
qui en est fait au sein de votre programme).

9. C obscur
Un peu dhistoire 1
LInternational Obfuscated C Code Contest (en franais : Concours
international de code C obscur) est un concours de programmation organis chaque anne depuis 1984 ( lexception de 1997, 1999, 2002 et
2003).
Lorigine de ce concours vient dune discussion entre dveloppeurs sur le
code abominable dont ils devaient faire la maintenance dans leur travail
quotidien.
Cest, ce que vous risquez de rencontrer dans votre vie de dveloppeur.
Pour plus dinformations sur le sujet : http:// www.de.ioccc.org.
1. http://fr.wikipedia.org/wiki/International_Obfuscated_C_Code_Contest.

Complments

153

Un petit exemple
Le premier lment dobfusctation est de constater que le C ne tient pas
compte des espacements comme vous pouvez le constater ci-dessous !
%
typedef unsigned char t;t*F="%c",l[]="|\\/=_ \n](.\0(),*(.(=(}*.)[[*.",N=\n,*
r;typedef(*H)();extern H Ar;Q(a){return(a|-a)>>31;}H S(c,a){return(H)(a&~c|(int
)Ar&c);}extern t*ist;V(t*u){*u^=*u&2^(*u>>7)*185;}Z(t*u,t n){*u-=n;}e(t c,H h){
R(h,Q(*
r^c));}
I(){r=l
+7-4*Q(
getchar
()^*l);
}R(H h,
int
c){Ar=S
(c,h);main()
;}P(){r
++;}z()
{
O(&N);}
O(t*c){
printf(
F,+*c);
}T(){r=
"This is not a function\n"
;}w(U){
U=Z(r,8
);
r-=~Q(*
r/8-4);
return 0;
}M(){r=
ist-68;
}
h(){t G
=r[1]-r
[2]^*r;
G^=30;V
(&G);e(
0,(O(&G
),P(P(*
r++)),z));}g(){M();R(h,0);}f(){P(O(r));e(f,g);}p(){P();e(a,f);}d(){P(O(r));
e(n,p);}c(u){u=r[-2];T(Ar=d);R(f,Q(u^"));}n(){e(w(O(l+*r%8)),c);}a(){I();R(
n,0);}main(){S(Q(Ar),a)();}H
Ar;t*ist="Rene Magritte"-(1898-1967);

Plusieurs commandes sur une ligne


Le ; est utilis pour terminer une commande, il peut donc tre utilis
pour mettre plusieurs commandes sur une ligne.
#include <stdio.h>
int main ()
{
printf("Bonjour");printf("Bien plus lisible\n");printf("Nest ce
, pas\n");
return 0;
}

Loprateur ; nest pas le seul pouvoir nous permettre le raliser ce


genre de choses.
En effet, loprateur , est un oprateur binaire dont le fonctionnement est
le suivant :
1. il value le premier oprateur et en ignore le rsultat
2. il value le second oprateur
3. il retourne le rsultat de ce qui a t calcul prcdemment ainsi
que son type.

154

Le C en 20 heures

#include <stdio.h>
int main ()
{
printf("Bonjour "),printf("Bien plus lisible\n"),printf("Nest
, ce pas\n");
int a=1,b=2,c=0;c=a+=2,a+b;
printf("a: %d b: %d c: %d\n",a,b,c);
return 0;
}

$ ./monprog
Bonjour Bien plus lisible
Nest ce pas
a: 3 b: 2 c: 3

Hexadcimal
Le langage C est proche de la machine, donc autant dire que cela ne le
drange pas si nous lui parlons en hexadcimal.
#include <stdio.h>
int main ()
{
printf("\x42\x6f\x6e\x6a\x6f\x75\x72\n");
return 0;
}

$ ./monprog
Bonjour

Oprateur ternaire
Le langage C possde un oprateur ternaire un peu exotique qui peut
tre utilis comme alternative if - else et qui a lavantage de pouvoir tre intgr dans une expression.
Lexpression suivante : <expr1> ? <expr2> : <expr3> est traduite
comme ceci :
Si <expr1> est non nulle, alors la valeur de <expr2> est fournie
comme rsultat.
Sinon, cest la valeur de <expr3> qui est fournie comme rsultat.

155

Complments

La suite dinstructions :
if (a>b)
maximum=a;
else
maximum=b;

peut donc tre remplace par :


maximum = (a > b) ? a : b;

Loprateur ternaire peut nuire la lisibilit dun programme, mais utilis avec prcaution, il fournit des solutions lgantes et concises.
Par exemple :
printf("Vous avez %i carte%c \n", n, (n==1) ? :
, s);

Pour n==1 on obtiendra donc Vous avez 1 carte.


Pour n != 1 on obtiendra donc Vous avez n cartes.
Notez le s la fin de carte.
Utilisation des oprateurs logiques
Il existe une autre forme raccourcie pour faire des tests dans le langage C : && et ||.
Le symbole && est lquivalent de si vrai et le symbole || lquivalent
de si faux. Cette forme de test est trs courante.
#include <stdio.h>
int main ()
{
int a=3;
( a >=2 ) && printf ("Test
( a <=2 ) || printf ("Test
( a <=2 ) && printf ("Test
|| printf ("Test 3 - a <
return 0;
}

1 - a >= 2\n");
2 - a < 2\n");
3 - a >= 2\n")
2\n");

CHAPITRE

13

Quelques exemples de programmes

Rien ne saurait mieux faire apprendre que de pratiquer,


donc pratiquons tout en tant guid. . .

1. Objectifs
Le but de ce chapitre est de vous montrer quelques problmes accompagns de leur solution.
On apprend beaucoup de choses en lisant des programmes finis ;-)

2. Convertisseur francs/euros
Voici un programme qui produit une petite table de conversion
francs/euros.
157

158

Le C en 20 heures

#include <stdio.h>
#define TAUX 6.55957
int main () {
float francs=0;
while (francs<=10) {
printf("%4.1f francs = %.2f euros\n",francs,francs/TAUX);
francs=francs+0.5;
}
return 0;
}

Lexcution donnera :
0.0
0.5
1.0
1.5
2.0
2.5
3.0
3.5
4.0
4.5
5.0
5.5
6.0
6.5
7.0
7.5
8.0
8.5
9.0
9.5
10.0

francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs
francs

=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=

0.00
0.08
0.15
0.23
0.30
0.38
0.46
0.53
0.61
0.69
0.76
0.84
0.91
0.99
1.07
1.14
1.22
1.30
1.37
1.45
1.52

euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros
euros

Notez lutilisation du format dans le printf :


%4.1f signifie : crire en utilisant 4 caractres, dont
1 aprs la virgule exactement (si lcriture du nombre
ncessite moins de 4 caractres, laffichage sera cal
droite).
%.2f signifie : crire le nombre en affichant 2 chiffres
aprs la virgule exactement.

Quelques exemples de programmes

159

3. Proportion de nombres pairs et impairs


Voici un programme dont le but est de gnrer 1000 nombres pseudoalatoires et de compter combien sont pairs. En gnrant suffisamment de
nombres, nous devrions nous rapprocher de 50%
#include <stdio.h>
#include <stdlib.h> /* pour le gnrateur pseudo-alatoire */
#include <time.h>
int main () {
int nb_hasard=0;
int i;
int nb_pairs=0; /*compte le nombre de nombres pairs */
int nb_impairs=0; /*compte le nombre de nombre impairs */
srand (time (NULL)); /*initialisation du gnrateur
de nombres alatoires une valeur
diffrente chaque excution */
i=0;
do {
nb_hasard = rand ();
if (nb_hasard % 2==0) /* cest un nombre pair */
nb_pairs=nb_pairs+1;
else
nb_impairs=nb_impairs+1;
i++;
}
while (i<1000);
printf("Proportion de nombres pairs=%f\n",(float)nb_pairs/i);
printf("Proportion de nombres impairs=%f\n",(float)nb_impairs/i)
, ;
return 0;
}

Notez lutilisation du cast dans les dernires lignes.


Les deux variables nb_pairs et i tant entires, la division aurait t arrondie (toujours 0 en loccurrence). La
conversion de type (float) est donc ici trs importante.

160

Le C en 20 heures

4. Affichage dune table de multiplication


Nous souhaitons obtenir la table de multiplication suivante :
1
2
3
4
5
6
7
8
9
10

2
4
6
8
10
12
14
16
18
20

3
6
9
12
15
18
21
24
27
30

4
8
12
16
20
24
28
32
36
40

5
10
15
20
25
30
35
40
45
50

6
12
18
24
30
36
42
48
54
60

7
14
21
28
35
42
49
56
63
70

8
16
24
32
40
48
56
64
72
80

9
18
27
36
45
54
63
72
81
90

10
20
30
40
50
60
70
80
90
100

Voici le programme qui ralise cette tche :


#include <stdio.h>
int main () {
int ligne, colonne;
for (ligne=1;ligne<=10;ligne++) {
for (colonne=1;colonne<=10;colonne++) {
printf("%4d",ligne*colonne); /* affichage sur 4 caractres */
}
printf("\n");
}
return 0;
}

5. Maximum dun tableau


Voici un exemple de fonction qui renvoie la valeur maximale stocke
dans un tableau qui lui est pass en paramtre. Voir le commentaire en
dessous.
#include <stdio.h>
int max_Tableau (int tab[], int taille);
int main() {
int t1[] = {1,10,4,5,-7}, t2[] = {2,1,14,3} ;
printf("Maximum de t1 : %d\n", max_Tableau(t1,5) );
printf("Maximum de t2 : %d\n", max_Tableau(t2,4) );
return 0;
}

Quelques exemples de programmes

161

int max_Tableau (int tab [], int taille) {


int i, max;
for (i=1, max=tab[0]; i< taille; i++) {
if (max<tab[i]) max=tab[i];
}
return max;
}

En fait, seule ladresse de la 1re case du tableau est passe en paramtre la fonction max_Tableau
La ligne qui suit le #include est la dclaration de la
fonction max_Tableau. En effet, lappel la fonction
(dans main) figure avant la dfinition de la fonction. Dans
une telle situation, il est ncessaire dannoncer au compilateur lexistence de la fonction en prcisant le type de
variables en paramtres, et le type de variable renvoy.

6. Inverser les lments dun tableau


Remarque prliminaire : Les deux programmes suivants ont des effets
rigoureusement quivalents :
#include <stdio.h>
#define TAILLE 10
int main () {
int i,j;
for (i=0, j=TAILLE-1; i<j; i++,j--)
printf("i=%d j=%d\n",i,j);
return 0;
}

#include <stdio.h>
#define TAILLE 10
int main () {
int i,j;
i=0;
j=TAILLE-1;

162

Le C en 20 heures

while (i<j) {
printf("i=%d j=%d\n",i,j);
i++, j--;
}
return 0;
}

Voici prsent un programme qui :


dans un premier temps remplit un tableau de 10 cases par des valeurs
saisies au clavier
puis, dans un second temps, inverse lordre des lments du tableau.
Ainsi, si au dpart si nous avions les dix valeurs suivantes dans le tableau :
1, 2, 3, 4, 5, 6, 7, 8, 9, 10

larrive, nous aurons le tableau suivant :


10, 9, 8, 7, 6, 5, 4, 3, 2, 1

Lide est dchanger les lments du tableau laide de deux indices


qui parcourent le tableau en commenant respectivement au dbut et la
fin du tableau et qui se rencontrent en son milieu. Tandis que lindice i va
dmarrer au dbut du tableau, lindice j va dmarrer la fin. Lorsque les
indices se croisent, on arrte.
Ds que les programmes sont un peu longs, il est prfrable de dcomposer le problme sous forme de fonctions.
#include <stdio.h>
#define TAILLE 10
/* Procdure permettant la saisie dun tableau de taille n */
void saisie(int t[], int n){
int i;
for (i=0; i<n; i++) {
printf("Elment %d: ", i+1);
scanf("%d", &t[i]);
}
}
/*Procdure affichant un tableau de taille n */
void affiche (int t[],int n) {
int i;
for (i=0; i<n; i++) {
printf ("%d ", t[i]);
}
printf("\n");
}

Quelques exemples de programmes

163

/* Procdure inversant le tableau */


void miroir (int t[], int n) {
int i,j; /* indices courants */
int aide; /* pour lchange */
for (i=0, j=n-1; i<j; i++,j--) {
/* Echange de t[i] et t[j] */
aide = t[i];
t[i] = t[j];
t[j] = aide;
}
}
int main() {
/* Dclarations */
int tab[TAILLE]; /* tableau donn */
saisie (tab,TAILLE);
printf("Tableau donn : \n");
affiche(tab,TAILLE);
miroir(tab,TAILLE);
printf("Tableau rsultat:\n");
affiche(tab,TAILLE);
return 0;
}

7. Tri dun tableau


Supposons que lon dispose dun tableau tab qui contient 10 valeurs.
Nous souhaitons trier ce tableau. Une solution toute simple consiste
faire un passage sur le tableau et comparer la case dindice n avec celle
dindice n+1.
Si la case se trouvant lindice n contient une valeur plus grande que
celle de lindice n+1, alors on inverse les deux valeurs, et ainsi de suite.
Voici un exemple sur un tableau de 10 cases :

164

Le C en 20 heures

Tableau initial :
Indice de la case :
Valeur stocke :

0
12

1
10

2
4

3
5

4
6

5
7

6
8

7
9

8
10

9
1

TABLE 13.1 - Tri dun tableau (1)

On teste la case dindice 0 et la case dindice 1, si besoin est, on permute :


0
10

1
12

2
4

3
5

4
6

5
7

6
8

7
9

8
10

9
1

TABLE 13.2 - Tri dun tableau (2)

On teste la case dindice 1 et la case dindice 2, si besoin est, on permute :


0
10

1
4

2
12

3
5

4
6

5
7

6
8

7
9

8
10

9
1

TABLE 13.3 - Tri dun tableau (3)

...
Au bout dun parcours complet du tableau, on obtient :
0
10

1
4

2
5

3
6

4
7

5
8

6
9

7
10

8
1

9
12

TABLE 13.4 - Tri dun tableau (4)

Nous constatons que le tableau est mieux tri, mais a nest pas
encore parfait. Dans le pire des cas (tableau tri dans lordre dcroissant)
n-1 passages seront ncessaires et suffisants pour trier un tableau de taille
n.
Cette mthode de tri porte un nom, il sagit du tri bulles.

Quelques exemples de programmes

Voici lensemble des fonctions qui ralisent ce travail :


#include <stdio.h>
#define TAILLE 10
/******************************************/
void saisie(int t[], int n);
void affiche(int t[],int n);
void tri_tableau (int tab[], int taille);
/******************************************/
int main () {
int tab[TAILLE];
saisie(tab,TAILLE);
tri_tableau(tab,TAILLE);
printf("Voici votre tableau tri :\n");
affiche(tab,TAILLE);
return 0;
}
/* Procdure permettant la saisie dun tableau de taille n */
void saisie(int t[], int n){
int i;
for (i=0; i<n; i++) {
printf("Elment %d : ", i+1);
scanf("%d",& t[i]);
}
}
/* Procdure de tri */
void tri_tableau (int tab[], int taille) {
int i,j;
int temp;
/* tri du tableau */
for (i=0; i<taille-1; i++)
for (j=0; j<taille-1; j++)
if (tab[j]>tab[j+1]) { /* change de valeurs */
temp=tab[j];
tab[j]=tab[j+1];
tab[j+1]=temp;
}
}
/* Procdure affichant un tableau de taille n */
void affiche(int t[],int n){
int i;
for (i=0; i<n; i++) {
printf("%d ", t[i]);
}
printf("\n");
}

Il est important davoir bien tout compris dans ce programme. . .

165

166

Le C en 20 heures

Notez que dans ce programme, nous avons repris des


fonctions qui avaient t crites pour un autre problme
(saisie et affiche crites pour le programme qui affiche un tableau lenvers). Un programmeur aime beaucoup rutiliser le travail quil a dj fait. Les fonctions
servent en partie cela : tre rutilisables. Rares sont
les programmeurs qui nont pas leur bote outils de fonctions avec eux !
Par ailleurs, le programme de tri peut tre amlior. En
effet, dans notre programme, nous effectuons toujours n-1
passages, qui sont ncessaires pour trier dans le pire des
cas. Parfois, le tableau peut tre nanmoins tri en moins
de passes. La boucle for extrieure pourra tre avantageusement remplace par une boucle while qui sarrte si le
tableau est effectivement tri (parce quil ny a eu aucun
change de valeurs lors du dernier passage par exemple).
Si ce dernier point vous parat un peu compliqu, vous
pourrez y revenir plus tard et y rflchir nouveau.

8. Jeu de la vie
Historique
John Horton Conway est un mathmaticien qui a exerc lUniversit
de Cambridge puis Princeton. Trs prolifique en matire de jeux mathmatiques, il dcrivit en 1970 le jeu de la vie, visant modliser dune
faon simplifie lvolution dorganismes vivants.
Rgles du jeu
Le jeu de la vie se joue normalement sur un damier infini. Chaque case
est occupe par une cellule qui peut tre vivante ou morte. chaque gnration, chaque cellule peut natre, mourir, ou rester dans son tat. Les
rgles qui permettent de passer dune gnration lautre sont prcises et

Quelques exemples de programmes

167

ont t choisies avec soin pour que lvolution des organismes soit intressante et semble imprvisible. En premier lieu, notons que sur un damier
infini, chaque case a exactement huit voisins (si on considre aussi les voisins par une diagonale 1 ). Les rgles du jeu de la vie sont les suivantes :
une cellule vivante ayant exactement 2 ou 3 voisins vivants survit la
gnration suivante.
une cellule vivante ayant de 4 8 cellules voisines vivantes meurt
dtouffement la gnration suivante.
une cellule vivante ayant zro ou une cellule voisine vivante meurt
disolement la gnration suivante.
sur une case vide ayant exactement 3 voisins vivants, une cellule natra la gnration suivante.
Notons que cest lensemble de la gnration actuelle qui doit tre pris
en compte pour ltablissement de ltat des cellules la gnration suivante.
Voici un exemple de figure sur un petit damier. Les cellules qui devront
mourir la gnration suivante sont grises (cette figure porte un nom, il
sagit dun planeur) :

FIGURE 13.1 - Gnrations (le jeu de la vie)

1. Ce type de voisinage sappelle voisinage de Moore.

168

Le C en 20 heures

Images obtenues
Si nous faisions une version graphique, voici ce que nous pourrions obtenir (figures 13.2 13.3) :

FIGURE 13.2 - Le jeu de la vie - configuration alatoire de dpart

Quelques exemples de programmes

169

FIGURE 13.3 - Le jeu de la vie - suite

Certaines configurations rapparaissent spontanment sur un damier initialement alatoire, comme les planeurs mentionns ci-dessus. Des centaines de figures sont ainsi recenses pour leur comportement plus ou
moins remarquable (le lecteur intress pourra faire des recherches trs
fructueuses sur Internet).
Proposition de programme
Nous allons considrer que toutes les cellules sont stockes dans une
matrice (notre damier ne sera donc pas infini). Pour une case m[i][j],
les huit voisins sont :
m[i-1][j], m[i+1][j], m[i][j-1], m[i][j+1]
m[i-1][j-1], m[i+1][j+1], m[i+1][j-1], m[i-1][j+1]

170

Le C en 20 heures

Pour viter les problmes qui se posent au bord de la matrice 1 nous ne


considrerons comme faisant partie du jeu que les cellules qui ne sont pas
sur la couronne extrieure de la matrice (voir figure suivante) :

FIGURE 13.4 - Damier (le jeu de la vie)

Nous admettrons que la couronne extrieure (grise sur limage) est


constitue de cellules mortes qui ne sont pas mises jour.
On se propose de dcouper le programme de la faon suivante (lisez
ce programme, qui nest pas encore complet, ainsi que les explications
ci-dessous) :
/**************************************
JEU DE LA VIE
****************************************/
#include <stdio.h>
#include <stdlib.h>
#define TAILLE_SOUS_MATRICE 7
#define TAILLE_SUR_MATRICE 9
/* Taille de la matrice contenant */
/* les cellules + 2 pour la bordure */
/****************************************/
/******* P R O T O T Y P E S ********/
/****************************************/

1. Rappelez-vous quaccder un tableau en dehors de ses bornes est une erreur frquente, et assez difficile dtecter.

Quelques exemples de programmes

171

/* Initialisation de la matrice de dpart */


void init(int matrice [][TAILLE_SUR_MATRICE ]);
/* Indique pour la cellule de coordonnes (ligne,colonne) */
/* le nombre de cellules voisines vivantes */
int nombre_voisins (int matrice [][TAILLE_SUR_MATRICE], int ligne
, , int colonne);
/* Ralise une tape de plus dans la mise jour de la matrice:
, */
/* autrement dit, on ralise un cycle de vie */
void mise_a_jour(int matrice[][TAILLE_SUR_MATRICE]);
/* Affichage de la matrice en cours */
void affiche_matrice(int matrice [][TAILLE_SUR_MATRICE]);
/****************************************/
/******* F O N C T I O N S **********/
/****************************************/
int main() {
int i;
int nbr_cycles;
int matrice[TAILLE_SUR_MATRICE] [TAILLE_SUR_MATRICE ];
}
void init(int matrice [][TAILLE_SUR_MATRICE]) { ... }
void mise_a_jour(int matrice[][TAILLE_SUR_MATRICE]) {
int matrice_densite[TAILLE_SOUS_MATRICE][ TAILLE_SOUS_MATRICE];
...
}

Lisons le prototype de la fonction init :


void init(int matrice[] [TAILLE_SUR_MATRICE]);

Il aurait t possible dcrire :


void init(int matrice[TAILLE_SUR_MATRICE] [TAILLE_SUR_MATRICE]);

Nous pourrions penser tort que la fonction reoit la


matrice TAILLE_SUR_MATRICE*TAILLE_SUR_MATRICE
en entre et qu la sortie elle ne sera pas modifie (passage
des paramtres par valeur). Ds lors, la fonction init ne
servirait rien.

172

Le C en 20 heures

En fait, cest ladresse mmoire de la matrice


(&matrice[0][0]) qui est passe la fonction init.
Cest donc un passage par adresse qui est effectu.
La fonction ne pourra effectivement pas modifier ladresse de matrice[0][0] mais, en revanche,
pourra modifier les contenus de matrice[0][0],
matrice[1][0]. . .
Avant de voir la correction complte de cet exercice, on notera que lors
dun cycle de vie, on ne doit pas modifier la matrice de vie courante au
fur et mesure, elle doit tre modifie dun coup. Il est donc ncessaire
de passer par 2 tapes :
1. construire une matrice intermdiaire qui contient par exemple le
nombre de voisins pour chaque cellule.
2. parcourir cette matrice en une passe et modifier la matrice contenant
les cellules vivantes.
Voici donc le programme complet :
/***************************************
JEU DE LA VIE
***************************************/
#include <stdio.h>
#include <stdlib.h>
#define TAILLE_SOUS_MATRICE 7
/* On peut avoir 7 * 7 cellules vivantes */
#define TAILLE_SUR_MATRICE 9
/* On place une bordure autour qui facilite la vie du programmeur
, ... */
/***************** PROTOTYPES ***********************/
void init(int matrice [][TAILLE_SUR_MATRICE ]);
int nombre_voisins (int matrice [][TAILLE_SUR_MATRICE ],
int ligne, int colonne);
void mise_a_jour(int matrice[][TAILLE_SUR_MATRICE ]);
void affiche_matrice(int matrice [][TAILLE_SUR_MATRICE ]);
void ligne(int largeur);
/*****************************************************/
int main( ) {
int i;
int nbr_cycles;
int matrice[TAILLE_SUR_MATRICE] [TAILLE_SUR_MATRICE ];
char s[2];

Quelques exemples de programmes

printf("Nombre de cycles : ");


scanf("%i",&nbr_cycles);
init(matrice);
printf("La population au dpart : \n");
affiche_matrice(matrice);
printf("Pressez sur ENTER pour continuer...\n");
gets(s);
for(i=0; i<nbr_cycles; i++) {
mise_a_jour (matrice);
printf("La population aprs %d cycles: \n", i+1);
affiche_matrice (matrice);
printf("Pressez sur ENTER pour continuer...\n");
gets(s);
}
return 0;
}

/****************************************/
/* Initialisation de la matrice */
void init(int matrice [][TAILLE_SUR_MATRICE ]) {
/****************************************/
int i,j;
for(i=0; i<TAILLE_SUR_MATRICE; i++) {
for(j=0; j<TAILLE_SUR_MATRICE; j++) {
if (i<=j && i>0 && j<=7)
matrice[i][j]=1;
else
matrice[i][j]=0;
}
}
/* On pourrait aussi faire une initialisation alatoire */
}
/****************************************/
/* Calcul du nombre de voisins vivants */
int nombre_voisins (int matrice[][TAILLE_SUR_MATRICE ],
int ligne, int colonne) {
/****************************************/
int compte=0; /* compteur de cellules */
int i,j;
/* On additionne les 9 cellules centres en ligne,colonne */
for (i=ligne-1;i<=ligne+1;i++)
for(j=colonne-1;j<=colonne+1;j++)
compte=compte+matrice[i][j];
/* Puis on retire celle du milieu... */
compte -= matrice[ligne][colonne];

173

174

Le C en 20 heures

return compte;
}
/****************************************/
/* Correspond ltape n+1 */
void mise_a_jour(int matrice[ ][TAILLE_SUR_MATRICE ]) {
/****************************************/
int i,j;
int nbr_voisins;
int matrice_densite[TAILLE_SOUS_MATRICE][TAILLE_SOUS_MATRICE];
/* matrice qui comptabilise le nombre de voisins */
/* et cela, case par case */
for(i=0; i< TAILLE_SOUS_MATRICE; i++)
for(j=0; j< TAILLE_SOUS_MATRICE; j++)
matrice_densite[i][j]=nombre_voisins(matrice,i+1,j+1);
/* i+1 et j+1 car on passe de la SOUS_MATRICE la MATRICE */
for(i=0; i< TAILLE_SOUS_MATRICE; i++)
for(j=0; j< TAILLE_SOUS_MATRICE; j++) {
nbr_voisins=matrice_densite[i][j];
if(nbr_voisins==2)
matrice[i+1][j+1]=1;
else if (nbr_voisins==0 || nbr_voisins==4)
matrice[i+1][j+1]=0;
}
}

/****************************************/
/* Affichage lcran des cellules vivantes */
void affiche_matrice(int matrice[ ][TAILLE_SUR_MATRICE ]) {
/****************************************/
int i,j;
for(i=1; i<=TAILLE_SOUS_MATRICE; i++) {
ligne(7);
for(j=1; j<= TAILLE_SOUS_MATRICE; j++)
if (matrice[i][j]==1)
printf("|%c",*);
else
printf("|%c",|);
printf("|\n");
}
ligne(TAILLE_SOUS_MATRICE);
}
/****************************************/
/* Trac dune ligne */
void ligne(int largeur) {
/****************************************/
int i;
for(i=0; i<largeur; i++)
printf("+-");
printf("+\n");

Quelques exemples de programmes

175

Lexcution de ce programme en mode non-graphique est un peu frustrante. Ladaptation ncessaire pour dessiner rellement les cellules
lcran est cependant assez simple (fondamentalement, il faut uniquement
modifier la procdure affiche_matrice pour obtenir un affichage diffrent).

CHAPITRE

14

En deuxime lecture. . .

Programmeur C, maintenant tu es. Mais bien dautres


choses inconnues te sont encore, jeune programmeur.

1. Quelques fonctions mathmatiques


Afin dutiliser les fonctions mathmatiques, pensez inclure le fichier
den-tte math.h dans votre code source
#include <stdio.h>
#include <maths.h>

et compiler en tapant :
gcc -o essai essai.c -lm

Voici quelques fonctions mathmatiques trs utiles.

177

178

Le C en 20 heures

Fonction
exp(x)
log(x)
log10(x)
pow(x,y)
sqrt(x)
fabs(x)
floor(x)
ceil(x)
sin(x)
cos(x)
tan(x)

Explication
fonction exponentielle
logarithme naturel
logarithme base 10
xy
racine carre de x
valeur absolue
arrondir en moins
arrondir en plus
sinus de x
cosinus de x
tangente de x

Exemple
y=exp(x)
y=log(x)
y=log10(x)
z=pow(x,y)
y=sqrt(x)
y=fabs(x)
floor(1.9) renverra 1
ceil(1.4) renverra 2
y=sin(x)
y=cos(x)
y=tan(x)

TABLE 14.1 - Quelques fonctions mathmatiques

2. Pointeurs et structures
Voici un programme qui utilise un pointeur sur une structure :
#include <stdio.h>
typedef struct {
char nom [40];
char prenom [20];
int age;
} personne;
int main () {
personne p;
personne * pointeur_sur_une_personne;
printf("Veuillez entrer le nom de la personne:");
scanf("%s",p.nom);
printf("Veuillez entrer le prnom de la personne:");
scanf("%s",p.prenom);
printf("Veuillez entrer lge de la personne:");
scanf("%d",&p.age); /* ne pas oublier le & !!! */
pointeur_sur_une_personne=&p;
printf("Voici les caractristiques de cette personne:\n");
printf("nom=%s\n",(*pointeur_sur_une_personne).nom);

En deuxime lecture. . .

179

/* autre faon de lcrire */


printf("nom=%s\n",pointeur_sur_une_personne->nom);
return 0;
}

Notez que cette seconde criture (plus pratique lusage) repose sur une
flche qui est construite avec le signe moins (-) et le signe suprieur (>).

3. En finir avec les warnings du gets


En premire lecture, nous pourrions dire que ce type de warning est
normal . Prenons un exemple :
char chaine[10];
gets(chaine) ;

Supposons que votre programme soit utilis sur internet et quun utilisateur malveillant entre une chane de caractres plus grande que 10 caractres, par exemple ABCDEFGHIJKLMNOPQR . Dans ce cas, lexcution,
votre programme va recopier partir de ladresse de la variable chaine
les caractres A, B, . . . jusqu R. Ds lors, les zones mmoires qui suivent
la variable chaine seront crases. Ceci explique que le compilateur vous
indique un warning.
Pour y remdier, vous pouvez utiliser :
char buffer[128];
fgets(buffer, 128, stdin);

en sachant que stdin (mis pour standard input cest dire entre standard) dsigne le plus souvent le clavier.
fgets prsente une particularit que navait pas gets :
elle place un \n la fin de la chane qui a t lue (juste
avant l\0).

180

Le C en 20 heures

4. Sortie standard : stdout


Nous venons de voir que stdin dsignait la plupart du temps le clavier.
De la mme faon, stdout (mis pour standard output cest dire sortie
standard) dsigne le plus souvent lcran du terminal.
Les deux programmes suivants sont quivalents :
int i=123;
printf("%d",i);

int i=123;
fprintf(stdout,"%d",i);

5. Tableaux de chanes de caractres


La dclaration char mois[12][10] rserve lespace mmoire pour
12 mots contenant 10 caractres (dont 9 caractres significatifs, cest
dire sans \0) :

FIGURE 14.1 - Une matrice de caractres

Lors de la dclaration, il est possible dinitialiser toutes les cases du


tableau par des chanes de caractres constantes :

En deuxime lecture. . .

char mois[12][10]={"janvier","fvrier","mars","avril","mai",
"juin","juillet","aot","septembre","octobre","novembre",
"dcembre"};

FIGURE 14.2 - Le contenu du tableau mois

Il est possible daccder aux diffrentes chanes de caractres dun tableau, en indiquant simplement la ligne
correspondante.
Lexcution des trois instructions suivantes. . .
char mois[12][10]= {"janvier","fvrier","mars","
, avril",
"mai","juin","juillet","aot","septembre",
"octobre","novembre","dcembre"};
int i = 4;

181

182

Le C en 20 heures

printf("Aujourdhui, nous sommes en %s !\n", mois[i


, ]);

. . . affichera, par exemple, la phrase : Aujourdhui,


nous sommes en mai ! Des expressions comme
mois[i] reprsentent ladresse du premier lment dune
chane de caractres.
La remarque prcdente est trs pratique pour rsoudre le problme qui
va suivre. Nous allons crire un programme qui lit un verbe rgulier en
er au clavier et qui affiche la conjugaison au prsent de lindicatif de
ce verbe. Nous contrlerons sil sagit bien dun verbe en er avant de
conjuguer.

Verbe : programmer
je programme
tu programmes
il ou elle programme
nous programmons
vous programmez
ils ou elles programment

Nous utiliserons deux tableaux de chanes de caractres : sujets pour


les sujets et terminaisons pour les terminaisons.
Solution possible :
#include <stdio.h>
#include <string.h>
int main() {
int i;
char sujets[6][13] = {"je","tu","il ou elle","nous","vous","ils
, ou elles"};
char terminaisons[6][5] = {"e","es","e","ons","ez","ent"};
char verbe[15]; /* chane contenant le verbe */
int longueur; /* longueur du verbe */
printf("Quel verbe souhaitez-vous conjuguer ? ");
scanf("%s", verbe);
/* Sagit-il dun verbe se terminant par "er" ? */
longueur=strlen(verbe);
if ((verbe[longueur-2] != e) || (verbe[longueur-1] != r))

En deuxime lecture. . .

183

printf("%s nest pas un verbe du premier groupe !!!\n",verbe);


else {
/* Supprimer la terminaison "er" */
verbe[longueur-2]=\0;
/* Conjuguer le verbe */
for (i=0; i<6; i++)
printf("%s %s%s\n",sujets[i], verbe, terminaisons[i]);
}
return 0;
}

6. Pointeurs et tableaux
Nous allons prsent examiner les liens trs troits quil y a entre pointeurs et tableaux. En fait, nous allons voir qu chaque fois que vous manipulez des tableaux, comme par exemple la ligne contenant le printf
du programme ci-dessous, le langage C va transformer votre instruction
tableau[i] en se servant de pointeurs. . .
int tableau[10]={1,2,3,4,5,6,7,8,9,10};
int i;
for (i=0;i<10;i++)
printf("%d ",tableau[i]);

Comme nous lavons dj constat prcdemment, le nom dun tableau reprsente ladresse de son premier lment. En dautres termes :
&tableau[0] et tableau sont une seule et mme adresse.
En simplifiant, nous pouvons retenir que le nom dun tableau est un
pointeur constant sur le premier lment du tableau.
En dclarant un tableau tableau de type int et un
pointeur p sur int,
int tableau[10];
int *p;

linstruction : p = tableau ; est quivalente p =


&tableau[0] ;

184

Le C en 20 heures

Si p pointe sur une case quelconque dun tableau, alors p+1 pointe sur
la case suivante.
Ainsi, aprs linstruction : p = p+1 ;
le pointeur p pointe sur tableau[1],
*(p+1)
*(p+2)

...
*(p+i)

dsigne le contenu de tableau[1]


dsigne le contenu de tableau[2]
...
dsigne le contenu de tableau[i]

TABLE 14.2 - Pointeurs et tableaux (1)

Il peut sembler surprenant que p+i nadresse pas le ie octet derrire p,


mais la ie case derrire p . . .
Ceci sexplique par la stratgie de programmation des crateurs du langage C : si nous travaillons avec des pointeurs, les erreurs les plus sournoises sont causes par des pointeurs mal placs et des adresses mal calcules. En C, le compilateur peut calculer automatiquement ladresse de
llment p+i en ajoutant p la taille dune case multiplie par i. Ceci
est possible, parce que :
chaque pointeur accde un seul type de donnes (cast) ;
le compilateur connat le nombre doctets utiliss pour chaque type.
Enfin, comme tableau reprsente ladresse de tableau[0] :
*(tableau+1)
*(tableau+2)

...
*(tableau+i)

dsigne le contenu de tableau[1]


dsigne le contenu de tableau[2]
...
dsigne le contenu de tableau[i]

TABLE 14.3 - Pointeurs et tableaux (2)

185

En deuxime lecture. . .

Voici un rcapitulatif de tout ceci.


Soit un tableau tableau dun type quelconque et i un indice entier
alors :
tableau
tableau+i
*(tableau+i)

dsigne ladresse de
dsigne ladresse de
dsigne le contenu de

tableau[0]
tableau[i]
tableau[i]

TABLE 14.4 - Pointeurs et tableaux (3)

Si p = tableau, alors :
p
p+i
*(p+i)

dsigne ladresse de
dsigne ladresse de
dsigne le contenu de

tableau[0]
tableau[i]
tableau[i]

TABLE 14.5 - Pointeurs et tableaux (4)

Les deux programmes suivants copient les lments


strictement positifs dun tableau tableau dans un
deuxime tableau positifs.
Formalisme tableau :
int main() {
int tableau[5] = {-4, 4, 1, 0, -3};
int positifs[5];
int i,j; /* indices courants dans tableau et positifs */
for (i=0,j=0 ; i<5 ; i++)
if (tableau[i]>0){
positifs[j] = tableau[i];
j++;
}
return 0;
}

Nous pouvons remplacer systmatiquement la notation tableau[i]


par *(tableau+i), ce qui conduit ce programme qui repose sur le
formalisme des pointeurs :

186

Le C en 20 heures

int main() {
int tableau[5] = {-4, 4, 1, 0, -3};
int positifs[5];
int i,j; /* indices courants dans tableau et positifs */
for (i=0,j=0 ; i<5 ; i++)
if (*(tableau+i)>0){
*(positifs+j) = *(tableau+i);
j++;
}
return 0;
}

Voici un exemple de programme qui range les lments


dun tableau tableau dans lordre inverse. Le programme
utilise des pointeurs p1 et p2 et une variable numrique
aux pour la permutation des lments.
#include <stdio.h>
#define TAILLE 100
int main() {
int
int
int
int
int

tableau[TAILLE]; /* tableau donn */


dim;
/* nombre rel dlments du tableau */
aux;
/* variable auxiliaire pour la permutation */
/* pointeurs daide */
*p1, *p2;
i;

printf("Dimension du tableau (maximum : %d) : ",TAILLE);


scanf("%d", &dim );
i=1;
for (p1=tableau; p1<tableau+dim; p1++) {
printf("Valeur de llment %d : ", i++);
scanf("%d", p1); // notez labscence de & !!!
}
/* Affichage du tableau avant inversion */
for (p1=tableau; p1<tableau+dim; p1++)
printf("%d ", *p1); // ne pas oublier ltoile !!!
printf("\n");
/* Inversion du tableau */
for (p1=tableau,p2=tableau+(dim-1); p1<p2; p1++,p2--) {
aux = *p1;
*p1 = *p2;
*p2 = aux;
}
/* Affichage du rsultat */

187

En deuxime lecture. . .

for (p1=tableau; p1<tableau+dim; p1++)


printf("%d ", *p1);
printf("\n");
return 0;
}

7. Tableaux de pointeurs
Attention, nous allons compliquer un peu les choses. . .
Si nous avons besoin dun ensemble de pointeurs du mme type, nous
pouvons les runir dans un tableau de pointeurs.
Dclaration dun tableau de pointeurs :
<Type> *<NomTableau>[<N>]

. . . dclare un tableau <NomTableau> de <N> pointeurs


sur des donnes du type <Type>.
Un premier exemple
double * tableau[10] ;

les crochets [ ] ont une priorit suprieure ltoile *


en lisant de droite gauche nous voyons que tableau[10] sera du
type double *
nous dclarons donc un tableau de 10 pointeurs sur des double.
Exemple avec allocation mmoire
Ce petit programme stocke les jours de la semaine dans des zones mmoires alloues explicitement laide de la fonction malloc.

188

Le C en 20 heures

#include <stdio.h>
#include <malloc.h>
#include <string.h>
int main() {
char * jours[7];
int i;
for (i=0;i<7;i++) {
jours[i]=(char *) malloc(9);
}
strcpy(jours[0],"lundi");
strcpy(jours[1],"mardi");
strcpy(jours[2],"mercredi");
// ...
return 0;
}

Exemple avec allocation mmoire implicite


char mois[12][10]= {"janvier","fvrier","mars","avril",
"mai","juin","juillet","aout","septembre","octobre",
"novembre","dcembre"};

Ce programme dclare un tableau mois[] de 12 pointeurs sur char.


Chacun des pointeurs est initialis avec ladresse de lune des 12 chanes
de caractres (voir figure) :

En deuxime lecture. . .

189

FIGURE 14.3 - Un tableau de pointeurs sur des chanes de caractres


Nous pouvons afficher les 12 chanes de caractres en fournissant les
adresses contenues dans le tableau mois la fonction printf :
int i;
for (i=0; i<12; i++)
printf("%s\n", mois[i]);

8. Choix multiples avec switch


Supposons que vous ayez une cascade de if et else imbriqus crire :
int i;
printf ("Entrez une valeur:");
scanf("%d",&i);
if (i==0)
printf ("Nombre nul\n");
else {
if (i==1)
printf ("Nombre non gal un\n");
else
printf ("Autre type de nombre\n");
}

190

Le C en 20 heures

Dans ce cas, nous pouvons, pour augmenter la lisibilit du programme,


utiliser switch/case/break :
int i;
printf ("Entrez une valeur:");
scanf("%d",&i);
switch (i) {
case 0:
printf ("Nombre nul\n");
break;
case 1:
printf ("Nombre gal un\n");
break;
default:
printf("Autre type de nombre\n");
break;
}

Notez lusage du break. Si vous ne faites pas de break,


le programme, aprs tre entr dans un case, continuera
sur le case suivant et ainsi de suite.
Testez le programme suivant en entrant la valeur 0 :
int i;
printf ("Entrer une valeur:");
scanf("%d",&i);
switch (i) {
case 0:
printf ("Nombre nul\n");
/* pas de break */
case 1:
printf("Nombre gal un\n");
break;
default:
printf("Autre type de nombre\n");
break;
}

Le programme affichera lexcution :


Nombre nul
Nombre gal un

Omettre un break nest pas forcment une faute et cette particularit


peut tre utilise pour raliser des conditions ou . Par exemple, la por-

191

En deuxime lecture. . .

tion de code suivante affichera un message si a vaut 1,2 ou 5, un autre


message si a vaut 3 ou 4 et un troisime message sinon.
int a;
printf ("Entrez une valeur:");
scanf("%d",&a);
switch (a) {
case 1: case 2:
printf("Voici
break;
case 3: case 4:
printf("Voici
break;
default:
printf("Voici
break;
}

case 5:
le premier message\n");

le second message\n");

le message par dfaut\n");

9. dition de liens
Jusqu prsent, nous construisions nos programmes excutables en
entrant la commande suivante :
gcc -o essai essai.c

En ralit, cette commande ralise deux oprations : la compilation proprement dite, et ldition de liens (si la compilation sest termine avec
succs). Pour raliser uniquement ltape de compilation, il faut entrer :
gcc -c essai.c

Cette tape a pour effet de gnrer le fichier essai.o qui ne contient pour
le moment que le code objet qui correspond au source compil, mais qui
ne lie pas les appels de fonctions des bibliothques extrieures telles que
printf, scanf, feof, sqrt leur code respectif.
Ltape suivante, appele dition de liens, est ralise en entrant la commande :
gcc -o essai essai.o -lm

192

Le C en 20 heures

Lors de cette tape, les rfrences aux fonctions appeles dans le fichier objet sont ajoutes. Pour cela, les fonctions utilises sont recherches automatiquement dans les bibliothques standard (on y trouvera
printf, scanf. . . ) et dans les bibliothques spcifies dans la commande (ici -lm qui dsigne la bibliothque mathmatique et qui permettra
de trouver une rfrence la fonction sqrt par exemple) 1

10. Utilit des prototypes

Un prototype reprsente lidentification de la structure


dune fonction, il est donc prudent de placer tous les prototypes de fonctions que vous appelez juste en dessous des
#include.
#include <stdio.h>
float mystere (int i); // prototype de la fonction mystre
int main () {
int j;
printf("Entrez une valeur:");
scanf("%d",&j);
printf("Rsultat de la fonction mystre:%f\n",mystere(j));
}
float mystere (int i) {
return i*i;
}

a Voyons ce qui se produit si nous omettons le prototype :


#include <stdio.h>
int main () {
int j;

1. Prcisons au passage quil existe deux sortes ddition de liens :


1. ldition de liens dynamiques, dont nous venons de parler, insre dans le programme
excutable des rfrences vers les fonctions des bibliothques, qui seront charges
par ailleurs lexcution.
2. ldition de liens statiques (option -static de gcc) insre le code complet des fonctions
dans lexcutable. Il en rsulte un fichier plus gros, mais qui a lavantage de ne plus
dpendre dautres bibliothques. . .

En deuxime lecture. . .

193

printf("Entrez une valeur:");


scanf("%d",&j);
printf("Rsultat de la fonction mystere:%f\n",mystere(j));
}
float mystere (int i) {
return i*i;
}

Au moment o le programme rencontre lappel la fonction mystere,


le compilateur dcouvre pour la premire fois cette fonction. Or il ne sait
pas ce que fait cette fonction et a fortiori il ignore le type du rsultat
qui sera renvoy. Dans ce cas, le compilateur suppose ( tort dans notre
exemple) que ce rsultat sera du type int (qui est le type par dfaut). Ceci
risque de poser des problmes dans la suite (et provoquera lmission dun
avertissement) quand le compilateur sapercevra quen fait, la fonction
renvoie un float.
Le fait que int soit le type par dfaut explique pourquoi
nous pouvons nous permettre dcrire un main comme
ceci :
main () {
...
}

plutt que comme cela :


int main () {
...
}

11. Utilit des librairies : 1+1=1


Il existe un autre intrt au prototypage des fonctions, celui de pouvoir
crer des librairies de fonctions, ses propres librairies.
Comme nous lavons vu, nous intgrons toujours le .h de stdio pour que
notre programme sache quels sont les services que ce dernier peut nous
fournir.
Amusons nous maintenant crer notre propre librairie.

194

Le C en 20 heures

Notre librairie va contenir pour lexemple une seule fonction nomme


minimum qui donne le nombre le plus petit entre deux.
Commenons par crire un programme principal.c utilisant cette
fonction :
#include <stdio.h>
int minimum (int a, int b)
{
if (a > b)
return (b);
else
return (a);
}
int main ()
{
int a=13;
int b=15;
int c=minimum(a,b);
printf ("Minimum entre %d et %d : %d",a,b,c);
return 0;
}

Compilons le :
gcc -o principal principal.c

et vrifions que ce dernier fait bien son travail :


$ ./principal
Minimum entre 13 et 15 : 13

Nous allons dcouper ce code en plusieurs parties de manire isoler


notre fonction minimum. Pour se faire, nous crons un fichier minimum.h
qui contient lannonce de notre fonction.
int minimum (int, int);

Changeons notre code principal pour ceci (principal.c) :


#include <stdio.h>
#include "minimum.h"
int main ()
{
int a=13;

En deuxime lecture. . .

195

int b=15;
int c=minimum(a,b);
printf ("Minimum entre %d et %d : %d",a,b,c);
return 0;
}

On remarquera que les < > de linclude se sont transforms en " " pour
indiquer que le .h est rechercher sur le rpertoire courant et non dans
les librairies C.
La compilation du programme fonctionne bien sauf au moment de la ralisation de ldition de liens.
$ gcc -c principal.c
$ gcc -o principal principal.o
principal.o: In function main:
principal.c:(.text+0x29): undefined reference to minimum
collect2: ld returned 1 exit status

La raison pour laquelle cette dernire ne fonctionne pas est quil nexiste
pas de fichier binaire (.o) contenant la fonction minimum contrairement
ce qui a t annonc.
Corrigeons cela tout de suite en crant un fichier minimum.c :
#include "minimum.h"
/* Cet appel est ncessaire */
int minimum (int a, int b)
{
if (a > b)
return (b);
else
return (a);
}

Compilons notre code :


gcc -c minimum.c

Lions notre objet notre programme principal :


gcc -o principal principal.o minimum.o

La liaison des objets seffectue sans erreur et le programme fonctionne


alors comme avant.
Nous avons cr notre propre librairie de fonctions.

196

Le C en 20 heures

Lintrt de cette mthode nest pas forcment trs vidente sur cet
exemple mais imaginez un programme disposant de centaines de fonctions !

CHAPITRE

15

Exercices

Rien ne saurait mieux faire apprendre que de pratiquer,


donc pratiquons mais cette fois sans filet. . .

1. Objectifs
Ces quelques exercices avec corrigs vous permettront de vous familiariser un peu plus avec le langage C. Dans tous ces exercices, on supposera
que lutilisateur ne fait pas derreur de saisie.

2. Jeu de morpion
Principe
Il sagit du morpion classique que lon pourrait reprsenter comme ceci :
197

198

Le C en 20 heures

X O .
X O X
O . O

Le gagnant est celui qui aligne le premier 3 signes identiques (3 X ou 3


O), sur lhorizontale, la verticale ou la diagonale.
Affichage du plateau de jeu
Nous allons dfinir un tableau pour composer le plateau du morpion et
linitialiser 0.
Nous dirons quune case du plateau de jeu est vide si la case du tableau
correspondante contient la valeur 0.
crivez une fonction qui dessine ce plateau.
crivez un programme qui dessine le plateau vide,
puis avec les ventuels O ou X .
Saisie des coordonnes
crivez une fonction qui permet la saisie dune coordonne.
Dans un premier temps on ne contrlera que si la coordonne est comprise entre 1 et 3.
Compltez le programme en permettant la saisie des
deux coordonnes X et Y.
Compltez le programme en affichant la grille rsultante de la saisie de ces coordonnes (on ne contrlera
pas le fait que la case soit occupe ou non).
Compltez le programme en testant si la case nest pas
dj occupe.

199

Exercices

Alternance des joueurs


Modifiez le programme pour que celui-ci fasse jouer
deux joueurs :
on positionnera la valeur de la case du tableau 1 pour
le joueur 1 et 2 pour le joueur 2,
on ne regardera pas qui a gagn dans limmdiat,
on affichera 0 pour le joueur 1 et X pour le joueur 2.
Fin du jeu
Modifiez le programme pour que celui-ci sarrte
lorsque toutes les cases sont occupes.
Modifiez le programme pour que celui-ci sarrte
lorsque lun des deux joueurs a gagn ou lorsque
toutes les cases sont occupes.
Amliorations possibles
Faites en sorte que lordinateur joue en tant que joueur 2 :
dans un premier temps de manire alatoire en testant la possibilit
de poser son pion (9 cases) ;
dans un second temps en pondrant les diffrentes cases du plateau
(le centre est la case la plus forte, le coin vient ensuite. . . )

3. Jeu de pendu
Principe
Le but du jeu est de deviner en moins de 7 essais un mot que seul lordinateur connat.
Pour mener bien votre mission, vous allez proposer une lettre :
si la lettre est correcte alors, celle-ci saffiche sa place dans le mot
deviner ;

200

Le C en 20 heures

si la lettre est incorrecte, alors, votre nombre dessais diminue de 1.


Autrement dit :

lorsquune lettre est correcte, le nombre dessais reste inchang ;


lorsquune lettre est incorrecte, le nombre dessais diminue de 1 ;
lorsque tout le mot a t devin, vous avez gagn ;
lorsque le nombre dessais est zro (0), vous avez perdu.

Exemple
Supposons que le mot deviner soit bonjour .
Vous proposez la lettre o , cette dernire se trouve dans le mot, lordinateur affiche donc *o**o**. Si vous proposez ensuite la lettre u ,
lordinateur affiche : *o**ou*.
Si vous vous sentez laise, ne lisez pas ce qui suit et essayez de programmer le jeu. Dans le cas contraire, le dtail des diffrentes tapes vous
aidera sans doute raliser le programme.
Un peu daide
Point de dpart
Commencez par crire un programme qui dclare une variable contenant le mot trouver : motAtrouver et une autre contenant une chaine
de mme longueur remplie avec des * : motCherche.
Algorithme
Un tour de jeu se compose des phases suivantes :
Saisie dun caractre
Initialisation des caractres correspondant
dans le mot cach
Si aucun caractre na t trouv
nombre dessais -1
Si motAtrouver == motCherche GAGNE
Si nombre dessais == 0 PERDU

201

Exercices

Amliorations possibles
Libre vous dajouter ce que vous voulez. Voici quelques suggestions :

lire le mot dans un dictionnaire de mots.


sauvegarder/recharger une partie en cours.
grer les meilleurs scores au moyen dun fichier.
dessiner une potence, mettre un peu de couleur.

4. Balle rebondissante
On souhaite raliser un programme qui permettrait de faire rebondir une
balle sur les bords de lcran. La balle doit pouvoir rebondir sur un cran
de la forme suivante (figure de gauche) :
Afin de faciliter la programmation, on rajoute une couronne dtoiles
qui permettra de simplifier les tests lorsque la balle arrivera sur un bord
(figure de droite).

FIGURE 15.1 - Balle rebondissante

La balle, qui est symbolise par le caractre O dmarre la position


[4;4]. Elle part suivant le vecteur dabscisse +1 et dordonne +1.

202

Le C en 20 heures

Lorsquelle arrive sur un bord vertical, il suffit dinverser le sens de dplacement selon laxe des ordonnes. De mme, si la balle arrive sur un
bord horizontal, on inverse le sens du dplacement selon laxe des abcisses.
Pour tester si on arrive sur un bord, il est possible de
faire :
if (grille[nouvelle_pos_x][nouvelle_pos_y]==*)

Naturellement il est aussi possible de tester les coordonnes nouvelle_pos_x et nouvelle_pos_y pour savoir
si on arrive sur un bord.
Essayez de complter le programme suivant . . .
#include <stdio.h>
#include <string.h>
/*******PROTOTYPES DES FONCTIONS **********/
/* Initialise la grille de faon ce quelle contienne ce quil
y a la figure de droite
*/
void init_grille (char grille[][10],int pos_balle_x,int
, pos_balle_y) ;
/* Affiche le rectangle dtoiles et la balle (tout ceci en mme
temps et non pas le rectangle puis la balle...)
*/
void affiche_grille (char grille[][10]);
/*10 lignes 10 colonnes*/
/* Calcule la nouvelle position de la balle en fonction de
lancienne position de la balle (old_pos_balle_x,
, old_pos_balle_y)
et du vecteur de dplacement (deplacement_x, deplacement_y).
*/
void calcule_position_balle (char grille[][10], int *pos_balle_x,
int *pos_balle_y, int *deplacement_x, int *
, deplacement_y);
/************* IMPLEMENTATION DES FONCTIONS ****************/
void init_grille (char grille[][10], int pos_balle_x,int
, pos_balle_y) {...}
void affiche_grille (char grille[][10]) {...}
void calcule_position_balle (char grille[][10], int *pos_balle_x,

203

Exercices

int *pos_balle_y,int *deplacement_x,int *


, deplacement_y); {...}
int main () {
int pos_balle_x=4, pos_balle_y=4; /* position balle au dpart */
int deplacement_x=1, deplacement_y=1; /* dplacement balle */
char grille[10][10] ; /* grille qui contiendra 3 caractres : */
/* * ou O ou le caractre espace */
init_grille (grille, pos_balle_x, pos_balle_y) ;
while (1) {
system("clear");
affiche_grille(grille);
calcule_position_balle (grille, &pos_balle_x, &pos_balle_y,
&deplacement_x, &deplacement_y);
usleep(500000) ; /* Pause de 500 000 micro secondes donc 1/2
, seconde */

}
}

Amliorations
Comme on peut le constater, le mouvement de la balle est cyclique,
introduisez un dplacement alatoire de 1 case tous les x tours o x est un
nombre alatoire entre 0 et 9.

5. Solutions
Le morpion
#include <stdio.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
void dessine_plateau (int plateau[][3]) {
int i=0,j=0;
printf ("\n-------\n");

204

Le C en 20 heures

for (i=0;i<3;i++) {
for (j=0;j<3;j++) {
printf("|");
switch(plateau[i][j]) {
case 0:
printf(" ");
break;
case 1:
printf("O");
break;
case 2:
printf("X");
break;
}
}
printf ("|\n");
printf ("-------\n");
}
}
int fin_jeu (int plateau[][3]) {
int i=0,j=0;
for (i=0;i<3;i++) {
for (j=0;j<3;j++) {
if (plateau [i][j]==0) {
return FALSE;
}
}
}
return TRUE;
}
int saisie_donnee (char *invite) {
int valeur;
do {
printf ("%s", invite);
scanf ("%d",&valeur);
} while (( valeur <1) || (valeur >3));
return (valeur);
}
int gagne (int plateau[][3]) {
int i=0;
// Test sur les lignes
for ( i=0; i<3; i++) {
if (( plateau[i][0] >0) && ( plateau[i][0] == plateau[i][1] )
, && ( plateau[i][1] == plateau[i][2] )) {
puts ("GAGNE");
return TRUE;

Exercices

205

}
}
// Test sur les colonnes
for ( i=0; i<3; i++) {
if (( plateau[0][i] >0) && ( plateau[0][i] == plateau[1][i] )
, && ( plateau[1][i] == plateau[2][i] )) {
puts ("GAGNE");
return TRUE;
}
}

// Test sur les diagonales


if (( plateau[0][0] >0) && ( plateau[0][0] == plateau[1][1] ) &&
, ( plateau[1][1] == plateau[2][2] )) {
puts ("GAGNE");
return TRUE;
}
// Test sur les diagonales
if (( plateau[0][2] >0) && ( plateau[0][2] == plateau[1][1] ) &&
, ( plateau[1][1] == plateau[2][0] )) {
puts ("GAGNE");
return TRUE;
}
return FALSE;
}
void jeu (int plateau[][3], int joueur) {
int pos_x=0,pos_y=0;
int correct=FALSE;
do {
printf ("Joueur %d\n",joueur);
pos_x= saisie_donnee ("Ligne : ");
pos_y= saisie_donnee ("Colonne : ");
if ( plateau[pos_x-1][pos_y-1]>0 ) {
printf ("Case occupe !\n");
} else {
plateau[pos_x-1][pos_y-1]=joueur;
correct=TRUE;
}
}
while (! correct);
dessine_plateau (plateau);
}
int main () {
int plateau [3][3];
int joueur=1;

206

Le C en 20 heures

// la fonction memset permet dinitialiser chacun


// des octets dune zone donne avec une valeur
// dtermine (ici: 0)
memset (plateau, 0, 9*sizeof (int));
dessine_plateau (plateau);
do {
jeu (plateau, joueur);
if ( joueur==1 ) {
joueur=2;
} else {
joueur=1;
}
}
while (( !gagne (plateau)) && (!fin_jeu (plateau)) );
return 0;
}

Le pendu
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
char lireCaractere() {
char chaine[2];
gets(chaine);
return chaine[0];
}
int main() {
int i=0;
int coups=7;
char motAtrouver[]="BONJOUR";
char lettreSaisie= ;
int lettre_trouvee=FALSE;
char gagne=FALSE;
char* motCherche;
motCherche=malloc (sizeof (motAtrouver));
memset (motCherche,*,sizeof (motAtrouver));
motCherche[sizeof (motAtrouver)-1]=0;
printf("Jeu de pendu \n");

Exercices

do {
// Aucune lettre trouve
lettre_trouvee=FALSE;
// Saisie dune lettre et mise en majuscule
printf("\nVotre lettre : ");
lettreSaisie=lireCaractere();
// Comparaison avec le mot secret
for(i=0; i<strlen (motAtrouver); i++) {
if(lettreSaisie==motAtrouver[i]) {
motCherche[i]=lettreSaisie;
lettre_trouvee=TRUE;
}
}
printf("%s", motCherche); //on affiche le mot cache
if (!lettre_trouvee) {
coups--;
}
printf("\nIl vous reste %d coups.\n ", coups );
gagne=! strcmp(motAtrouver, motCherche);
}
while(!gagne && coups>0);
if ( gagne )
puts ("GAGNE");
else
puts ("PERDU");
getchar();
free (motCherche);
return 0;
}

207

208

Le C en 20 heures

Balle rebondissante
#include <stdio.h>
#include <string.h>
/************PROTOTYPES DES FONCTIONS **************/
/* Initialise la grille de faon ce quelle contienne ce quil
y a la figure de droite
*/
void init_grille (char grille[][10],int pos_balle_x,int
, pos_balle_y) ;
/* Affiche le rectangle dtoiles et la balle (tout ceci en mme
temps et non pas le rectangle puis la balle...)
*/
void affiche_grille (char grille[][10]); /* 10 lignes 10 colonnes
, */
/* Calcule la nouvelle position de la balle en fonction de
lancienne position de la balle (old_pos_balle_x,
, old_pos_balle_y)
et du vecteur de dplacement (deplacement_x, deplacement_y).
*/
void calcule_position_balle (char grille[][10], int *pos_balle_x,
int *pos_balle_y, int *deplacement_x, int *
, deplacement_y);
/***************** IMPLEMENTATION ******************************/
void init_grille(char grille[][10],int pos_balle_x,int
, pos_balle_y){
int ligne, colonne;
memset (grille, ,100);
for (colonne=0; colonne <10; colonne++) {
grille [0][colonne]=*;
grille [9][colonne]=*;
}
for (ligne=0; ligne<10; ligne++) {
grille [ligne][0]=*;
grille [ligne][9]=*;
}
grille [pos_balle_x][pos_balle_y]=O;
}
void affiche_grille (char grille[][10]) {
int ligne, colonne;
for (ligne=0; ligne<10; ligne++ ) {
for (colonne=0; colonne <10; colonne++) {
printf ("%c",grille[ligne][colonne]);
}

Exercices

209

printf ("\n");
}
}
void calcule_position_balle (char grille[][10], int *pos_balle_x,
int *pos_balle_y,int *deplacement_x,int *
, deplacement_y) {
int theo_pos_x=0;
int theo_pos_y=0;
// On efface lancienne balle
grille[*pos_balle_x][*pos_balle_y]= ;
printf ("Position actuelle : %d / %d\n",*pos_balle_x,*
, pos_balle_y);
printf ("Dplacement : %d / %d\n",*deplacement_x,*deplacement_y)
, ;
// On calcule la future position thorique de la balle
theo_pos_x = *pos_balle_x + *deplacement_x;
theo_pos_y = *pos_balle_y + *deplacement_y;
// En fonction de la position thorique de la balle
// on modifie les vecteurs de dplacement
if (grille[theo_pos_x][theo_pos_y]==*) {
// Si on tape sur laxe vertical
if (( theo_pos_x == 0 ) || ( theo_pos_x == 9 ))
*deplacement_x = - *deplacement_x;
// Si on tape sur laxe horizontal
if (( theo_pos_y == 0 ) || ( theo_pos_y == 9 ))
*deplacement_y = - *deplacement_y;
}
// On calcule la nouvelle position de la balle
*pos_balle_x += *deplacement_x;
*pos_balle_y += *deplacement_y;
printf ("Nouvelle Pos : %d/%d\n",*pos_balle_x,*pos_balle_y);
// On met la balle dans la grille
grille[*pos_balle_x][*pos_balle_y]=O;
}
int main () {
int pos_balle_x=4, pos_balle_y=4; /* position balle au dpart */
int deplacement_x=1, deplacement_y=1; /* dplacement balle */
char grille[10][10] ; /* grille qui contiendra 3 caractres : */
/* * ou O ou le caractre espace */
init_grille (grille, pos_balle_x, pos_balle_y) ;

210

Le C en 20 heures

while (1) {
system("clear");
affiche_grille(grille);
calcule_position_balle (grille, &pos_balle_x, &pos_balle_y, &
, deplacement_x, &deplacement_y);
usleep(500000) ; /* Pause de 500 000 micro secondes donc 1/2
, seconde */
}
}

ANNEXE

Petit extrait de la table Ascii

...
32:
40:
48:
56:
64:
72:
80:
88:
96:
104:

(
0
8
@
H
P
X

33:
41:
49:
57:
65:
73:
81:
89:
97:
105:

!
)
1
9
A
I
Q
Y
a
i

34:
42:
50:
58:
66:
74:
82:
90:
98:
106:

"
*
2
:
B
J
R
Z
b
j

35:
43:
51:
59:
67:
75:
83:
91:
99:
107:

#
+
3
;
C
K
S
[
c
k

36:
44:
52:
60:
68:
76:
84:
92:
100:
108:

...

211

$
,
4
D
L
T
\
d
l

37:
45:
53:
61:
69:
77:
85:
93:
101:
109:

%
5
=
E
M
U
]
e
m

38:
46:
54:
62:
70:
78:
86:
94:
102:
110:

.
6
F
N
V
^
f
n

39:
47:
55:
63:
71:
79:
87:
95:
103:
111:

/
7
?
G
O
W
_
g
o

ANNEXE

Bon savoir

Type de donne

Signification

Taille
(en octets)
1
4

char
int

caractre
entier

float

flottant (rel)

double

flottant double

Les types numriques les plus utiles

213

Plage de valeurs
128 127
2147483648
2147483647
3.4 1038
3.4 1038
1.7 104932
1.7 104932

214

Le C en 20 heures

Les trois tableaux suivants sont connatre par coeur !

Format

Conversion en

%d
%f
%lf
%c
%s

int
float
double
char
char*

Les formats les plus utiles pour printf

Format

Conversion en

%d
%f
%f
%c
%s

int
float
double
char
char*

Les formats les plus utiles pour scanf

215
Bien des erreurs pourraient tre vites en matrisant ce tableau :

Erreur

Version correcte

if (i=0)

if (i==0)

scanf ("%d",n);

scanf ("%d",&n);

scanf ("%s",s) est quivalent


gets(s)

gets(s) permet de lire une phrase

if (a & b)

if (a && b)

Oublier dinitialiser une variable

var=0; ptr=NULL;

tab[i,j]

tab[i][j]

Les bornes dun tableau de N cases


varient entre 1 et N

Les bornes dun tableau de N cases


varient entre 0 et N-1

char c ;

char c ;

printf ("%s",c);

printf ("%c",c);

char * s;

char * s;

printf ("%c",c);

printf ("%s",c);

complte (jusqu lappui sur


ENTREE )

Douze erreurs parmi les plus classiques en langage C (1)

216

Le C en 20 heures

char chaine[3]="12345";

char chaine[6]="12345";
ou mieux :

char chaine[]="12345";

char chaine[]=12345;

char chaine[]="12345";

Si vous utilisez des fonctions


mathmatiques telles que sqrt. . .

compilez le programme avec -lm

gcc -0 essai essai.c -lm

Douze erreurs parmi les plus classiques en langage C (2 suite)

Liste des tableaux

4.1 4.2 4.3 4.4 4.5 4.6 4.7 -

Oprateurs de comparaison . . . . . . . . . . . .
Oprateurs logiques . . . . . . . . . . . . . . . .
Loprateur ou . . . . . . . . . . . . . . . . . . .
Loprateur ou : analogie avec laddition . . . . .
Loprateur et . . . . . . . . . . . . . . . . . . .
Loprateur et : analogie avec la multiplication . .
Oprateurs : formulations correctes et incorrectes

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

36
37
37
37
38
38
39

6.1 - Incrmentation / Dcrmentation . . . . . . . . . . . . . . 59


6.2 - Incrmentation / Dcrmentation (bis) . . . . . . . . . . . 64
8.1 8.2 8.4 8.5 8.6 8.7 8.8 -

quivalence entre lcriture binaire et lcriture dcimale


Base 16 et base 10 . . . . . . . . . . . . . . . . . . . . .
Stockage de variables (a) . . . . . . . . . . . . . . . . .
Stockage de variables (b) . . . . . . . . . . . . . . . . .
Stockage de variables (c) . . . . . . . . . . . . . . . . .
Stockage de variables (d) . . . . . . . . . . . . . . . . .
Stockage de variables (e) . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.

76
77
80
81
81
88
89

9.1 - Tableau de caractres . . . . . . . . . . . . . . . . . . . . 98


12.1 -Dclaration de variables . . . . . . . . . . . . . . . . . . . 145
217

218

Le C en 20 heures

13.1 -Tri dun tableau (1)


13.2 -Tri dun tableau (2)
13.3 -Tri dun tableau (3)
13.4 -Tri dun tableau (4)

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

164
164
164
164

14.1 -Quelques fonctions mathmatiques


14.2 -Pointeurs et tableaux (1) . . . . .
14.3 -Pointeurs et tableaux (2) . . . . .
14.4 -Pointeurs et tableaux (3) . . . . .
14.5 -Pointeurs et tableaux (4) . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

178
184
184
185
185

Liste des illustrations

1.1 - Une fentre de terminal . . . . . . . . . . . . . . . . . . .


1.2 - Une fentre de terminal et lditeur Scite . . . . . . . . . .

2
3

11.1 -Dbogage dun programme . . . . . . . . . . . . . . . . . 135


12.1 -Adresse mmoire (a) . . . . . . . . . . . . . . . . . . . . 149
12.2 -Adresse mmoire (b) . . . . . . . . . . . . . . . . . . . . 149
12.3 -Adresse mmoire (c) . . . . . . . . . . . . . . . . . . . . 150
13.1 -Gnrations (le jeu de la vie) . . . . . . . . . . .
13.2 -Le jeu de la vie - configuration alatoire de dpart
13.3 -Le jeu de la vie - suite . . . . . . . . . . . . . . .
13.4 -Damier (le jeu de la vie) . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

167
168
169
170

14.1 -Une matrice de caractres . . . . . . . . . . . . . . . . . . 180


14.2 -Le contenu du tableau mois . . . . . . . . . . . . . . . . . 181
15.1 -Balle rebondissante . . . . . . . . . . . . . . . . . . . . . 201

219

Table des matires

Avant de commencer
1

iv

Premiers pas
1. Systme dexploitation et C . . . . . .
2. Utiliser un diteur sous G NU/Linux . .
3. Exemple de programme . . . . . . . .
4. Normalisation du programme . . . . .
5. Petit mot sur ce quest une bibliothque
6. Un exemple de fichier en-tte . . . . .
7. Complments . . . . . . . . . . . . .
8. Squelette de programme . . . . . . . .
9. Blocs . . . . . . . . . . . . . . . . .
10. Commentaires . . . . . . . . . . . .
11. Exercice dapplication . . . . . . . .
12. Corrig de lexercice du chapitre . . .
13. retenir . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

1
1
2
4
5
7
7
8
9
9
9
10
10
11

Variables (partie 1)
1. Objectif . . . . . . . . . .
2. Affichage : la fonction printf
3. Notion de variable . . . . .
4. Dclaration dune variable .
5. Application : exemples . .
6. Utilisation de % dans printf

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

13
13
14
14
15
15
17

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

221

222

Le C en 20 heures

7. Exercices . . . . . . . . . . . . .
8. Rutilisation dune variable . . . .
9. Caractres spciaux . . . . . . . .
10. Exercices . . . . . . . . . . . . .
11. Corrigs des exercices du chapitre
12. retenir . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

18
18
19
20
20
22

Variables (partie 2)
1. Objectif . . . . . . . . . . . . .
2. Exercice de mise en bouche . . .
3. Dclaration de variables . . . . .
4. Saisie des variables . . . . . . .
5. Les types flottants . . . . . . . .
6. Dautres fonctions utiles . . . . .
7. Corrigs des exercices du chapitre
8. retenir . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

23
23
24
24
25
28
28
29
32

Conditions
1. Objectif . . . . . . . . . . . . . . .
2. Exercice de mise en bouche . . . . .
3. Condition : Si Alors Sinon . . . . . .
4. Oprateurs de comparaison . . . . .
5. Oprateurs logiques . . . . . . . . .
6. Vrai ou faux . . . . . . . . . . . . .
7. Combinaison . . . . . . . . . . . .
8. Accolades . . . . . . . . . . . . . .
9. Exercices . . . . . . . . . . . . . .
10. Corrections des exercices du chapitre
11. retenir . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

33
33
34
35
36
37
37
39
40
41
42
43

Mise au point
1. Objectif . . . . . . . . . . . . . . . .
2. Plus petit ou plus grand . . . . . . . .
3. Retour sur getchar() . . . . . . . . . .
4. Boucle : Faire . . . Tant que (condition)
5. Oprateur modulo . . . . . . . . . . .
6. Nombres pseudo-alatoires . . . . . .
7. Corrigs des exercices du chapitre . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

45
45
46
46
47
49
49
51

.
.
.
.
.
.
.
.

223
6

Et les Shadoks pompaient :


je pompe donc je suis
1. Objectifs . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2. Boucle While . . . . . . . . . . . . . . . . . . . . . . . . .
3. Et les Shadoks apprenaient que reprendre quivaut apprendre
4. Fonction toupper() . . . . . . . . . . . . . . . . . . . . . . .
5. tant quen emporte le Shadok . . . . . . . . . . . . . . . .
6. Et les Shadoks continuaient pomper pour obtenir le rsultat .
7. Dans le clan des Shadoks, on trie, voyelles, chiffres premiers .
8. Incrmentations, pr-incrmentations. . . . . . . . . . . . . . .
9. Corrigs des exercices du chapitre . . . . . . . . . . . . . . .
10. retenir . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

53
54
54
54
55
56
56
57
58
60
64

Boucles
1. Objectifs . . . . . . . . . . . . . . . . . .
2. Et les Shadoks pdalrent pendant 15 tours
3. Syntaxe . . . . . . . . . . . . . . . . . .
4. Notion de double boucle . . . . . . . . . .
5. Et les Shadoks ftrent Nol. . . . . . . . .
6. Table Ascii . . . . . . . . . . . . . . . .
7. Corrigs des exercices du chapitre . . . . .
8. retenir . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

65
65
65
66
67
68
69
70
73

Pointeurs et fonctions
1. Objectifs . . . . . . . . . . . . .
2. Binaire, octets. . . . . . . . . . .
3. Variables : pointeurs et valeurs . .
4. Fonctions . . . . . . . . . . . .
5. Corrigs des exercices du chapitre
6. retenir . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

75
75
76
78
83
93
94

Tableaux et chanes de caractres


1. Objectifs . . . . . . . . . . . . .
2. Tableaux . . . . . . . . . . . . .
3. Chanes de caractres . . . . . .
4. Quelques fonctions utiles . . . .
5. Tableaux 2 dimensions . . . . .
6. Zro . . . . . . . . . . . . . . .
7. Correction de lexercice . . . . .
8. retenir . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

97
97
98
99
106
109
111
114
114

224
10 Structures et fichiers
1. Les types synonymes
2. Structures . . . . . .
3. Bases sur les fichiers .
4. Fichiers et structures .

Le C en 20 heures

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

117
117
118
120
124

11 Dbogage dun programme


1. Objectif . . . . . . . . . . . . . . . . . . . . . .
2. Deux types derreurs . . . . . . . . . . . . . . . .
3. Un phnomne surprenant. . . . . . . . . . . . . .
4. La chasse aux bugs. . . . . . . . . . . . . . . . . .
5. Bonne chasse. . . . . . . . . . . . . . . . . . . . .
6. Erreurs dexcution : les erreurs de segmentation. . .
7. Solutions . . . . . . . . . . . . . . . . . . . . .
8. retenir . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

127
127
127
129
130
132
132
138
138

12 Complments
1. Objectif . . . . . . . . . . . . . . . .
2. Conversions de type . . . . . . . . . .
3. Usage trs utile des conversions de type
4. Fonction putchar . . . . . . . . . . . .
5. Allocation dynamique de mmoire . .
6. Avez-vous bien compris ceci ? . . . . .
7. Sur lutilit des pointeurs . . . . . . .
8. Un mot sur les warnings . . . . . . . .
9. C obscur . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

141
141
141
142
143
144
148
148
151
152

13 Quelques exemples de programmes


1. Objectifs . . . . . . . . . . . . . . . .
2. Convertisseur francs/euros . . . . . . .
3. Proportion de nombres pairs et impairs
4. Affichage dune table de multiplication
5. Maximum dun tableau . . . . . . . .
6. Inverser les lments dun tableau . . .
7. Tri dun tableau . . . . . . . . . . . .
8. Jeu de la vie . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

157
157
157
159
160
160
161
163
166

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

225
14 En deuxime lecture. . .
1. Quelques fonctions mathmatiques
2. Pointeurs et structures . . . . . . .
3. En finir avec les warnings du gets .
4. Sortie standard : stdout . . . . . .
5. Tableaux de chanes de caractres .
6. Pointeurs et tableaux . . . . . . . .
7. Tableaux de pointeurs . . . . . . .
8. Choix multiples avec switch . . .
9. dition de liens . . . . . . . . . .
10. Utilit des prototypes . . . . . . .
11. Utilit des librairies : 1+1=1 . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

177
177
178
179
180
180
183
187
189
191
192
193

15 Exercices
1. Objectifs . . . . . .
2. Jeu de morpion . .
3. Jeu de pendu . . . .
4. Balle rebondissante
5. Solutions . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

197
197
197
199
201
203

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

Petit extrait de la table Ascii

211

Bon savoir

213

Вам также может понравиться