Vous êtes sur la page 1sur 68

Universit de Savoie

Module ETRS-505

Cours
Initiation la programmation en C

Sylvain MONTAGNY
sylvain.montagny@univ-savoie.fr
Btiment chablais, bureau 13
04 79 75 86 86

Source du cours : Yann Laurillau Yann.Laurillau@imag.fr

Retrouver tous les documents de Cours/TD/TP sur le site


www.master-electronique.com

1.

Introduction .......................................................................................................... 4
1.1. Petite histoire du C............................................................................................................ 4
1.2. Outils pour programmer en C ........................................................................................... 4
1.3. Quel environnement de travail ? ....................................................................................... 4
1.3.1 Comment faire sous Linux (version Redhat 9 en franais)....................................... 4
1.3.2 Comment faire sous Mac OS X ? ............................................................................. 6
1.4. Programmer en C : comment a marche ? ........................................................................ 6
1.5. Programmer en C : quoi a ressemble ?......................................................................... 6
1.6. La fonction printf() ........................................................................................................... 8
1.7. Lecture formate de donnes ............................................................................................ 9
1.8. Systmes de numration ................................................................................................. 10

2.

Variables et oprateurs arithmtiques ............................................................. 12


2.1.
2.2.
2.3.
2.4.
2.5.
2.6.

3.

La dclaration de variables ............................................................................................. 12


Laffectation dune valeur constante .............................................................................. 12
Manipuler une variable (II) : les oprateurs arithmtiques............................................. 13
Priorit des oprateurs..................................................................................................... 14
Dclaration et affectation simultanes ............................................................................ 15
Les oprateurs de manipulation de bits........................................................................... 15

Types lmentaires et conversion de type ........................................................ 17


3.1. Les types lmentaires .................................................................................................... 17
3.2. Reprsentation des nombres ........................................................................................... 17
3.2.1 Les nombres entiers ................................................................................................ 17
3.2.2 Les nombres rels ................................................................................................... 18
3.2.3 Le type caractre ..................................................................................................... 19
3.3. La conversion de type ..................................................................................................... 20

4.

Expressions conditionnelles et oprateurs de comparaison ........................... 21


4.1. Si-Alors-Sinon ................................................................................................................ 21
4.2. Oprateurs de comparaison et logiques .......................................................................... 21
4.2.1 Oprateurs de comparaison..................................................................................... 21
4.2.2 Oprateurs logiques ................................................................................................ 23
4.3. Switch ............................................................................................................................. 24

5.

Boucles et itrations............................................................................................ 26
5.1. While et do-while............................................................................................................ 26
5.1.1 La boucle While...................................................................................................... 26
5.1.2 La boucle Do-While................................................................................................ 26
5.2. Boucle for........................................................................................................................ 27
5.3. Imbrication de boucles.................................................................................................... 27
5.4. Mots-cl break ................................................................................................................ 28

6.

Fonctions et procdures ..................................................................................... 29


6.1.
6.2.
6.3.
6.4.
6.5.

7.

Tableaux et chanes de caractres..................................................................... 33


7.1.
7.2.

8.

Les fonctions................................................................................................................... 29
Les prototypes et les appels inter-fonctions.................................................................... 30
Les variables globales ..................................................................................................... 30
Le type void et les procdures ........................................................................................ 31
Variables globales et porte des identificateurs.............................................................. 31
Les tableaux .................................................................................................................... 33
Les chanes de caractres ................................................................................................ 34

Pointeurs.............................................................................................................. 35
2

8.1. Gnralits sur les pointeurs ........................................................................................... 35


8.2. Oprateur & : adresse de................................................................................................. 36
8.3. Oprateur * : valeur de.................................................................................................... 36
8.4. Une valeur de pointeur particulire : NULL................................................................... 37
8.5. Pointeur de pointeur........................................................................................................ 38
8.6. Pointeur et paramtres de fonctions............................................................................... 39
8.6.1 Passage par rfrence.............................................................................................. 39
8.7. Pointeurs et tableaux ....................................................................................................... 40
8.7.1 Tableau de pointeurs ............................................................................................... 40
8.7.2 Analogie entre tableau et pointeur .......................................................................... 41
8.7.3 Passage par rfrence dun tableau en paramtre dune fonction........................... 42
8.7.4 Arithmtique de pointeurs et notion de dcalage (offset) ....................................... 44
8.7.5 Oprateurs de comparaison..................................................................................... 44

9.

Dfinition de types, types numrs.................................................................. 45


9.1.
9.2.

10.

Structures ........................................................................................................ 47
10.1.
10.2.
10.3.
10.4.
10.5.
10.6.

11.

Erreurs frquentes ....................................................................................................... 60

Fichiers et E/S ................................................................................................. 61


13.1.

14.

Fonctions dattribution et de libration dynamique de la mmoire ............................ 55


Demande d'allocation mmoire (malloc) :.................................................................. 55
Libration mmoire (free) :......................................................................................... 56
Demande d'allocation mmoire pour tableau (calloc) : .............................................. 57
Tableaux multiples : allocation et libration............................................................... 57
Erreurs frquentes ....................................................................................................... 58

Manipulation des chanes de caractres....................................................... 59


12.1.

13.

Gnralits .................................................................................................................. 47
Structure et typedef ..................................................................................................... 48
Structures imbriques.................................................................................................. 49
Structures et tableaux.................................................................................................. 50
Structures et pointeurs................................................................................................. 51
Structures rcursives ................................................................................................... 53

Gestion dynamique de la mmoire ............................................................... 55


11.1.
11.2.
11.3.
11.4.
11.5.
11.6.

12.

Typedef ........................................................................................................................... 45
type numr : dfinition de constantes symboliques..................................................... 45

Les fichiers.................................................................................................................. 61

Prprocesseur, headers et programmation multi-fichiers.......................... 67


14.1.
Prprocesseur .............................................................................................................. 67
14.1.1
Dfinition de constante ....................................................................................... 67
14.1.2
Usage des macros................................................................................................ 67
14.1.3
Inclusion de fichiers en-tte (headers)................................................................ 68
14.1.4
Erreurs frquentes ............................................................................................... 68

1. Introduction
Le langage C est un langage de programmation qui peut, comme son nom lindique, produire des
programmes, cest--dire des logiciels. Une grande partie de ceux que nous utilisons, comme Linux,
sont crits dans le langage C.

1.1. Petite histoire du C


Ce langage a t invent par Kernighan et Ritchie au dbut des annes 70 (1972) dans les
laboratoires Bell Labs et a t utilis pour dvelopper le systme UNIX. Il a t cr pour tre
efficace et offrir une programmation plus volue que la programmation dans le langage assembleur
(plus fastidieuse). Pour la petite histoire, le langage C est une volution du langage B. Depuis, le
langage C lui- mme a connu une volution vers la programmation par objet avec le C++. On trouve
galement des variations avec le langage Java et le langage C# (prononcer si-charp) imagin par
Microsoft. Cest le langage de prdilection dun grand nombre de dveloppeurs et il est loin dtre
abandonn.

1.2. Outils pour programmer en C


Les outils utiliser pour programmer en C sont : un diteur de texte (qui peut tre aussi simple que
le bloc-note) et un compilateur. Il existe de nombreux compilateurs, payants pour la plupart. Sous
Linux, on dispose de gcc, compilateur C gratuit install en standard par la majorit des distributions.
Cest galement ce compilateur qui est utilis sur les ordinateurs Apple sous MacOS X.
Il existe galement des logiciels qui intgrent deux logiciels et que lon nomme environnement de
dveloppement (IDE). Cest le cas de Visual C/C++ de Microsoft ou de Borland C (plus
anciennement, Turbo C).

1.3. Quel environnement de travail ?


1.3.1 Comment faire sous Linux (version Redhat 9 en franais)
Pour diter un programme C, il est possible d'utiliser un diteur de texte tel que XEmacs. Pour
compiler, on utilise le compilateur gcc en ligne de commande partir dun terminal, accessible
depuis le menu principal de l environnement de travail.
Toutes les compilations seront effectues depuis la ligne de commande (shell). Il est cependant
ncessaire de connatre les commandes lmentaires pour manipuler les fichiers dans un shell Unix
dont voici la liste et des exemples dutilisation :
Format
ls
ls -l
cd
cp
rm
mkdir
rmdir
rm -rf
mv
pwd

Action
Affiche la liste des fichiers et rpertoires
idem mais avec plus de dtails
Change le rpertoire courant
Copie un fichier
Supprime un fichier
Cre un rpertoire
Supprime un rpertoire vide
Supprime un rpertoire et son contenu
Dplace un fichier ou un rpertoire
Indique le chemin pour accder au rpertoire courant

Exemple

cd src/
cp exercice.c programme.c
rm programme.c
mkdir cours
rmdir cours
rm rf cours
mv exercice.c programme.c
pwd

Exemple : sortie de la commande ls l

1.
2.
3.
4.
5.
6.
7.
8.

$ ls -l
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
-rw-r--r--rw-r--r--rw------$

1
1
1
1
1
1

yalau
yalau
yalau
yalau
yalau
yalau

yalau
yalau
yalau
yalau
yalau
yalau

128 sep 8 15:06 .


128 sep 8 15:06 ..
128 sep 8 15:06 code
208 sep 8 15:06 exercice_I_3.c
1790 sep 8 15:06 linux-conf.tar
53760 sep 8 15:06 Uds-T0-9-1.doc

Signification des informations du ls l :


Premire colonne : la premire lettre
o Le d indique quil sagit dun rpertoire (directory).
o Le indique quil sagit dun fichier.
o Le l indique quil sagit dun raccourci (link).
Premire colonne : trois blocs de trois lettres
o Indique les droits daccs pour le propritaire du fichier/rpertoire (premier bloc),
pour les membres du groupe (second bloc), pour tous les autres (dernier bloc). Si un
droit nest pas accord, un tiret apparat.
o r : droit en lecture (Read).
o w: droit en criture (Write).
o x : droit en excution (eXecute).
La troisime colonne indique le nom du propritaire du fichier/rpertoire
La quatrime colonne indique le groupe associ au fichier/rpertoire
La cinquime colonne indique la taille en octets du fichier
Comme on peut le constater, il existe deux rpertoires spciaux . et .. qui reprsentent
respectivement le rpertoire courant et le rpertoire parent dans la hirarchie. Il existe un autre
rpertoire spcial not ~ qui symbolise le rpertoire personnel (home directory). Ces notations ., .. et
~ peuvent tre utilises avec les autres commandes de manipulation de fichiers et rpertoires tels que
cd ou mv. Enfin, un rpertoire est identifi par le chemin qui permet dy accder dans la hirarchie.
Ce chemin est constitu des noms des rpertoires parents spars par un / (que lon nomme
sparateur). Un / seul dsigne la racine de cette hirarchie (root). Par exemple, la commande pwd
produit dans ce format le chemin du rpertoire courant.
Exemple : utilisation de la commande pwd

1.
2.
3.
4.
5.
6.
7.
8.

$ pwd
/home/syscom/yalau
$ cd /
$ pwd
/
$ cd home
$ pwd
/home

Sous windows, il est possible d'installer l'environnement cygwin qui reproduit lenvironnement
UNIX avec les terminaux et les diffrents shell pour l'accs la ligne de commande. Au sein de cet
environnement, il est galement possible d'installer gcc et ainsi reproduire l'identique
l'environnement dont vous disposez sous Linux (http://www.mingw.org )
Il existe galement les environnements de dveloppement du type Visual C++, Borland C ou autre
qui vous permettent de compiler un programme. Toutefois, il faut faire attention car ces
environnements peuvent prendre en compte une version non normalise du langage.

1.3.2 Comment faire sous Mac OS X ?


Il s'agit d'un UNIX et ce qui est relatif Linux sapplique Mac OS X. Par contre il est ncessaire
d'installer les paquets de dveloppement (logiciel XCode) qui contiennent une version de gcc pour
Mac OS X. Normalement, les manipulations en ligne de commande sont identiques Linux. Il est
galement possible de travailler directement avec qui est un environnement de dveloppement
complet.

1.4. Programmer en C : comment a marche ?


Programmer en C, cest dabord crire un document textuel (le code du programme) conforme un
ensemble de rgles, dfini sous forme de grammaire. Par convention, le code du programme est
enregistr dans un fichier avec lextension .c. Une fois le programme crit, il doit tre compil
laide dun outil : le compilateur. Le rle dun compilateur est danalyser le code et de le transformer
en instructions binaires qui pourront tre ensuite excutes par le processeur. Si le code que vous
avez crit contient des erreurs dcriture, cest--dire qui nest pas conforme aux rgles du langage
C, le compilateur ne produira pas de binaire et vous affichera des messages derreur vous permettant
ensuite de localiser les erreurs et de les corriger.
Voici le principe gnral de la compilation :

01010101
10101010
bibliothque
01010101
10101010

fichier.c

Compilateur
excutable

Utilisation de gcc :
Pour compiler sous Linux avec gcc, voici la forme gnrale :
gcc Wall o <nom de lexcutable> <fichier compiler>

Il existe bien sr dautres options dont certaines seront dtailles ultrieurement.


Exemple : gcc Wall o mon_programme exemple.c
Pour lancer lexcutable mon_programme :
./mon_programme

1.5. Programmer en C : quoi a ressemble ?


Etudions un exemple trs simple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

