Professional Documents
Culture Documents
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
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.
3.
4.
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.
7.
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
9.
10.
Structures ........................................................................................................ 47
10.1.
10.2.
10.3.
10.4.
10.5.
10.6.
11.
14.
13.
Gnralits .................................................................................................................. 47
Structure et typedef ..................................................................................................... 48
Structures imbriques.................................................................................................. 49
Structures et tableaux.................................................................................................. 50
Structures et pointeurs................................................................................................. 51
Structures rcursives ................................................................................................... 53
12.
Typedef ........................................................................................................................... 45
type numr : dfinition de constantes symboliques..................................................... 45
Les fichiers.................................................................................................................. 61
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.
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
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
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.
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>
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.
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
%c
unsigned char
caractre
%s
char*
chane de caractres
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.
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
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
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>;
12
1.
2.
3.
4.
5.
6.
7.
8.
9.
int main(void)
{
int nombre;
nombre = 28;
nombre = 2;
return 0;
}
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;
}
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) || (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 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;
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;
}
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)
<<=
>>=
&=
|=
^=
n = 0x15;
n <<= 3;
n >>= 2;
n &= 0x1C;
n |= 0x1C;
n ^= 0x1C;
0x15 = 00010101 et 0x1C = 00011100
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
Nature
Codage
Valeurs
Caractre
Caractre non sign
1 octet
1 octet
-128 127
0 255
[-32768, 32767]
[0, 65535]
[-231, 231-1] si 4
octets
[0, 232-1] si 4 octets
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
[-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
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
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.
18
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;
}
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
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
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.
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;
}
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
Complmentaire
Faux
Vrai
Faux
Vrai
Faux
Vrai
Vrai
Vrai
Faux
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 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 !!!
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;
}
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;
}
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;
}
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;
}
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>;
}
( 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
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);
}
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;
}
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;
}
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
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
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';
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 */
[]
}
(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
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.
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 ;
}
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)
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. }
1.
2.
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
1.
2.
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;
}
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]
(*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;
}
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. }
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 */
*/
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.
*/
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.
*/
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;
}
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
44
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
p = &n;
(*p) = 12; /* instruction quivalent n = 12; */
return 0;
}
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.
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.
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 :
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.
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.
*/
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
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.
->
->
->
->
->
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.
53
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
Le schma suivant reprsente les relations entre les deux structures lissue du programme :
elment1
valeur = 5
suivant
element2
valeur = 10
suivant
54
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.
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.
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
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.
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.
57
11.6.
Erreurs frquentes
indirection illgale
char * ptr;
(*ptr) = (char *) malloc(100 * sizeof(char));
(*ptr) = 'y';
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));
}
58
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
<string.h>
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
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';
60
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)
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.
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;
}
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.
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;
}
Arguments de fseek :
1) descripteur de fichier
2) dcalage (peut tre ngatif)
64
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.
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");
}
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)
int main(void)
{
int x;
int n = 6;
int main(void)
{
int x;
int n = 6;
14.1.3
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
68