#include <stdio.h>
/* Calcule et affiche le cube des nombres de 1 a 5 */
int main(void)
{
int nombre, cube;
nombre = 1;
while (nombre <=
{
cube = nombre *
printf("Le cube
nombre = nombre
}

5)
nombre * nombre;
de %d est %d\n", nombre, cube);
+ 1;

return 0;
}

Anatomie du programme :
Ce programme calcule et affiche le cube des nombres compris entre 1 et 5.
1) Il s'agit d'une srie d'instructions, ou commandes (on parle de bloc d'instructions), dlimites
deux accolades. En pascal, cela correspond Begin/End
2) Une directive de prcompilation : il s'agit dune commande pour le prprocesseur. Sans
entrer dans le dtail, il s'agit d'indiquer que le code repose sur lutilisation de fonctions
standards d'entre et de sortie, savoir, ici, afficher du texte dans le terminal (STD =
standard, IO = Input/Output). C'est ce qui est fait avec l'appel de fonction printf dans la
suite du programme.
3) Un commentaire : a ne produit pas de code binaire mais c'est trs utile (et indispensable)
pour documenter le code du programme. Un commentaire commence toujours par un /* et se
termine par un */. Entre les deux, on peut y mettre n'importe quoi (y compris du code qui ne
sera pas compil).
Par exemple:
/* commentaire */
/* commen /* taire */
/*
** commentaire
*/

4) Une fonction : Une fonction est un bloc dinstruction qui est dsigne par un nom, une valeur
de retour et une dclaration darguments entre parenthse. Le mot-cl void indique quil ny
a pas dargument. Lintrt des fonctions est de pouvoir excuter plusieurs fois un mme
bloc dinstruction sans avoir le rcrire dans le programme. Cette fonction se nomme main
et est particulire car elle est indispensable pour que le programme compile correctement et
puisse tre excut : il sagit du point dentre du programme.
5) Une dclaration de variables : une variable est une zone mmoire qui permet de sauvegarder
une valeur. Pour chaque variable, il est ncessaire de prciser la nature de cette valeur, quon
appelle type. Dans notre, exemple, il sagit de deux variables, nombre et cube, qui nous

permettent de manipuler deux nombres entiers (int pour integer). Il existe dautres types
comme les nombres rels.
6) Une affectation : on mmorise dans la variable nombre la valeur 1. Cette variable est utilise
dans la suite pour calculer son cube.
7) Une boucle avec condition : une boucle permet de rpter plusieurs fois un bloc
dinstruction. Dans notre exemple, le bloc est rpt autant de fois que la valeur mmorise
par la variable nombre nest pas suprieure 5. Ceci nous permet de calculer donc le cube
des nombres de 1 5.
8) Une expression et une affectation : dans cette instruction on calcule le cube en multipliant
trois fois le contenu de la variable nombre laide de loprateur arithmtique *. Le rsultat
de cette expression est mmoris dans la variable cube.
9) Une commande daffichage : cette instruction affiche le message le cube de X est Y . le %d
indique que lon va imprimer un nombre entier. A chaque %d, on doit associer une variable
contenant un entier et, laffichage, le %d est substitu par la valeur entire contenue dans
cette variable. Dans notre exemple, nous avons deux %d et nous utilisons les variables nombre
et cube. Le \n indique que le message doit se terminer par un retour la ligne.
10) Une expression et une affection : cette instruction permet de passer au nombre suivant en
ajoutant 1 et en mmorisant la nouvelle valeur dans la variable nombre.
11) Un retour : il sagit dune instruction particulire retournant une valeur pour la fonction
main. Pour plus dinformation, se reporter la partie sur les fonctions.
Point important : toutes les instructions et dclarations se terminent systmatiquement par un pointvirgule.

1.6. La fonction printf()


La fonction printf est linstruction indispensable de tout programmeur en C et permet l'criture
formate sur le flux standard de sortie stdout (l'cran par dfaut). Le premier paramtre de cette
fonction est toujours une chane de caractres dlimites par deux guillemets ". Cette fonction peut
prendre ensuite un nombre de paramtre qui est gale au nombre de variable quon a demander
afficher.
printf("chane de caractre", <expression1>, [], <expressionN>);

Le caractre \n indique le retour ligne. Dune manire gnrale, tous les caractres spciaux sont
prcds systmatiquement dun \ comme le \" qui dsigne un guillement (pour ne pas confondre
avec le guillemet utilis en fin et dbut de chane dans les paramtres du printf).
Exemple : utilisation de la fonction printf

1.
2.
3.
4.
5.
6.
7.
8.

#include <stdio.h>
int main(void) {
int nbre = 5;
long prix = 12;

printf("Bonjour\n");
printf("Nombre : %d Prix : %ld Total : %ld \n",nbre, prix, prix *
nbre);
9.
printf("\n");
10.
return 0;
11. }

Point important : pour utiliser la fonction printf, le programme doit systmatiquement comporter
la directive de compilation #include <stdio.h> au tout dbut du programme comme cela figure
dans lexemple comment.
Lensemble des spcificateurs de format sont rassembls dans le tableau suivant :
Format
%d
%ld
%u
%lu
%o
%lo
%x
%lx
%f
%lf
%e
%le
%g

Type
int
long int
unsigned int
unsigned long int
unsigned int
unsigned long int
unsigned int
unsigned long int
double
long double
double
long double
double

Dtail
entier signe
entier signe
entier non signe
entier non signe
octale non signe
octale non signe
hexadcimale non signe
hexadcimale non signe
dcimale virgule fixe
dcimale virgule fixe
dcimale notation exponentielle
dcimale notation exponentielle

%lg

long double

dcimale, reprsentation la plus courte parmi %lf et %le

%c

unsigned char

caractre

%s

char*

chane de caractres

dcimale, reprsentation la plus courte parmi %f et %e

1.7. Lecture formate de donnes


La fonction scanf est la fonction symtrique printf; elle nous offre pratiquement les mmes
conversions que printf, mais en sens inverse.
scanf("<format>",<AdrVar1>,<AdrVar2>, ...)

"<format>" : format de lecture des donnes


<AdrVar1>,... : adresses des variables auxquelles les donnes seront attribues
La fonction scanf reoit ses donnes partir du fichier d'entre standard stdin (par dfaut le clavier).
Les donnes reues correctement sont mmorises successivement aux adresses indiques par
<AdrVar1>,... .L'adresse d'une variable est indique par le nom de la variable prcd du signe &.

Exemple : utilisation de la fonction scanf

1.
2.
3.
4.
5.
6.
7.
8.
9.

#include <stdio.h>
int main(void) {
int jour, mois, annee;
scanf("%d %d %d", &jour, &mois, &anne);
return 0;
}

Cet exemple lit trois entiers, spars par des espaces, tabulations ou interlignes. Les valeurs sont
attribues respectivement aux trois variables : jour, mois et anne.

1.8. Systmes de numration


Sur les ordinateurs, linformation est stocke et traite sous forme dune suite constitue de 0 et 1,
cest--dire en binaire. Aussi, les informaticiens et les programmeurs ont lhabitude de manipuler les
nombres en base 2 ou dans toutes autres bases dune puissance de 2 comme loctal (base 8) ou
lhexadcimal (base 16). Trs souvent, ceci nous oblige faire des conversions entre le systme
dcimal (base 10) et la base 8 ou la base 16 par exemple.
Pour reprsenter un nombre en base 10, on utilise les chiffres de 0 9.
Pour reprsenter un nombre en base 2, on utilise les chiffres 0 et 1.
Pour reprsenter un nombre en base 16, on utilise les chiffres de 0 9 et les lettres de A F
(avec A = 10, B = 11, C = 12, D = 13, E = 14 et F = 15)
Exemples : criture du nombre 237 dans les diffrentes bases
En base 10 : 237

= 2 * 102 + 3 * 101 + 7 * 100


= 200 + 30 + 7

En base 2 : 11101101 = 1 * 27 + 1 * 26 + 1 * 25 + 0 * 24 + 1 * 23 + 1 * 22 + 0 * 21 + 1 * 20
= 128 + 64 + 32 + 0 + 8 + 4 + 0 + 1
En base 16 : 0xED

= E * 161 + D * 160
= 14 * 16 + 13 * 1
= 224 + 13

Mthode pour convertir un nombre dcimal en binaire : A partir du nombre dcimal, il faut
soustraire les puissances de 2 dans lordre dcroissant jusqu ce que lon obtienne 0. On note 1 si on
utilise une certaine puissance de 2 et 0 dans le cas contraire. Au final, on doit obtenir dans sa
reprsentation binaire.
Exemple : convertir 163 en binaire
128
1 163 128 = 35
64
0
32
1 35 32 = 3
16
0
8
0
4
0
2
1 32=1
1
1 11=0
= 10100011

10

Mthode pour convertir un nombre binaire en hexadcimal : on regroupe, partir du dernier


chiffre, les nombres binaires en bloc de 4 chiffres. Sil manque des chiffres pour former le dernier
bloc, le bloc est complt avec des 0. Pour chaque bloc on regarde la valeur entre 0 et 16 pour
dterminer la reprsentation hexadcimale.
Exemple : convertir 11010100011
0
1
1
0
=0*8+1*4+1*2+0*1
=6
6

1
0
1
0
=1*8+0*4+1*2+0*1
= 10
A
= 0x6A3

0
0
1
1
=0*8+0*4+1*2+1*1
=3
3

11

2. Variables et oprateurs arithmtiques


2.1. La dclaration de variables
Une variable est une zone mmoire qui permet de stocker des valeurs. Avant dutiliser une
variable, il est ncessaire de la dclarer, cest--dire choisir un nom et un type. Le type est la
nature de la variable.
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.

int main(void)
{
int x;
int y, nombre;
[]
return 0;
}

Dans notre exemple, les variables sont de type entier (int), cest--dire que les valeurs de x, y et
nombre seront des nombres entiers. La syntaxe pour dclarer une ou plusieurs variables de mme
type est (notez le point-virgule en fin de ligne) :
<type> <variable1>, <variable2>, [], <variableN>;

En C, les dclarations de variable se font systmatiquement en dbut de bloc dinstruction,


cest--dire juste aprs une accolade ouvrante {.
Il est possible de dclarer des variables sur plusieurs lignes en dbut de bloc, mme si elles sont du
mme type.
Remarque : Le mot-cl const signifie que la variable est constante donc non modifiable sauf
linitialisation.

2.2. Laffectation dune valeur constante


Une variable ne se contente pas de stocker une valeur, il est galement possible den modifier son
contenu laide dune affectation. Pour cela, on utilise un oprateur particulier, loprateur
daffectation reprsent par un = et la syntaxe est :
<variable> = <valeur>;
Il est bien sr possible de rutiliser une variable et den changer successivement le contenu.
Exemple :

12

1.
2.
3.
4.
5.
6.
7.
8.
9.

int main(void)
{
int nombre;
nombre = 28;
nombre = 2;
return 0;
}

2.3. Manipuler une variable (II) : les oprateurs arithmtiques


Laffectation dune variable ne se limite pas aux valeurs constantes. Il est possible daffecter le
rsultat dune expression arithmtique laide doprateurs mathmatiques. La syntaxe est
identique :
<variable> = <expression>;
Pour cela, on dispose de plusieurs oprateurs arithmtiques :
Op.
+
*
/
%
++
-+=
-=
*=
/=
%=

Fonction
Exemple
Addition
calcul = nombre + 5;
Soustraction
calcul = 10 nombre;
Multiplication
calcul = 3 * nombre;
Division
calcul = nombre / 2;
Modulo
calcul = nombre % 3;
Incrmentation
nombre++; quivalent nombre = nombre + 1;
Dcrmentation
nombre--; quivalent nombre = nombre - 1;
Addition et affectation
nombre += 5;
Soustraction et affectation
nombre -= 6;
Multiplication et affection
nombre *= 3;
Division et affectation
nombre /= 2;
Modulo et affectation
nombre %= 4;
<variable> op= <expr>; est quivalent <variable> = <variable>op<expr>;
Par exemple :
nombre += 5; est quivalent nombre = nombre + 5;

Point important : dans une expression, il est possible dutiliser en mme temps des valeurs
constantes et plusieurs variables. De plus, comme en mathmatique, il est possible dutiliser des
parenthses.
Rgles de priorit : il existe des rgles de priorit entre ces oprateurs. Dans une expression, la
multiplication, la division et le modulo sont prioritaires sur laddition et la soustraction. Ces priorits
sont identiques au modle mathmatique.
Exemple :

13

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.

#include <stdio.h>
int main(void)
{
int nombre1, nombre2, calcul;
nombre1 = 10;
nombre2 = 4;
nombre2++;
calcul = 3 * (nombre1 + nombre2) - 50;
nombre1 *= nombre2;
nombre2 += nombre2;
printf("%d %d %d \n", nombre1, nombre2, calcul);
return 0;
}

2.4. Priorit des oprateurs


Les diffrents oprateurs qui ont t vus jusqu' prsent doivent agir selon des rgles de priorit.
Lorsqu'une instruction combine plusieurs oprateurs de mme priorit, le langage C dfinit alors un
ordre de priorit de droite gauche ou, le plus souvent, de gauche droite. Le tableau ci-dessous
donne la liste des oprateurs par ordre dcroissant de priorit.
Priorit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Exemples :
o a = b + c *
o c - d + e
o a = b = c =
o x = y++;
o x = ++y
o x >= 0 && y
o (x == 0) ||
est quivalent
(x == 0) ||
o

Oprateurs
() [] . ->
! ~ ++ -- - + (<type>) sizeof
* (pointeur) & (pointeur)
* / %
+ << >>
< <= > >=
== !=
&
^
|
&&
||
? :
= += -= etc
,
est quivalent
est quivalent
0
est quivalent
est quivalent
est quivalent
>= 0 est quivalent
(y == 1) && (x == 2)

Sens
gauche droite
droite gauche
gauche
gauche
gauche
gauche
gauche
gauche
gauche
gauche
gauche
gauche
droite
droite
gauche

droite
droite
droite
droite
droite
droite
droite
droite
droite
droite
gauche
gauche
droite

a = (b + (c * d))
(c - d) + e
a = (b = (c = 0))
x = y; y++;
++y; x = y;
(x >= 0) && (y >= 0)

((y == 1) && (x == 2))

(y == 1) && (x == 2) || (x == 0)
est quivalent
((y == 1) && (x == 2)) || (x == 0)
14

Par contre, il existe des combinaisons qui produisent des effets de bords mais dont l'ordre est tout de
mme dterminable :
o

a || b++

a && b++

o b n'est pas incrment si a est vraie


o b n'est pas incrment si a est faux
Il existe des cas non prvus par la grammaire du langage C :
o a[i] = i++;
o a = i++ * i++;
o b = (f() + g()) * h()

o On ne sait pas dans quel ordre sont appeles f() et g(). Voici une solution:
tf = f();
tg = g();
th = h();
b = (tf + tg) / th;

2.5. Dclaration et affectation simultanes


Le langage C autorise la dclaration et laffectation simultanes des variables. La syntaxe devient
alors :
<type> <variable1> = <expression>, [], <variableN> = <expression>;
Exemples :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

#include <stdio.h>
int main(void)
{
int n = 10;
int m = 5 * n, l = m + n + 5;
printf("%d %d %d\n", n, m, l);
return 0;
}

2.6. Les oprateurs de manipulation de bits


Il existe des oprateurs permettant de faire des oprations arithmtiques directements sur les bits.
Ces oprateurs disposent dune version combine avec laffectation.

15

Oprateur
<<
>>
&
|
^
~

Signification
Dcalage gauche
Dcalage droite
ET bit bit
OU bit bit
OU Exclusif bit bit
Complment (dpendent du type)

Exemples
(0x15<<3) quivalent 10101000 (0xA8)
(0x15>>2) quivalent 101 (0x5)
(0x15 & 0x1C) quivalent 10100 (0x14)
(0x15 | 0x1C) quivalent 11101 (0x1D)
(0x15 ^ 0x1C) quivalent 1001 (0x9)
~0x15 quivalent 11101010 (0xEA)

<<=
>>=
&=
|=
^=

Dcalage gauche et affectation


Dcalage droite et affectation
ET bit bit et affectation
OU bit bit et affectation
OU Exclusif bit bit et affectation

n = 0x15;
n <<= 3;
n >>= 2;
n &= 0x1C;
n |= 0x1C;
n ^= 0x1C;
0x15 = 00010101 et 0x1C = 00011100

Les rgles pour les oprateurs &, | et ^ sont :


0 & 0 = 0
0 | 0 = 0
0 ^ 0 = 0

1 & 0 = 0
1 | 0 = 1
1 ^ 0 = 1

0 & 1 = 0
0 | 1 = 1
0 ^ 1 = 1

1 & 1
1 & 1
1 ^ 1

= 1
= 1
= 0

16

3. Types lmentaires et conversion de type


3.1. Les types lmentaires
Il existe trois types lmentaires qui se dcomposent en sous catgories :
Types
Dclaration C
Caractre
char
unsigned char
Nombres entiers
short int (ou short)
unsigned short int
int
unsigned int
long int (ou long)
unsigned long int
Nombres rels
float
double
long double

Nature

Codage

Valeurs

Caractre
Caractre non sign

1 octet
1 octet

-128 127
0 255

Entier positif ou ngatif


Entier positif uniquement
Entier positif ou ngatif

[-32768, 32767]
[0, 65535]
[-231, 231-1] si 4
octets
[0, 232-1] si 4 octets

Entier positif ou ngatif


Entier positif uniquement

2 octets
2 octets
2 (sur processeur 16 bits)
4 (sur processeur 32 bits
2 (sur processeur 16 bits)
4 (sur processeur 32 bits
4 ou 8 octets
4 ou 8 octets

6 chiffres significatifs
16 chiffres significatifs

4 octets
8 octets

(a dpend)

10/16 octets

Entier positif uniquement

[-231, 231-1]
[0, 232-1]
[-3.4 1038, 3.4 1038]
[-1.7 10308, 1.7
10308]
231 = 2 147 483 648
232 = 4 294 967 296

3.2. Reprsentation des nombres


3.2.1 Les nombres entiers
Toute valeur entire est reprsente par une srie de chiffres de 0 9. Cependant, il existe dautres
faons de reprsenter une valeur : en octal (base 8) et en hexadcimal (base 16). De plus, il est
possible dutiliser les suffixes L et U lorsque lon veut indiquer que cette valeur correspond un
entier long et un entier non sign.
Exemple : reprsentation de la valeur 58
normal
Dcimal
Base Octal
Hexadcimal

58
072
0x3A

Catgorie dentier
long
non sign
58L
072L
0x3AL

58U
072U
0x3AU

long et non
sign
58UL
072UL
0x3AUL

17

1. int main(void)
2. {
3.
int entier;
4.
long int entierLong;
5.
unsigned int entierPositif;
6.
unsigned long int entierLongPositif;
7.
8.
entier = 0x3A;
9.
entierLong = 58L;
10. entierPositif = 072U;
11. entierLongPositif = 0x3AUL;
12.
13. return 0;
14. }

Pour afficher une valeur entire lcran laide la fonction printf, nous avons vu quil fallait
utiliser la chane de formatage %d. En fait cela varie selon le type dentier.
Chane de formatage
%d
%u
%ld
%lu
%X (en hexadecimal)
%lX (en hexadecimal)

Catgorie dentier
int ou short int
unsigned int ou unsigned short int
long int
unsigned long int
int ou unsigned int
long int ou unsigned long int

Exemple : On veut afficher les valeurs de lexemple prcdent

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

#include <stdio.h>
int main(void)
{
printf("entier
printf("entier
printf("entier
printf("entier
printf("entier

positif
long
long positif
en hexadecimal

:
:
:
:
:

%d\n", entier);
%u\n", entierPositif);
%ld\n", entierLong);
%lu\n", entierLongPositif);
%X\n", entier);

return 0;
}

Attention : il est possible daffecter une valeur ngative un entier non sign. La valeur sera
convertie en une valeur positive.

3.2.2 Les nombres rels


Les nombres rels sont constitus dune partie avec virgule et dune partie exposant. La partie
exposant est toujours prfixe par la lettre E ou e. Si lon veut prciser quil sagit dun nombre rel
de type flottant (float) ou un nombre rel double prcision (long double), on ajoute le suffixe F
(ou f) ou L (ou l). Pour afficher un nombre rel avec la fonction printf, on utilise principalement
les chanes de formatage %f. Il existe bien sr dautres faons dafficher des nombres rels avec la
fonction printf.

18

Exemple : le nombre rel 0.01234 peut scrire de diffrentes faons :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

#include <stdio.h>
int main(void)
{
float f1, f2, f3;
f1 = 0.01234;
f2 = .01234;
f3 = 12.34e-3;
printf("affichage dun flottant : %f\n", f1);
return 0;
}

3.2.3 Le type caractre


Le type caractre (char) est un nombre entier un peu particulier car il permet de reprsenter une
lettre ou des symboles comme les chiffres. Du point de vue de lentier, une variable de type char
reprsente une valeur entire comprise entre 128 et 127 ou entre 0 et 255 sil est non sign.
Du point de vue des caractres, il se trouve que les lettres affiches lcran sont dsignes par des
codes (une valeur entire) dfinies dans une table, la table ASCII (American Standard Code for
Information Interchange) et que celles-ci ont pour taille 1 octet (la taille du type char).
Pour afficher une lettre ou un symbole laide de la fonction printf, il est possible dutiliser soit %d
(type entien : code de la lettre) ou %c (type caractre : le caractre lui-mme).
Exemple : la lettre A majuscule est dsigne par le code 65

En C, il nest pas toujours ncessaire de connatre la valeur ASCII dun caractre en utilisant
directement celui-ci entre deux guillemets simples.
Exemple : deux exemples diffrents pour dsigner la lettre A dans une affectation et pour lafficher lcran.
On doit observer que le rsultat est identique pour les deux formes daffectation.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

#include <stdio.h>
int main(void)
{
char c1, c2;
c1 = 65; /* code de la lettre A */
c2 = 'A'; /* accs direct au caractre */
printf("affichage du code : %d %d\n", c1, c2);
printf("affichage du caractere : %c %c\n", c1, c2);
return 0;
}

Il existe galement des codes pour dsigner les caractres non imprimables comme le retour la
ligne, dsign par le code de valeur 13 ou par le caractre '\n', trs utilis en programmation.

19

3.3. La conversion de type


La conversion de type consiste transformer un type de valeur en un autre. Dans certains cas, il est
ncessaire de rendre explicite cette conversion, notamment pour les affectations, en respectant la
syntaxe suivante :
(<type>) <expression>
Les oprateurs (addition), - (soustraction), * (produit), / (division), nccsite deux arguments mme
type, le rsultat sera toujours du type des oprandes. Lorsque les deux oprandes sont de type
diffrent (mais numrique videment), le compilateur prvoit une conversion implicite (vous ne
l'avez pas demande mais il la fait nanmoins) suivant l'ordre :
{ char -> int -> long -> float -> double } et { signed -> unsigned }
Les calculs arithmtiques sont faits uniquement soit en long soit en double, pour viter des
dpassements de capacit.
Exemples :

1.
2.
3.
4.
5.
6.
7.

int main(void)
{
int n, m, l;
double d;
d = 5;
// conversion implicite dun entier en un rel
n = (int) 3.4; // conversion explicite dun rel en entier avec
arrondi

8.
9.
n = 1;
10. m = 5;
11. d = 2;
12. l = (int) (m / d);// l = 2 car 5/2.0 = 2.5 mais arrondi 2
13. d = n / m;
// d = 0.0 car division entire 1 / 5
14. d = n / ((double) m); // d = 0.2 car division relle 1 / 5.0
15. d = 'A' * 6.0 m + 0xA2CL;
16.
17. return 0;
18. }

20

4. Expressions conditionnelles et oprateurs de


comparaison
4.1. Si-Alors-Sinon
La construction Si-Alors-Sinon est linstruction conditionnelle la plus lmentaire en C et dans tous
les langages en gnral. La syntaxe est :
if (<expression conditionnelle>) { <instructions si condition vraie> }
else { <instructions si condition fausse> }

Si la condition est vraie alors cest le premier bloc dinstruction qui est excut, sinon (else) cest le
second bloc. Cependant, le second bloc est optionnel et la syntaxe devient :
if (<expression conditionnelle>) { <instructions si condition vraie> }
La valeur dune expression conditionnelle est de type entier qui vaut soit 1 si la condition est vraie et
qui vaut 0 dans le cas contraire. Cette expression repose sur lutilisation doprateurs de
comparaison et logiques.

4.2. Oprateurs de comparaison et logiques


Il existe deux catgories doprateurs pour les expressions conditionnelles : les oprateurs de
comparaison qui permettent la comparaison de valeurs ; les oprateurs logiques qui permettent la
combinaison de plusieurs expressions logiques.

4.2.1 Oprateurs de comparaison


Ces oprateurs sont des oprateurs binaires. Leur syntaxe est :
<expression1> <oprateur> <expression2>
Oprateur
<
>
==
!=
<=
>=

Signification
Infrieur stricte
Suprieur stricte
Egalit
Diffrent
Infrieur ou gal
Suprieur ou gal

Dtails
Vaut 1 si le membre de gauche est infrieur au membre de droite
Vaut 1 si le membre de gauche est suprieur au membre de droite
Vaut 1 si les deux membres sont gaux
Vaut 1 si les deux membres sont diffrents
Vaut 1 si le membre de gauche est infrieur au membre de droite
Vaut 1 si le membre de gauche est suprieur au membre de droite

Exemple :

21

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.

#include <stdio.h>
int main(void)
{
int a = 5;
float b = 3.6;
if (a < 2) { printf("a est inferieur a 2\n"); }
else { printf("a est superieur ou egal a 2\n"); }
if (b >= 1.5f) { printf("b est plus superieur ou egal a 1.5\n"); }
else { printf("b est inferieur a 1.5\n"); }
if (a < b) { printf("a est plus petit que b\n"); }
b = a;
if (a == b) { printf("a et b sont egaux\n"); }
if (a != b) { printf("a et b sont differents\n"); }
return 0 ;
}

A savoir :
Il est possible denchaner les instructions if-then-else. Par exemple :
if (<condition>) { <instructions> }
else if (<condition>) { <instructions> }
else if (<condition>) { <instructions> }
else { <instructions> }

Il existe une version sous forme doprateur ternaire du if-then-else que lon rencontre souvent
combin avec une affectation. La valeur de cette oprateur varie selon la valeur de lexpression
conditionnelle. Par contre, contrairement la forme classique if-then-else, le second et troisime
membres de loprateur ne sont pas des blocs dinstructions mais des expressions qui doivent
imprativement retourner une valeur. La syntaxe est :
(<expression conditionnelle>) ? <valeur si vrai> : <valeur si faux>
Exemple : utilisation de loprateur ternaire et son quivalent avec linstruction if-then-else
int main(void)
{
int a, b = 6;
a = ((b == 3) ? (b 2) : (b + 5));
return 0;
}

int main(void)
{
int a, b = 6;
if (b == 3) { a = (b 2); }
else { a = (b + 5); }
return 0;
}

Dans cet exemple, si la condition est :


Vraie, cest--dire que b est gal 3, alors on affecte le rsultat de lexpression (b - 2) la
variable a. En dautre terme, par substitution, lexpression est quivalente a = (b 2);
Fausse, cest--dire que b nest pas gal 3, alors on affecte le rsultat de lexpression
(b + 5) la variable a. En dautre terme, par substitution, lexpression est quivalente
a = (b + 5);
22

4.2.2 Oprateurs logiques


Ces oprateurs sont binaires sauf loprateur de complment qui est unaire. La syntaxe est :
<expression
<expression
<expression
! <expression

conditionnelle 1> && <expression conditionnelle 2>


conditionnelle 1> || <expression conditionnelle 2>
conditionnelle 1> ^ <expression conditionnelle 2>
conditionnelle>

Oprateur
&&

Signification
ET logique

||

OU logique

OU exclusif

Dtail
Vaut 1 si les deux conditions sont vraies (i.e. gales 1)
&&
Vrai
Faux
Faux
Vrai
Vrai
Faux
Faux
Faux
Vaut 1 si lune des deux conditions est vraie
||
Vrai
Vrai
Vrai
Faux
Vrai

Vaut 1 si uniquement une seule des deux conditions est vraie


^
Vrai
Faux

Complmentaire

Faux
Vrai
Faux

Vrai
Faux
Vrai

Vrai
Vrai
Faux

Vaut 1 si la condition est fausse et inversement

Exemple :

1. #include <stdio.h>
2.
3. int main(void)
4. {
5. int a;
6.
7. a = 2;
8. if ((a >= -5) && (a <= 5)) { printf("a est dans lintervalle [-5,5]\n"); }
9.
10.
if ((a < -5) || (a > 5))
11.
{
12.
printf("a est dans lintervalle ]-inf , 5[ ou ]5, + inf[\n");
13.
}
14.
15.
if (! (a < -5)) { printf("a nest pas dans lintervalle [-5, +inf[\n") ; }
16.
}
17.

A savoir (i) :
!
!
!
!

(a < 5) est quivalent (a >= 5)


(a != 0) est quivalent (a == 0)
(<c1> && <c2>) est quivalent ((! <c1>) || (! <c2>))
(<c1> || <c2>) est quivalent ((! <c1>) && (! <c2>))

A savoir (ii) :
Il est possible denchaner plusieurs expressions conditionnelles avec ces oprateurs. Par exemple :

23

1.
2.
3.
4.
5.
6.
7.
8.

int main(void)
{
int x = 50, y = -2, z = 0;
if ((x > 0) && (x < 100) && (y > 0) && (y < 100) && (z == 0)) { [] }
return 0;
}

A savoir (iii) :
Cependant, ces oprateurs logiques binaires sont dits paresseux puisque si une condition permet
de dterminer le rsultat de lexpression conditionnelle, le reste de lexpression nest pas valu.
Dans notre exemple, on sait que la troisime condition (y > 0) est fausse car y vaut 2. Aussi,
lexcution, les expressions (y < 100) et (z == 0) ne seront pas values.

4.3. Switch
Linstruction switch est une variante des enchanements de if-then-else mais la diffrence que les
expressions conditionnelles se rduisent une comparaison dentiers. La syntaxe est la suivante :
switch(<expression entire>)
{
case <valeur entire>:
<bloc dinstructions>
break;
case <valeur entire>:
<bloc dinstructions>
break;
[]
case <valeur entire>:
<bloc dinstructions>
break;
default:
<bloc dinstructions>
break;
}
Exemple :

24

1. int main(void)
2. {
3.
int n = 8;
4.
5.
switch(n)
6.
{
7.
case 2:
8.
printf("n est egal a 2\n");
9.
break;
10.
11.
case 8:
12.
printf("n est egal a 8\n");
13.
break;
14.
15.
default:
16.
printf("n nest egal ni a 2, ni a 8\n");
17.
break;
18. }
19.
20. return 0;
21. }

A chaque case, correspond une clause possible (case) que peut prendre lexpression entire.
Linstruction break indique la fin de la clause sachant quelle peut tre omise ( viter dans un
premier temps).
A laide de linstruction if-then-else, linstruction switch peut scrire :
1. int main(void)
2. {
3.
int n = 8;
4.
5.
if (n == 2) { printf("n est egal a 2\n"); }
6.
else if (n == 8) { printf("n est egal a 8\n"); }
7.
else { printf("n nest egal ni a 2, ni a 8\n"); }
8.
9.
return 0;
10. }
11.

25

5. Boucles et itrations
Les boucles et itrations offrent la possibilit de rpter et dexcuter en srie un bloc dinstructions
sans avoir le rcrire. Ces boucles et itrations sont excutes autant fois que la condition darrt
na pas t vrifie. Et parfois, les boucles tournent en rond !!!

5.1. While et do-while


5.1.1 La boucle While
La premire instruction de boucle est le while qui signifie tant que. La syntaxe est :
while (<expression conditionnelle>)
{
<instructions>
}

Le principe est le suivant : tant que la condition est vraie le bloc dinstruction est excut.
Exemple : La table de multiplication par 7.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.

#include <stdio.h>
int main(){
int i=0;
printf("Apprenons la table de 7\n\n");
while(i < 10){/* TANT QUE i < 10
*/
i++;/* repeter les instructions */
printf("%d x 7 = %d\n", i, i * 7);/* suivantes
}

*/

return 0;
}

5.1.2 La boucle Do-While


Il existe une autre forme de la boucle qui est de la forme faire-tant-que. Dans cette forme,
lexpression conditionnelle est value aprs lexcution du bloc dintruction. La syntaxe devient
alors :
do
{
<instructions>
} while(<expression conditionnelle>);

Dans certains cas, il peut tre utile dexcuter une fois le bloc dinstruction avant de raliser
lvaluation de lexpression conditionnelle.
Exemple : boucle do-while utilise lorsque lon ne peut pas prdire le nombre ditrations

26

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

#include <stdio.h>
int main(){
int age=32;
do{
printf("Quel est votre age ? ");
scanf("%d", &age);
}while(age < 18);
return 0;
}

5.2. Boucle for


La boucle for est une variante de la boucle while intgrant un compteur de boucle, fort utile lorsque
lon connat par avance le nombre ditration ncessaire. La syntaxe est la suivante :
for(<instructions>; <expression conditionnelle>; <instructions>)
{
<instructions>
}

Le premier membre du for offre un espace pour linitialisation de variables (en gnral le compteur
de boucle). Le second membre est rserv lexpression conditionnelle qui permet darrter la
boucle. Le dernier membre peut contenir nimporte quelle instruction. Il est en gnral rserv pour
lincrmentation de la boucle for. Les diffrents membres peuvent tre vides.
Point important : lvaluation de lexpression conditionnelle est ralise avant lexcution du corps
de la boucle et le dernier membre est excut aprs lexcution corps de la boucle.
Exemple : calcul et affichage de la somme des nombres de 1 10

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

#include <stdio.h>
/* Calcule et affiche la somme des nombres de 1 a 10 */
int main(void)
{
int i, somme = 0;
for(i = 1; i <= 10; i++) { somme += i; }
printf("la somme des nombres de 1 a 10 est %d\n", somme);
return 0;
}

5.3. Imbrication de boucles


Il est possible dimbriquer les boucles et dutiliser les variables de compteur au sein de ses boucles.
Le programme ci-dessous ralise laddition des indices de deux matrices 10x10.

27

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

#include <stdio.h>
int main(void)
{
int i, j, somme = 0;
for(i = 1; i <= 10; i++)
{
for(j = 1; j <= 10; j++)
{
somme += i + j;
}
}
printf("Somme des coefficients egale a %d\n", somme);
return 0;
}

5.4. Mots-cl break


Le mot-cl break, utilis dans une boucle, permet dinterrompre une boucle et den sortir mme si
lexpression conditionnelle darrt de boucle na pas t vrifie. Ce mot-cl est a utiliser avec
prudence.
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

#include <stdio.h>
int main(void)
{
int i = 1;
while(i < 10)
{
if (((i + 3) % 5) == 0) { break; }
i++;
}
printf("Arret a la valeur %d\n", i);
return 0;
}

28

6. Fonctions et procdures
6.1. Les fonctions
Les fonctions sont un moyen pour fragmenter le code dun programme afin de le rendre plus lisible.
Cette mthode consiste sparer des blocs dinstructions du reste du code et les remplacer par un
appel de fonction. A limage de la fonction en mathmatique, les fonctions en C peuvent prendre des
paramtres ncessaires lexcution du bloc dinstruction et doivent retourner une valeur. La
syntaxe est la suivante :
<type de retour> <nom de la fonction>([<type> <variable1>,]*)
{
<instructions>
return <expression du type de retour>;
}

On nomme en gnral prototype ou signature la premire ligne dune dclaration de fonction et de


corps de fonction pour dsigner le bloc dinstruction. Il convient dajouter que les paramtres sont
des copies et que lon ne travaille pas directement sur les variables utilises pour le passage de
paramtre. Ceci peut tre contourner avec lutilisation des pointeurs.
f : R 2 R
Exemple : Voici par analogie une fonction mathmatique,

( x, y ) 2 x 3 + y + 1

, et sa traduction dans le

langage C.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

#include <stdio.h>
float f(float x, int y)
{
return (2 * x * x * x + y + 1);
}
int main(void)
{
float u, valeur;
valeur = f(5.3, 2);
printf("la valeur de f(5.3, 2) est %f\n", valeur);
u = 2.2;
valeur = f(u, 5); /* appel de procdure */
printf("la valeur de f(%f, 5) est %f\n", u, valeur);
return 0;
}

Point important : il est autoris dutiliser plusieurs fois linstruction return pour interrompre
prmaturment lexcution de la fonction. Ceci est utiliser avec prcaution.

29

6.2. Les prototypes et les appels inter-fonctions


Dans certains cas, il est possible quune fonction appelle une fonction dont la dclaration intervient
la suite, dans le code du programme. Dans ce cas, il est ncessaire de dclarer le prototype de la
fonction avant de pouvoir lutiliser.
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.

#include <stdio.h>
/* Dclaration du prototype (noter le point-virgule) */
float f(int y);
int main(void)
{
float valeur;
valeur = f(11);
printf("le resultat de f(11) est %f\n", valeur) ;
return 0;
}
/* Dclaration complte de la fonction f */
float f(int y)
{
return (y * y + 2);
}

6.3. Les variables globales


Jusqu prsent, les variables taient considres comme tant locales un bloc dinstructions. Il est
cependant possible de dclarer des variables en dehors de ces blocs et qui peuvent tre manipules
par diffrentes fonctions. En gnral, la dclaration de ces variables globales est ralise avant la
dclaration des fonctions.
Cependant, les variables globales sont viter autant que possible.
Exemple :

30

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.

#include <stdio.h>
int compteur = 11;
int incrementer(int increment)
{
compteur += increment;
return compteur;
}
int main(void)
{
printf("le compteur vaut %d\n", compteur);
compteur = incrementer(3) + 2;
printf("le compteur vaut %d\n", compteur);
return 0;
}

6.4. Le type void et les procdures


En C, il existe un type particulier qui est le type void qui signifie rien ou nant . Cest type
quon utilise lorsquune fonction ne ncessite pas darguments. Ce type peut galement tre utilis
pour indiquer quil ny a pas de valeur de retour. Dans ce dernier cas, cette fonction se nomme
procdure et linstruction return na plus a tre utilise. Le type void est galement utilis dans
dautres circonstances avec notamment les pointeurs. Lors de lappel dune fonction ou dune
procdure ne prenant aucun paramtre, il convient de ne pas oublier les parenthses (c.f. exemple).
Exemples : voici quelques exemples dutilisation du type void avec les fonctions et procdures

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.

#include <stdio.h>
float indice = 2.5;
float PI(void) { return 3.14159; }
void ajoutIndice(int x) { indice += x ; }
void incrementIndice(void) { indice += 3; }
int main(void)
{
indice = PI();
ajoutIndice(6);
incrementIndice();
return 0;
}

6.5. Variables globales et porte des identificateurs


Il est autorise de dclarer deux variables portant le mme nom avec un type identique ou diffrent
dans deux blocs imbriqus. Les variables suivent les rgles suivantes :
Toute variable dfinie dans un bloc est exploitable dans tout le bloc et sous-blocs,
Une variable nexiste plus en dehors du bloc,

31

Entre deux variables identiques, cest la dclaration la plus proche qui est utilise.

Exemple :
#include <stdio.h>
float a, b;
int main(void)
{
float a, b;
if(a == 0)
{
float a, b;
}
}

Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

#include <stdio.h>
int i = 10;
int main(void)
{
int i = 20, j = 3;
if (j == 3)
{
int i = 30;
printf("valeur de i %d\n", i);
}
printf("valeur de i %d\n", i);
return 0;
}

32

7. Tableaux et chanes de caractres


7.1. Les tableaux
Un tableau est un regroupement conscutif de donnes de mme type et de taille fixe. A chaque
entre du tableau correspond un indice pouvant prendre une valeur dans lintervalle [0, taille 1]. Il
est possible de dclarer des tableaux avec de multiples indices (tableaux plusieurs dimensions)
comme les matrices. La syntaxe est :
Syntaxe (tableau une dimension) :
<type> <nom du tableau>[<expression entire constante>];

Syntaxe (tableau plusieurs dimensions) :


<type> <nom du tableau>[<expr entire>][<expr entire>];

Point important : lexpression entre crochet indique le nombre dlments que peut contenir le
tableau (taille du tableau selon sa dimension). Cependant, cette expression doit tre imprativement
constante, entire et positive car le compilateur doit connatre lavance cette taille pour produire
lexcutable.
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.

#include <stdio.h>
int main(void)
{
int i, j;
int t1[3];
/* tableau une dimension
*/
float t2[5][5]; /* tableau deux dimensions */
/*
** Utilisation des oprateurs usuels avec les lments du tableau
*/
t1[0] = 10;
t1[1] = 20;
t1[2] = t1[0] + t1[1];
for(i = 0; i < 5 ; i++)
{
for(j = 0; j < 5; j++)
{
t2[i][j] = i + j;
}
}
return 0;
}

Points importants :
Lindice entre crochet pour accder un lment du tableau doit tre imprativement entre
[0] et [taille 1].
Le compilateur ne contrle pas le dbordement dindice, ni la compilation, ni lexcution.
33

7.2. Les chanes de caractres


Une chane de caractres est un cas particulier de tableau contenant des lments de type char
uniquement et contenant systmatiquement 0 comme dernire valeur que lon nomme zro terminal
ou caractre nul not '\0'. En tenant compte du zro terminal, la taille exacte du chane de caractre
est gale nb de caractres + 1. De plus, un tableau peut tre initialis laide dune chane de
caractres entre guillemets : le zro terminal est ajout automatiquement par le compilateur. Enfin,
une chane caractre est affichable laide de la fonction printf en utilisant la chane de formatage
%s. Il existe des fonctions particulires pour manipuler les chanes de caractres, comme la
concatnation, qui seront abordes avec ltude sur les pointeurs.
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.

#include <stdio.h>
int main(void)
{
char chaine[7] = "Coucou";
/* sous cette forme, le 0 terminal est ajout automatiquement */
printf("le tableau contient la chaine \"%s\"\n", chaine);
chaine[0]
chaine[1]
chaine[2]
chaine[3]
chaine[4]
chaine[5]

=
=
=
=
=
=

'H';
101; /* Code ascii de la lettre e */
'l';
'l';
'o';
'\0';

printf("la nouvelle chaine est \"%s\"\n", chaine) ;


return 0;
}

34

8. Pointeurs
8.1. Gnralits sur les pointeurs
Le type pointeur fait partie de la famille des types lmentaires et la valeur contenue dans une
variable de type pointeur est ladresse dune donne stocke en mmoire (on parle galement de
rfrence). Aussi, pour connatre la nature de cette donne, il convient den prciser son type lors de
la dclaration du pointeur. Par abus de langage, le terme pointeur est la fois utilis pour dsigner le
type et la variable.
Syntaxe (dclaration dune variable de type pointeur) :
<type> * <variable>
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.

int main(void)
{
int i;
float f;
int * iPtr;
float * fPtr;

/*
/*
/*
/*

une
une
une
une

variable
variable
variable
variable

de type entier
*/
de type flottant */
pointeur sur un entier
*/
pointeur sur un flottant */

[]
}

Les pointeurs sont trs utiles et relativement indispensables. Cela permet de :


Modifier les paramtres en retour dune fonction (passage par rfrence par opposition au
passage par copie),
Passer un tableau dune taille quelconque en paramtre dune fonction,
Grer dynamiquement la mmoire,
Accder nimporte quelle donne en mmoire.
Reprsentation en mmoire dun pointeur sachant pointer : un pointeur p sur un entier i
0x00000000

(int) i = 50

0x0A143FC2

(int *) p = 0x0A143FC2

0x3F25187F

pointe sur

0xFFFFFFFF

Dans cette illustration, nous considrons une variable p de type pointeur sur un entier. Cette variable
p contient comme valeur ladresse dune variable i de type entier. Aussi, comme le symbolise la
flche, on dit que p pointe sur la variable i. De plus, laide de ce pointeur, comme nous le verrons
dans la suite, il est possible de modifier indirectement la valeur de la variable i laide des deux
oprateurs & (adresse de) et * (valeur de).

35

Autre reprsentation :
p

pointe sur

i = 50

8.2. Oprateur & : adresse de


Loprateur & permet dobtenir ladresse de nimporte quelle variable (on parle galement de
rfrence) et retourne ainsi un pointeur sur la variable manipule.
Syntaxe :
&<variable>
Exemple : dans cet exemple, nous rutilisons le schma prcdent et montrons comment faire en sorte que
la variable pointeur p pointe sur la variable i.

1. int main(void)
2. {
3.
int i = 50;
4.
int * p; /* dclaration dun pointeur non initialis */
5.
/* (pointe sur une zone quelconque de la mmoire */
6.
7.
p = &i; /* p pointe dsormais sur la variable i */
8.
9.
return 0;
10. }

Attention : il convient de vrifier que la variable et le pointeur sont de type compatible. De plus,
lorsque lon dclare une variable de type pointeur, la valeur de cette variable est quelconque ce qui
revient dire que le pointeur dclar pointe nimporte o dans la mmoire. Ceci est source de
nombreuses erreurs des ce problme de non-initialisation que lon rencontre avec tout type de
variable.

8.3. Oprateur * : valeur de


Loprateur * ralise linverse de loprateur &. Connaissant un pointeur sur une donne, il est
possible de lire et de modifier la valeur de celle-ci en utilisant cet oprateur.
Syntaxe :
*<pointeur>
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

#include <stdio.h>
int main(void)
{
int i = 50;
int * p;
// dclaration dun pointeur non initialis
p = &i
// p pointe dsormais sur la variable i
(*p) = 18; // a laide de loprateur *, la valeur de i est modifie
// et cette instruction est quivalente i = 18;
printf("%d\n", i); // cette instruction doit afficher en thorie 18
return 0;
}

36

Combin avec cet oprateur, le pointeur se comporte alors comme la variable pointe. Il est alors
possible dutiliser tous les oprateurs compatibles avec le type de la variable.
Exemple :

1. int main(void)
2. {
3.
int i = 18, *iPtr;
4.
float f = 3.6f, *fPtr;
5.
double d = 0.5, *dPtr;
6.
7.
iPtr = &i;
8.
fPtr = &f;
9.
dPtr = &d;
10.
11. (*dPtr) = d + 3.0 * (*iPtr) + i / (*fPtr) (*dPtr);
12.
13. return 0;
14. }

Attention : ne jamais accder une donne pointe avec un pointeur qui na pas t
correctement initialis. Ceci est source de bug et provoque en gnral larrt involontaire du
programme.
Exemple : mauvais exemple d lutilisation dun pointeur non initialis.

1.
2.
3.
4.
5.
6.
7.
8.
9.

int main(void)
{
int i = 50;
int * p;
(*p) = 12; /* ERREUR : le pointeur nest pas initialis */
return 0 ;
}

8.4. Une valeur de pointeur particulire : NULL


Il existe un pointeur de valeur particulire, note NULL, de valeur gale 0 et pointant sur la zone
mmoire dadresse 0x00000000. Cette valeur est utilise pour initialiser des variables de type
pointeur et pour dtecter plus facilement les erreurs de pointage. En particulier, lorsquon ne connat
pas encore la valeur du pointeur lors de sa dclaration, il fortement recommand de linitialiser
systmatiquement avec le pointeur NULL.
Exemple :

37

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

#include <sdtio.h>
int main(void)
{
int i = 50 ;
int * p = NULL ; /* dclaration dun pointeur initialis */
/* mais qui nest pas utilisable */
p = &i; // le pointeur pointe dsormais sur une zone valide
(*p) = 12;
return 0;
}

Attention : ne jamais accder une donne pointe par NULL laide de loprateur *. Avant de
manipuler un pointeur, il faut systmatiquement vrifier que celui-ci nest pas quivalent NULL.
Exemple : mauvais exemple d lutilisation dun pointeur nul.

1. int main(void)
2. {
3.
int i = 50;
4.
int * p = NULL;
5.
6.
(*p) = 12; /* ERREUR : le pointeur pointe sur une zone non valide */
7.
/* (i.e. NULL) */
8.
9.
return 0 ;
10. }

A savoir : la constante symbolique NULL est dfinie laide dune directive de prcompilation
#define NULL ((void *) 0)

8.5. Pointeur de pointeur


Par dfinition, une variable pointeur peut pointer sur nimporte quel type, y compris un pointeur. La
dclaration et lutilisation dun pointeur de pointeur suit les mmes rgles quun pointeur sur un type
lmentaire.
Syntaxe :
<type> ** <variable>
Exemple : ptr2 est un pointeur qui pointe sur ptr1 et ptr1 pointe sur la variable i.

38

1. int main(void)
2. {
3.
int i, * ptr1;
4.
int ** ptr2;
/* dclararation dun pointeur de pointeur */
5.
6.
ptr1 = &i;
/* ptr1 pointe sur i
*/
7.
ptr2 = &ptr1;
/* ptr2 pointe sur ptr1 */
8.
(*(*ptr2)) = 12; /* par le jeu dun double drfrencement
9.
/* la valeur de i est modifie. Linstruction est */
10.
/* quivalente i = 12;
*/
11. printf("%d\n", i);
12.
13. return 0;
14. }

8.6. Pointeur et paramtres de fonctions


8.6.1 Passage par rfrence
Une limitation lie aux fonctions est limpossibilit de modifier les paramtres autrement que
localement. En effet, les valeurs des paramtres sont passs par copie et les modifications
apportes sont appliques uniquement ces copies. Le problme se complique lorsquil sagit de
tableaux de grande dimension car cest une mthode trs gourmande en ressources : il est peu
recommandable de passer par copie un tableau de plusieurs Mo.
Exemple de passage par copie des paramtres : Les variables a et b ne sont pas modifies lors de lappel de
la fonction. Les modifications apportes aux variables x et y sont locales la fonction et nont aucun impact
sur les variables a et b.

1.
2.

void formule(int x, int y)


/* x et y contiennent la copie des valeurs de a et b lors de lappel
de la fonction */
3. {
4. /* x et y sont modifies localement et ces oprations*/
5.
x = x + y + 12; y++;
6. /* nont aucun effet en dehors de la fonction */
7. }
8.
9. int main(void)
10. {
11. int a = 12, b = 55;
12.
13. formule(a, b);
14. /* a et b ne sont pas affects pas lappel de la fonction : */
15. /* leur valeur reste la mme avant et aprs la fonction
*/
16.
17. return 0;
18. }

Aussi, un moyen pour contourner cette limitation consiste exploiter les pointeurs. Le passage de
paramtres par le biais des pointeurs est qualifi de passage par rfrence. Cette autre mthode
consiste passer ladresse de la variable (on parle de rfrence) en guise de paramtre laide de
loprateur & au lieu de passer sa valeur.

39

Exemple : Version passage par rfrence de lexemple prcdent.

1.
2.

void formule(int * x, int * y)


/* Lors de lappel de la fonction, x et y sont deux pointeurs
contenant les adresses des variables a et b */
3. {
4.
(*x) = (*x) + (*y) + 12;
5. /* les modifications apportes via x et y sont rpercutes sur a et
b */
6.
(*y) ++;
7. }
8.
9. int main(void)
10. {
11. int a = 12, b = 55;
12.
13. formule(&a, &b);
14. // la fonction est appele en donnant les rfrences
15. // sur les variables a et b. A lissue, les valeurs
16. // de a et b sont modifies
17. return 0;
18. }

Utilisation du pointeur NULL pour la programmation dfensive :


Lutilisation du passage par rfrence est galement soumis au problme de validit des pointeurs.
En dautre terme, avant toute manipulation des paramtres, il est ncessaire de vrifier que les
pointeurs sont valides. Cependant, il est pratiquement impossible de dterminer si un pointeur pointe
sur une zone valide ou non. La seule vrification possible consiste sassurer que le pointeur nest
pas le pointeur NULL.
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

void fonction_non_defensive(int * x)
{
(*x)++; /* source derreur si x nest pas un pointeur valide */
}
void fonction_ defensive(int * x) { if (x != NULL) { (*x)++; } }
int main(void)
{
fonction_non_defensive(NULL); /* appel de fonction source derreur */
fonction_defensive(NULL);
/* fonction "protge" */
return 0;
}

8.7. Pointeurs et tableaux


8.7.1 Tableau de pointeurs
En programmation C, un tableau peut contenir des lments de nimporte quel type. En particulier, il
est possible de dfinir un tableau de pointeurs. La syntaxe est inchange en dehors du fait que lon
utilise le type pointeur pour dfinir la nature des lments.
40

Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.

#include <stdio.h>
int main(void)
{
int i, j, k, l;
/* quatre entiers */
int* t1[4];
/* un tableau de 4 pointeurs sur des entiers */
int* t2[2][2]; /* un tableau 2D de pointeurs sur des entiers */
/*
** le
** le
** le
** le
*/
t1[0]

premier lment de t1 pointe sur i


second lment de t1 pointe sur j
troisime lment de t1 pointe sur k
quatrime lment de t1 pointe sur l
= &i; t1[1] = &j; t1[2] = &k; t1[3] = &l;

(*t1[0]) = 12 /* quivalent
(*t1[1]) = 99 /* quivalent
(*t1[2]) = 25 /* quivalent
(*t1[3]) = 57 /* quivalent
/*
** llment (0,0) pointe sur
** llment (0,1) pointe sur
** llment (1,0) pointe sur
** llment (1,1) pointe sur
*/
t2[0][0] = &i; t2[0][1] = &j;
t2[1][0] = &k; t2[1][1] = &l;
(*t2[0][0])
(*t2[0][1])
(*t2[1][0])
(*t2[1][1])

=
=
=
=

255
374
199
736

/*
/*
/*
/*

i
j
k
l

=
=
=
=

12;
99;
25;
57;

*/
*/
*/
*/

i
j
k
l

255;
374;
199;
736;

i
j
k
l

quivalent
quivalent
quivalent
quivalent

=
=
=
=

*/
*/
*/
*/

return 0;
}

8.7.2 Analogie entre tableau et pointeur


o Il y a une analogie entre les tableaux et pointeurs dans le sens o un pointeur peut tre
utilis avec loprateur [] destin aux tableaux. De plus, le nom dun tableau peut
tre considr comme tant un pointeur sur son premier lment, cest dire
quivalent &(t[0]). Par contre, contrairement au pointeur, il est impossible de le
faire pointer sur une autre zone. Si loprateur [] peut galement tre utilis avec les
pointeurs pour les tableaux plusieurs dimensions (cf chapitre sur la gestion
dynamique de la mmoire). Par contre, lanalogie entre pointeurs et tableaux nest
valide que pour les tableaux une seule dimension.

41

Exemple :
t

0 1 2 3 4 5 6 7 8 9
255 4 127 8 10 12 14 16 18 20

t1

1. int main(void)
2. {
3.
int t[10] = { 2,
4.
int * t1, * x;
5.
6.
t1 = t;
/*
7.
t1[3] = 255; /*
8.
x = &(t[2]); /*
9.
(*x) = 127;
/*
10. x[0] = 127;
/*
11.
12. return 0;
13. }

4, 6, 8, 10, 12, 14, 16, 18, 20 };

quivalent
quivalent
x pointe sur
quivalent
quivalent

t1 = &(t[0]); */
t[3] = 255;
*/
le troisime lment de t */
t[2] = 127; */
(*x) = 127; donc t[2]=127 */

Remarque : Cette analogie est largement utilise dans le cadre des chanes de caractres quelles
soient dclares de faon statique ou dynamique. Aussi, la notation pointeur pour les chane de
caractres est galement trs utilise avec la notation tableau.
Exemple :
char ch1[7] = "Coucou";
char ch2[] = "Hello";
char * ch3 = "Bonjour"; /* Utilisation de la notation pointeur */

8.7.3 Passage par rfrence dun tableau en paramtre dune fonction


Du fait de la conversion d'un identificateur de type tableau en l'adresse du premier lment, lorsqu'un
tableau est pass en paramtre effectif, c'est cette adresse qui est passe en paramtre. Le paramtre
formel correspondant devra donc tre dclar comme tant de type pointeur.
Voyons sur un exemple. Soit crire une procdure imp_tab qui est charge d'imprimer un tableau
d'entiers qui lui est pass en paramtre. On peut procder de la manire suivante :
1.
2.
3.
4.
5.

void imp_tab(int *t, int nb_elem) /* dfinition de imp_tab


{
int i;
for (i = 0; i < nb_elem; i++) printf("%d ",*(t + i));
}

*/

Cependant, cette mthode a un gros inconvnient. En effet, lorsqu'on lit l'en-tte de cette procdure,
c'est dire la ligne :
void imp_tab(int *t, int nb_elem)

il n'est pas possible de savoir si le programmeur a voulu passer en paramtre un pointeur vers un int
(c'est dire un pointeur vers un seul int), ou au contraire si il a voulu passer un tableau, c'est dire
un pointeur vers une zone de n int. De faon ce que le programmeur puisse exprimer cette
diffrence dans l'en-tte de la procdure, le langage C admet que l'on puisse dclarer un paramtre
formel de la manire suivante :

42

1.
2.
3.
4.

void proc(int t[])


{
...
/*
corps de la procdure proc
}

*/

car le langage assure que lorsqu'un paramtre formel de procdure ou de fonction est dclar comme
tant de type tableau de X, il est considr comme tant de type pointeur vers X.
Si d'autre part, on se souvient que la notation *(t + i) est quivalente la notation t[i], la dfinition de
imp_tab peut s'crire :
1.
2.
3.
4.
5.

void imp_tab(int t[], int nb_elem)


/*
dfinition de imp_tab
{
int i;
for (i = 0; i < nb_elem; i++) printf("%d ",t[i]);
}

*/

Cette faon d'exprimer les choses est beaucoup plus claire, et sera donc prfre. L'appel se fera de
la manire suivante :
1.
2.
3.
4.
5.
6.
7.
8.

#define NB_ELEM 10
int tab[NB_ELEM];
int main(void)
{
imp_tab(tab,NB_ELEM);
}

Remarque :
Quand une fonction admet un paramtre de type tableau, il y a deux cas possibles :
- soit les diffrents tableaux qui lui sont passs en paramtre effectif ont des tailles diffrentes,
et dans ce cas la taille doit tre un paramtre supplmentaire de la fonction, comme dans
l'exemple prcdent ;
- soit les diffrents tableaux qui lui sont passs en paramtre effectif ont tous la mme taille, et
dans ce cas la taille peut apparatre dans le type du paramtre effectif :
9.
10.
11.
12.
13.
14.
15.
16.
17.

#define NB_ELEM 10
void imp_tab(int t[NB_ELEM])
{
...
}

Cette analogie entre pointeur et tableau permet ainsi le passage par rfrence dun tableau en
paramtre dune fonction. Ceci vite de recopier tout le contenu dun tableau lors de lappel dune
fonction.
Exemple :

43

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

#include <stdio.h>
/* Fonction qui calcule la somme des termes contenus dans le tableau t */
int sommeTableau(int * t, int n)
{
int i, somme = 0;
if (t != NULL) { for(i = 0 ; i < n ; i++) { somme += t[i]; } }
return somme;
}
int main(void)
{
int t[5] = { 1, 3, 5, 7, 9 };
printf("%d\n", sommeTableau(t, 5));
return 0;
}

8.7.4 Arithmtique de pointeurs et notion de dcalage (offset)


Les oprateurs arithmtiques +, -, ++ et -- peuvent tre galement utiliss pour raliser des
dcalages en mmoire. Cette mthode est trs utilises pour se reprer dans les tableaux.
Exemple :

0
2

1. int main(void)
2. {
3.
int * x, * y;
4.
int t[10] = { 2,
5.
6.
y = &(t[0]);
7.
x = y + 5;
8.
(*x) = 34;
9.
x++;
10. (*x) = 23;
11. *(y + 3) = 127;
12.
13. return 0;
14. }

1
4

2
3
4 5 6 7 8 9
6 127 34 23 14 16 18 20
+5
+1
+3
y+3 x1 x2

4, 6, 8, 10, 12, 14, 16, 18, 20 };


/* y pointe sur le 1er lment */
/* x pointe sur le 5me lment */
/* quivalent t[5] = 34; */
/* quivalent t[6] = 23; */
/* quivalent t[3] = 127; */

8.7.5 Oprateurs de comparaison


Les oprateurs de comparaison <=, >=, <, >, != et == peuvent galement tre utiliss pour comparer
des pointeurs, cest--dire des adresses. En particulier, il peut tre parfois utile de savoir si un
pointeur est situ avant ou aprs un autre pointeur.

44

9. Dfinition de types, types numrs


9.1. Typedef
Le mot-cl typedef permet de dfinir des synonymes (alias) sur des types de donnes lmentaires
ou composs. Cela revient crer de nouveaux types et rendre plus lisible le code.
Syntaxe :
typedef <type> <nom du synonyme>;
Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

typedef int MonTypeEntier;


typedef int * IntPtr;
int main(void)
{
MonTypeEntier n = 0;
IntPtr p = &n;

/* Nouveau type quivalent int */


/* Nouveau type quivalent int* */

/* Dclaration avec le nouveau type */

p = &n;
(*p) = 12; /* instruction quivalent n = 12; */
return 0;
}

9.2. type numr : dfinition de constantes symboliques


Le mot-cl enum est ce que lon appelle un type numr permettant de dfinir des constantes
symboliques quivalentes des valeurs entires. Par la suite, la constante symbolique peut tre
utilise telle quelle dans le code. Cette approche est trs utile pour rendre plus lisible le code et pour
le faire voluer plus facilement (changer les valeurs). Lors de la compilation, la constante
symbolique est remplace par la valeur entire quelle reprsente.
Syntaxe :
enum {<nom de constante> [=<expr>]1[,<nom de constante> [=<expr>]1]+ };

Par dfaut, la premire constante est quivalente la valeur 0, la suivante la valeur 1, etc.
Exemple :
enum
{
NORD, /*
SUD, /*
EST, /*
OUEST /*
};

NORD
SUD
EST
OUEST

est
est
est
est

quivalent
quivalent
quivalent
quivalent

la
la
la
la

valeur
valeur
valeur
valeur

0
1
2
3

*/
*/
*/
*/

Cependant, il est possible de prciser la valeur dune constante laide de loprateur =. La constante
suivante est tout simplement gale la valeur de la constante prcdente plus 1.
Exemple :

45

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.

enum
{
PIQUE,
COEUR = 12,
CARREAU,
TREFLE
};

/*
/*
/*
/*

PIQUE
COEUR
CARREAU
TREFLE

est
est
est
est

quivalent
quivalent
quivalent
quivalent

la
la
la
la

valeur
valeur
valeur
valeur

0
12
13
14

*/
*/
*/
*/

int main(void)
{
int carte = CARREAU; /* quivalent carte = 13;
*/
int direction = SUD; /* quivalent direction = 1; */
if (carte == TREFLE) { [] } /* quivalent if (carte == 14) { [] }*/
switch(direction)
{
case NORD: /* quivalent case 0: */
[];
break;
case SUD:
/* quivalent case 1: */
[];
break;
case EST:
/* quivalent case 2: */
[];
break;
case OUEST: /* quivalent case 3: */
[];
break;
default:
break;
}
[]
return 0;
}

46

10. Structures
10.1.

Gnralits

Si les tableaux permettent le regroupement de plusieurs donnes de mme type, il souvent fort utile
de pouvoir regrouper des donnes de type diffrent. Cest ce que permettent les structures (struct),
selon la terminologie employe par le langage C. Pour dsigner les diffrentes donnes composant la
structure, on parle alors de champs. A chaque champ est associ un type. Dfinir une structure,
revient crer un nouveau type. Dans la syntaxe propose ci-dessous, le mot-cl typedef nest pas
utilis. Dans la suite, nous montrons quel est leffet de lutilisation du mot-cl typedef combin
avec les structures.
Syntaxe : dfinition dune structure
struct <nom de la structure> { [<type> <nom du champ>;]+ };
Exemple :

1.

/* Dfinition du type Personne identifi par son age, ses nom et


prnom */
2. struct Personne
3. {
4.
int age;
5.
char * nom;
6.
char * prenom;
7. };
8.
9. int main(void)
10. {
11. []
12. }

Point important : les dclarations de structure sont gnralement situes en dehors du corps des
fonctions.
Par la suite, il devient alors possible de dclarer et dexploiter des variables de type structure. Aussi,
pour dclarer une variable de ce type, le mot-cl struct est indipensable. Dans la suite, nous
verrons quavec le mot-cl typedef, il est possible de faire disparatre le mot-cl struct.
Syntaxe : dclaration de variables de type structure
struct <nom de la structure> <nom de variable>[,<nom de variable>]+;

Tout comme les tableaux, il est galement possible dinitialiser tous les champs dune structure en
donnant la liste des valeurs spares par des virgules, le tout entre accolades. Lordre des valeurs est
dtermin par lordre dans lequel sont dclars les champs.
Pour accder et modifier les diffrents champs, qui sutilisent de la mme faon que nimporte quelle
variable, on utilise la notation pointe ".".
Syntaxe :
<nom de la variable de type structure>.<nom du champ>

47

Exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

/* Cration du type Personne */


struct Personne
{
int age;
char * nom;
char * prenom;
};
int main(void)
{
struct Personne p1 = { 25, "Martin", "Pierre" };
struct Personne p2;
p2.age = p1.age;
p2.nom = "Dupont";
p2.prenom = "Hubert";
p2.age = p2.age+50;
return 0;
}

A savoir : le nom de la structure nest pas indispensable lorsque lon combine la dclaration de la
structure et dclaration de variables de ce type. Le nom de la structure est ncessaire lorsque lon
dsire dclarer plusieurs instances dune mme structure divers endroits du programme.
Exemple : version sans dclaration de structure de lexemple prcdent.

1. int main(void)
2. {
3.
struct
4.
{
5.
int age;
6.
char * nom;
7.
char * prenom;
8.
} p1 = { 25, "Martin", "Pierre" }, p2;
9.
10. p2.age = p1.age;
11. p2.nom = "Dupont";
12. p2.prenom = "Hubert";
13.
14. p2.age += 5;
15.
16. return 0;
17. }

10.2.

Structure et typedef

Les structures appartiennent la catgorie des types composs. A ce titre, il est possible de crer de
nouveaux types avec le mot-cl typedef. Un des effets de ce genre dinstruction est de faire
disparatre le mot-cl struct. De plus, le nom de la structure, ne pas confondre avec le nom du
nouveau type, peut tre omis. Par contre, lutilisation de la structure et laccs aux diffrents champs
reste inchange.
48

Syntaxe :
typedef struct [<nom de la structure>]1 {<liste des champs>} <nom du type>;
Exemple :

1. struct Date /* Exemple sans typedef */


2. {
3.
int jour;
4.
int mois;
5.
int annee;
6. };
7.
8. typedef struct /* Exemple avec typedef */
9. {
10. int heures;
11. int minutes;
12. int secondes;
13. } Heure;
14.
15. int main(void)
16. {
17. struct Date jour; /* dclaration sans typedef : struct est conserv
*/
18. Heure horloge;
/* dclaration avec typedef : struct est supprim
*/
19.
20. dateDuJour.jour = 17;
21. horloge.heures = 10;
22.
23. [];
24. }

10.3.

Structures imbriques

Les structures appartiennent la famille des types composs. Aussi, il est autoris de dclarer des
champs de type structure au sein mme dune autre structure (sil sagit de la mme structure, on
parle de structure rcurrente dont le sujet est abord dans la suite). Pour accder aux diffrents
champs de ce champ de type structure, il est alors ncessaire dutiliser la notation pointe autant de
fois que cela est ncessaire.
Exemple :

49

1.
2.
3.
4.

struct Date { int jour; int mois; int annee; };


typedef struct { int heures; int minutes; int secondes; } Heure;

/* Dclaration dune structure compose


structure */
5. struct RendezVous
6. {
7.
struct Date date;
8.
Heure horaire;
9. };
10.
11. int main(void)
12. {
13. struct RendezVous rdv;
14. /* Accs aux champs du champ date */
15. rdv.date.jour = 14;
16. rdv.date.mois = 2;
17. rdv.date.annee = 2004;
18. /* Accs aux champs du champ horaire */
19. rdv.horaire.heures = 14;
20. rdv.horaire.minutes = 30;
21. rdv.horaire.secondes = 17;
22.
23. return 0;
24. }

10.4.

de

deux

champs

de

type

Structures et tableaux

Par extension, il possible de dclarer des tableaux de structures ainsi que des champs de type tableau
au sein dune structure. Suivant la dclaration, lutilisation de loprateur [] doit tre effectue dans
le bon ordre.
Exemple :

50

1.
2.
3.
4.
5.

struct Date { int jour; int mois; int annee; };


typedef struct { int heures; int minutes; int secondes; } Heure;
struct RendezVous { struct Date date; Heure horaire; };
/* structure Agenda contenant un champ de type tableau de structures

*/
6. struct Agenda
7. {
8.
struct Date calendrier[365];
9. };
10.
11. int main(void)
12. {
13. struct RendezVous rdv[5]; /* tableau
RendezVous */
14. struct Agenda agenda;
15.
16. rdv[0].date.jour = 14;
17. agenda.calendrier[38].jour = 29;
18. }

10.5.

de

structures

de

type

Structures et pointeurs

Par gnralisation, les pointeurs peuvent tre galement exploits avec les structures. Il est alors
possible davoir des pointeurs sur des structures. Toutefois, le langage dfinis loprateur flche "->"
pour accder aux diffrents champs dune structure partir dun pointeur, autrement que par
loprateur de dfrencement "*". Cet oprateur a lavantage dindiquer explicitement que le code
manipule un pointeur sur une structure. Lexemple suivant montre comment ces deux oprateurs
sont utiliss.
Syntaxe :
<pointeur> -> <champ>
Exemple :

51

1. struct Date { int jour; int mois; int annee; };


2.
3. int main(void)
4. {
5.
struct Date * p;
/* pointeur sur une structure Date */
6.
struct Date today; /* variable de type structure Date */
7.
8.
today.jour = 17;
9.
today.mois = 9;
10. today.annee = 2004;
11. p = &today;
/* initialisation du pointeur : celui-ci pointe
sur */
12.
/* la variable today
*/
13.
14. (*p).annee = 2012;
15. /* utilisation de loprateur *, linstruction est quivalente
today.annee = 2012; */
16.
17. p -> jour = 24;
/* utilisation de loprateur ->, linstruction est quivalente
(*p).jour = 24; qui est quivalente today.jour = 24;*/
18. return 0;
19. }

Plus gnralement, il est possible de dfinir des structures contenant des champs de type pointeur,
des tableaux de pointeurs sur des structures, une structure contenant un tableau de pointeur, etc.
Toutes les combinaisons possibles et imaginables (ou presque) peuvent tre alors mises en uvre.
Exemple : exemple plus complet avec diverses combinaisons

52

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.

struct Date { int jour; int mois; int annee; };


struct Heure { int heures; int minutes; int secondes; };
struct RendezVous { struct Date date; Heure horaire; };
struct RendezVous
{
struct Heure horaire;
struct Date * date;
struct Date calendrier[365];
struct Heure * crenaux[10];
};
int main(void)
{
struct Date today;
struct RendezVous rdv;
struct RendezVous * ptr;
rdv.date = &today;
rdv.date -> jour = 14;
rdv.date -> annee = 2004;
ptr = &rdv;
ptr -> horaire.heures = 10;
ptr -> horaire.minutes = 30;
ptr
ptr
ptr
ptr
ptr

->
->
->
->
->

date = &today;
date -> jour = 28;
date -> annee = 2004;
calendrier[3].jour = 15;
crenaux[3] -> heure = 18;

return 0;
}

10.6.

Structures rcursives

Jusqu prsent, nous avons pu observer que les structures peuvent contenir des champs de
nimporte quel type, y compris de type structure. Cependant, une structure ne peut pas contenir un
champ qui soit une instance delle-mme. Par contre, il est possible pour une structure davoir un
champ de type pointeur et pointant sur cette dernire. Ce cas est largement utilis pour dfinir des
listes chanes. On parle alors de structure rcurrente.
Pour dfinir une structure rcurrente, une mthode simple consiste dfinir un type pointeur sur la
structure avant de dfinir la structure elle-mme. Cette mthode repose sur lutilisation du mot-cl
typedef selon deux tapes. Lexemple suivant montre comment dclarer une telle structure pour
dfinir une liste chane dentiers.

Exemple : dclaration dune structure rcurrente

53

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

/* 1re tape : dclaration dun type pointeur sur la structure */


typedef struct Liste * ListePtr;
/* 2nde tape : dclaration de la structure */
struct Liste
{
int valeur;
ListePtr suivant; /* la structure contient un champ pointant */
/* sur ce type de structure
*/
};
int main(void)
{
struct Liste element1;
struct Liste element2;
element1.valeur = 5;
element1.suivant = &element2;
element1.suivant -> valeur = 10;
element1.suivant -> suivant = &element1;
return 0;
}

Le schma suivant reprsente les relations entre les deux structures lissue du programme :
elment1
valeur = 5
suivant

element2
valeur = 10
suivant

54

11. Gestion dynamique de la mmoire


Allocation de la mmoire :
Contrairement aux tableaux dfinis statiquement ou aux dclarations de variables dont lespace
mmoire est rserv par le compilateur, il peut arriver que lon ne connaisse pas lavance la taille
dun tableau ou dune manire gnrale de lespace mmoire dont on a besoin lexcution.
Bien quavec les pointeurs on puisse accder nimporte quelle zone de la mmoire, il convient de
rserver un espace mmoire pour tre sr de ne pas avoir un comportement alatoire de notre
programme (en thorie).
Cette procdure dallocation consiste indiquer au systme la taille de lespace dont on a besoin et
celui-ci nous retourne un pointeur sur le dbut de cette zone.

11.1.
Fonctions dattribution et de libration dynamique de la
mmoire
Ncessite la directive de prcompilation #include <stdlib.h>
Type size_t : dfini dans type.h et de type unsigned long

Type utilis pour les tailles de chanes de caractres et de bloc mmoire, dfini
par lANSI-C
void * malloc(<expression entire (i.e nb doctets)>); /* allocation */
void * calloc(<taille du tableau>, <taille dune cellule>); /* alloc tab */
void
free(void * pointeur); /* libration */

11.2.

Demande d'allocation mmoire (malloc) :

malloc demande l'allocation d'un bloc de mmoire de size octets conscutifs dans la zone de
mmoire du tas.
Valeur retourne :
Si l'allocation russit, malloc retourne un pointeur sur le dbut du bloc allou.
Si la place disponible est insuffisante ou si size vaut 0, malloc retourne NULL.
Remarque :
Les fonctions d'allocation dynamique retournent des pointeurs sur des void. Il faut donc oprer des
conversions de types explicites pour utiliser ces zones mmoire en fonction du type des donnes qui
y seront mmorises.
Exemple :

55

1. #include <stdio.h>
2. #include <stdlib.h>
3.
4. main() {
5.
char *ptr;
6.
struct s_fiche { char nom[30];
7.
int numero;
8.
struct s_fiche *suiv;
9.
} *fiche;
10.
11.
ptr = (char *) malloc(80); /* demande d'allocation de 80 octets */
12.
13.
if ( ptr == NULL) {
14.
fprintf(stderr,"Allocation mmoire impossible\n");
15.
exit(1);
16.
}
17.
18.
if (fiche = (struct s_fiche *) malloc(sizeof(struct s_fiche)) ==
NULL) {
19.
fprintf(stderr, "Allocation mmoire impossible\n");
20.
exit(1);
21.
}
22.
23.
free(fiche);
/* libration de la mmoire */
24.
free(ptr);
25. }

11.3.

Libration mmoire (free) :

La fonction free libre un bloc mmoire d'adresse de dbut ptr. Ce bloc mmoire a t
prcdemment allou par une des fonctions malloc, calloc, ou realloc.
Attention :
Il n'y a pas de vrification de la validit de ptr.
Ne pas utiliser le pointeur ptr aprs free, puisque la zone n'est plus rserve.
A tout appel de la fonction malloc ( ou calloc ) doit correspondre un et un seul appel la
fonction free.
Les appels pour librer des blocs de mmoire peuvent tre effectus dans n'importe quel
ordre, sans tenir compte de celui dans lequel les blocs ont t obtenus l'allocation.
Valeur retourne : aucune
Exemple 1 :

1. #include <stdio.h>
2. #include <stdlib.h>
3.
4. int main() {
5.
int *str = NULL, i;
6.
7.
str = (int *) malloc(10 * sizeof(int));
8.
for (i=0; i < 10; i++)
9.
str[i] = i * 10;
10.
printf("%d\n", i[9]);
11.
free(str);
12.
return 0;
13. }

56

Exemple 2 : illustration d'une erreur commise frquemment

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

#include <stdio.h>
#include <stdlib.h>
int main() {
char *str;
str = (char *) malloc(100 * sizeof(char));
gets(str); /* saisie d'une chaine de caracteres */
/* suppression des espaces en tete de chaine */
while ( *str == ' ')
str++;
/* free ne libere pas toute la zone allouee car str ne designe
plus le debut de la zone memoire allouee par malloc */
free(str);
return 0;
}

11.4.

Demande d'allocation mmoire pour tableau (calloc) :

La fonction calloc rserve un bloc de taille [nbr_element] x [element_size] octets conscutifs. Le


bloc allou est initialis 0.
Valeur retourne :

Si succs, calloc retourne un pointeur sur le dbut du bloc allou.


Si chec, calloc retourne NULL s'il n'y a plus assez de place ou si nelem ou elsize valent 0.

Exemple :

1. #include <stdio.h>
2. #include <stdlib.h>
3.
4. int main() {
5.
int *str = NULL;
6.
7.
str = (char *) calloc(10, sizeof(int));
8.
printf("%d\n", str[9]);
9.
free(str);
10.
return 0;
11. }

11.5.

Tableaux multiples : allocation et libration

tableaux 2 dimensions (10x20)


int i;
int ** tab;
tab = (int **) malloc(10 * sizeof(int *));
if (tab != NULL)
{
for(i = 0; i < 10; i++) { tab[i] = (int *) malloc(20 * sizeof(int)); }
}

57

for(i = 0; i < 10; i++) { free(tab[i]); }


free(tab);

tableaux 3 dimensions (10x20x30)


int i, j ;
int *** tab;
tab = (int ***) malloc(10 * sizeof(int **));
if (tab != NULL)
{
for(i = 0; i < 10; i++)
{
tab[i] = (int **) malloc(20 * sizeof(int *));
if (tab[i] != NULL)
{
for(j = 0; j < 20; j++)
{
tab[i][j] = (int *) malloc(30 * sizeof(int));
}
}
}
}

11.6.

Erreurs frquentes

indirection illgale
char * ptr;
(*ptr) = (char *) malloc(100 * sizeof(char));
(*ptr) = 'y';

dbordement de tableau ou dpassement de zone mmoire


int * tab ;
tab = (int *) malloc(10 * sizeof(int)) ;
tab[20] = 12;

libration dune zone invalide ou dun tableau statique


int * p ;
int t[50];
free(p) ;
free(t);

accs mmoire interdit (cf init)


int * p ;
(*p) = 12 ;

fuites mmoire
void swap(int * ptr1, int * ptr2, int n)
{
int * tmp = (int *) malloc(n * sizeof(int));
memcpy(tmp, ptr1, n * sizeof(int));
memcpy(ptr1, ptr2, n * sizeof(int));
memcpy(ptr2, tmp, n * sizeof(int));
}

utilisation dun pointeur dun espace libr


int * p = (int *) malloc(10 * sizeof(int));
free(p);
p[0] = 10;

58

12. Manipulation des chanes de caractres


Type size_t : dfini dans type.h et de type unsigned long

Type utilis pour les tailles de chanes de caractres et de bloc mmoire, dfini
par lANSI-C
Existence de fonctions standards pour la manipulation des chanes de caractres

Ncessite la direction de prcompilation #include

<string.h>

size_t strlen (const char *s)


/* taille dune chaine */
char *strcat (char *dest, const char *src);
char *strncat (char *dest, const char *src, size_t n) ;
/* concatenation de deux chanes, savoir src copi la suite de dest (avec
arrt au bout de n caractres pour strncat) */
int strcmp (const char *s1, const char *s2);
int strncmp (const char *s1, const char *s2, size_t n);
/* comparaison de deux chanes (avec arrt au bout de n caractres de s1 pour
strncmp) */

Regles de comparaison :
Si (s1 < s2) strcmp retourne 1
Si (s1 == s2) strcmp retourne 0
Si (s1 > s2) strcmp retourne 1

La comparaison est effectu caractre par caractre sur la base de leur code ASCII
Ainsi :
"abcd" < "abz" car 'c' < 'z' cest--dire 99 < 122

Par contre
"abcd" > "abZ" car 'c' > 'Z' cest--dire 99 > 90

Et
"abc" > "ab" car 'c' > '\0' cest--dire 99 > 0
char *strcpy (char *dest, const char *src);
char *strncpy (char *dest, const char *src, size_t n);
/* copie de src dans dest (avec arrt au bout de n caractres pour strncmp) */
char *strchr (const char *s, int c);
char *strrchr (const char *s, int c);
/* recherche dun caractre dans une chane et retour dun pointeur sur le
caractre correspondant (NULL si non trouv)*/

La fonction strchr() renvoie un pointeur sur la premire occurence du caractre c dans la chane s.
La fonction strrchr() renvoie un pointeur sur la dernire occurence du caractre c dans la chane
s.
char *strstr (const char *meule_de_foin, const char *aiguille);
/* recherche la premire occurrence de aiguille dans meule_de_foin, retourne le
pointeur sur la premire occurrence, NULL sinon */

59

Exemples :

1. int main(void)
2. {
3.
int n;
4.
char * p;
5.
char chaine[100];
6.
char texte[] = "Ceci est un texte";
7.
8.
n = strlen(texte);
9.
strncpy(chaine, texte, 6);
10. strncat(chaine, texte, n);
11. n = strchr(texte, 't');
12. n = strrchr(texte, 't');
13. n = strcmp(chaine + 3, texte, 5);
14. }

12.1.

Erreurs frquentes

Oubli du 0 terminateur ds les chanes (ex : copie)


char * s = (char *) malloc(2 * sizeof(char));
s[0] = 'A';
s[1] = 'B';
printf("%s\n", s);

Egalit de chanes de caractre vs galit de pointeurs


char * texte1 = "Coucou";
char * texte2 = "Hello";
if (texte1 == texte2) { [] }

Dbordements de buffer
strcpy et strcat autorisent le dbordement de buffer sans vrifier la taille de la destination :
char dest[10] ;
char * src = "abcdefghijklmnopq";
strcpy(dest, src);

Il vaut mieux utiliser les fonctions strncpy et strcat mais ces fonctions ne permettent pas dviter
compltement les dbordements de buffer (tronques les chanes sans insrer un 0 terminateur). Il
vaut mieux utiliser les fonctions non-standard strlcpy et strlcat qui insre systmatiquement un 0
terminateur.
Modification de mmoire constante
char * s = "Coucou";
s[0] = 'H';

libration de mmoire constante


char * texte = "coucou";
free(texte);

60

13. Fichiers et E/S


13.1.

Les fichiers

2 types de fichiers :
Les fichiers textes : chaques octets du fichier sont des caractres imprimables : exemple, les
fichiers .c
Les fichiers binaires : + compacts mais non lisible par un humain car succession doctets qui
ne sont pas ncessairement des caractres imprimables : exemple, les fichiers .pdf
Un fichier peut tre vu comme tant un norme tableau doctets avec un indicateur de position gal
au dcalage depuis le dbut du fichier.
Crer/ouvrir un ficher :
FILE * fopen(const char * restrict path, const char * restrict mode);

retourne un pointeur de type FILE * : descripteur de fichier ncessaire pour identifier l'accs au
fichier lors de toutes les oprations. Le pointeur est NULL si l'ouverture a chou.
En gnral on dsigne l'accs au fichier comme un flux de donnes (stream). C'est galement le
terme employ pour communiquer travers le rseau.
premier argument : nom du fichier
second argument : mode d'ouverture du fichier (chane de caractre)

Les diffrents modes sont :


"r" (read) : ouverture du fichier en lecture depuis le dbut du fichier
"r+" : ouverture du fichier en lecture et criture depuis le dbut du fichier
"w" : ouverture du fichier en criture (fichier cr s'il n'existe pas sinon de
taille 0) avec positionnement en dbut de fichier
"w+" : ouverture du fichier en lecture et criture (fichier cr s'il n'existe
pas sinon de taille 0)
"a" : ouverture en criture avec positionnement la fin du fichier
"a+" : ouverture en lecture et criture

Possibilit d'ajouter la lettre b pour indiquer que l'on fonctionne en mode binaire (sinon, il y a
conversion des retours la ligne selon les plates-formes). Normalement le "b" n'est plus ncessaire
mais a peut qd mme varier selon les libc.
Exemple :
FILE * fichier = NULL;
fichier = fopen("test.txt", "w+");

Fermer un fichier :
le corollaire de la fonction fopen est la fonction fclose. Une valeur de retour de 0 indique une
erreur.
int fclose(FILE *stream);
Une fois le fichier ferm, le flux ne peut plus tre utilis.

61

Exemple:

1.
2.
3.
4.
5.
6.
7.

FILE * fichier = NULL;


fichier = fopen("test.txt", "rb");
if (fichier != NULL)
{
fclose(fichier);
fichier = NULL;
}

Dans le cas o on lit des octets, lindex de fichier avance dautant doctets.
Lire des donnes dans un fichier :
size_t fread(void * restrict ptr, size_t size, size_t n, FILE * stream);

Arguments
a) pointeur sur le buffer contenant les donnes copier du fichier vers une zone mmoire
b) taille des objets copier
c) nombre d'objet copier
d) descripteur de flux
Retour : nb dobjets rellement lus
Exemple :

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

char buffer[50];
FILE * fichier = NULL;
fichier = fopen("test.zip", "rb");
if (fichier != NULL)
{
fread(buffer, sizeof(char), 50, fichier);
fclose(fichier);
fichier = NULL;
}

Ecrire dans un fichier :


size_t fwrite(void * restrict ptr, size_t size, size_t n, FILE * stream);

Arguments
a) pointeur sur le buffer contenant les donnes copier dans le fichier
b) taille des objets copier
c) nombre d'objet copier
d) descripteur de flux
Retour : nb dobjets rellement crits
Exemple : recopier un fichier

62

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

FILE * fileIn = NULL, * fileOut = NULL;


fileIn = fopen("Input.txt", "rb");
fileOut = fopen("Output.txt", "wb");
if ((fileIn != NULL) && (fileOut != NULL))
{
int n;
char buffer[1024];
n = fread(buffer, 1, 1024, fileIn);
while (n != 0)
{
fwrite(buffer, 1, n, fileOut);
n = fread(buffer, 1, 1024, fileIn);
}
fclose(fileIn); fclose(fileOut);
}

Exemple : crire et line un entier, un tableau, une structure

63

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.

#include <stdio.h>
int main(void)
{
FILE * fichier;
int n = 129;
double tab[3] = { 0.5, 3.14, 1.44 };
struct
{
int age;
char nom[50];
} personne = { 25, "Martin" };
fichier = fopen("test.dat", "wb");
if (fichier != NULL)
{
fwrite(&n, sizeof(n), 1, fichier);
fwrite(tab, sizeof(tab), 1, fichier);
fwrite(&personne, sizeof(personne), 1, fichier);
fclose(fichier);
}
n = 99;
tab[0] = 0; tab[1] = 0; tab[2] = 0;
personne.age = 99;
strncpy(personne.nom, "Dupont", 6);
personne.nom[6] = '\0';
fichier = fopen("test.dat", "rb");
if (fichier != NULL)
{
fread(&n, sizeof(n), 1, fichier);
fread(tab, sizeof(tab), 1, fichier);
fread(&personne, sizeof(personne), 1, fichier);
printf("n = %d\n", n);
printf("tab = { %lf, %lf, %lf }\n", tab[0], tab[1], tab[2]);
printf("personne = { %d, %s }\n", personne.age, personne.nom);
fclose(fichier);
}
return 0;
}

Problme de portabilit : criture dentiers, de structures (little endian vs big endian)


Se dplacer dans un fichier :
int fseek (FILE *stream, long offset, int whence); /* 0 si ok */
long ftell (FILE *stream); /* position dans le fichier depuis le dbut */

Arguments de fseek :
1) descripteur de fichier
2) dcalage (peut tre ngatif)
64

3) position de rfrence pour effectuer le dcalage


SEEK_SET : par rapport au dbut du fichier
SEEK_CUR : par rapport la position actuelle
SEEK_END : par rapport la fin du fichier
Exemple :
FILE * fichier;
fichier = fopen("donnees.dat", "rb");
if (fichier != NULL)
{
fseek(fichier, -50, SEEK_SET); /* 50 octets en arrire depuis la fin */
fseek(fichier, 10, SEEK_CUR);
/* 10 octets en avant / pos courante */
fseek(fichier, 20, SEEK_SET);
/* position 20 octets apres le debut */
fclose(fichier);
}

Lire et crire des caractres et chanes de caractres :


int
int
int
int

fprintf (FILE *stream, const char format, ...); /* comme printf */


fscanf (FILE *stream, const char *format, ...); /* comme scanf */
fgetc (FILE *stream); /* retourne EOF si fin de fichier */
fputc (int c, FILE *stream); /* copie un caractre */

Les fonctions fprintf et fscanf permettent de faire du filtrage mais ce sont des fonctions
dangereuses.
Exemple :

65

1. #include <stdio.h>
2.
3. int main(void)
4. {
5.
FILE * fichier;
6.
int n = 35, ip[4] = {0, 0, 0, 0};
7.
float pi = 3.14;
8.
9.
fichier = fopen("config.txt", "wb");
10. if (fichier != NULL)
11. {
12.
fprintf(fichier, "IP 193.48.120.32\n");
13.
fprintf(fichier, "n = %d\n", n);
14.
fprintf(fichier, "pi = %f\n", pi);
15.
fclose(fichier);
16. }
17.
18. n = 99;
19. pi = 1.44;
20.
21. fichier = fopen("config.txt", "rb");
22. if (fichier != NULL)
23. {
24.
fscanf(fichier,
"IP
%d.%d.%d.%d\n",
&(ip[0]),&(ip[1]),&(ip[2]),&(ip[3]));
25.
fscanf(fichier, "n = %d\n", &n);
26.
fscanf(fichier, "pi = %f\n", &pi);
27.
fclose(fichier);
28. }
29.
30. printf("IP %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
31. printf("n = %d\n", n);
32. printf("pi = %f\n", pi);
33.
34. return 0;
35. }
36.

Autre fonctions pour laccs au fichiers :


int open(const char *pathname, int flags);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const char *buf, size_t count);
int close(int fd);

Plus bas niveau mais plus complet


Spcifiques Unix et ne faisant pas partie des bibliothques standard

Utilise des entiers pour dfinir les descripteurs de fichiers : des


descripteurs de fichier particuliers stdin (0) stdout (1) stderr (2)
Erreurs frquentes :
Pb conversion dune machine une autre pour les fichiers textes et les caractres de retour la
ligne :
- Unix : '\n'
- Windows : '\r\n'
- MacOS : '\r'
66

14. Prprocesseur,
multi-fichiers
14.1.

headers

et

programmation

Prprocesseur

Phase intervenant avant la compilation du programme qui apporte plusieurs modifications sur le
code du programme :
- inclusion de fichiers en-tte comme #include <stdio.h>
- dfinition de constantes
- substitution par des macros
Ces directives peuvent intervernir nimporte ou dans le code du programme.

14.1.1

Dfinition de constante

Syntaxe :
#define <nom de constante> <expression quelconque>

A chaque fois que le precompilateur rencontre le nom de la constante, celle-ci est substitu par
lexpression qui peut contenir nimporte quoi.
Exemple : avant et aprs prcompilation

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

#define PI 3.1415
#define TEXTE "bonjour"
int main(void)
{
float x;
x = PI;
printf("%s", TEXTE);
}

14.1.2

int main(void)
{
float x;
x = 3.1415
printf("%s", "bonjour");
}

Usage des macros

Une macro est une simili fonction mais qui repose uniquement sur la substitution et ne fait aucune
vrification de type, ni darguments.
Syntaxe :
#define <nom_macro>(<arguments>) <expression>
Exemple :

67

1.
2.
3.
4.
5.
6.
7.
8.
9.

#define SOMME(a,b,c)

((a) + (b) + (c))

int main(void)
{
int x;
int n = 6;

int main(void)
{
int x;
int n = 6;

x = SOMME(n, 10, 23);


}

x = ((n) + (10) + (23));


}

14.1.3

Inclusion de fichiers en-tte (headers)

Le prprocesseur dispose dune directive permettant dinclure compltement le code contenu dans
un autre fichier que lon nomme en gnral fichier en-tte (header). Ces fichiers contiennent
principalement des dclarations de constantes, de types et de fonctions dont le code est gnralement
stock dans des bibliothques de fonctions. Cest le cas de la fonction printf dont la dclaration est
contenu dans le fichier stdio.h.
Syntaxe :
#include <nom_de_fichier>
#include "nom_de_fichier"

La premire forme permet dinclure les dclarations appartenant un fichier dit systme comme
stdio.h. La seconde forme permet dinclure un fichier local notre programme.
Exemple :
programme.h
#define AGE_MIN 18
#define TAILLE 40
typedef struct
{
int age;
char nom[TAILLE];
} Personne;
programme.c
#include "programme.h"
int main(void)
{
Personne p;

Rsultat de la prcompilation :
programme.c est modifi
typedef struct
{
int age;
char nom[40];
} Personne;
int main(void)
{
Personne p;
p.age = 5 + 18;
p.nom = "Martin";
return 0;
}

p.age = 5 + AGE_MIN;
p.nom = "Martin";
return 0;
}

Les directives dinclusion et les fichiers en-tte sont trs utiliss pour la compilation spare et la
cration de bibliothques. Ce point est abord dans la suite.

14.1.4

Erreurs frquentes

Ajout dun ; dans les macros


Ajout dun espace entre le nom de la macro et de la liste des arguments
Absence de parenthses

68