Vous êtes sur la page 1sur 53

UNIVERSITE IBN TOFAIL

Faculté des Sciences- Kénitra

Département d’Informatique

Programmation en Langage C

SMA-SMI 3

Année universitaire 2011- 2012

J. Alami Chentoufi 1
Table des matières
I. Introduction au langage C ------------------------------------------------------------------------------------------------ 5
I.1. Caractéristiques du langage C ------------------------------------------------------------------------------------- 5
I.2. Définitions -------------------------------------------------------------------------------------------------------------- 5
I.3. Premier programme en C ------------------------------------------------------------------------------------------- 6
I.4. Commentaires --------------------------------------------------------------------------------------------------------- 7
II. Les variables ---------------------------------------------------------------------------------------------------------------- 7
II.1. Déclaration d’une variable ---------------------------------------------------------------------------------------- 8
II.1.1. Syntaxe ------------------------------------------------------------------------------------------------------- 8
II.1.2. Types de base ----------------------------------------------------------------------------------------------- 8
II. 1. 3. Exemples de déclaration de variables --------------------------------------------------------------- 9
II. 2. Les constantes ----------------------------------------------------------------------------------------------------- 10
II.2.1. Constantes entières ------------------------------------------------------------------------------------- 10
II.2.2. Constantes réelles --------------------------------------------------------------------------------------- 10
II.2.3. Constantes caractères ---------------------------------------------------------------------------------- 11
II.2.4. Constantes chaînes de caractères ------------------------------------------------------------------- 11
II.2.5. Les constantes nommées ------------------------------------------------------------------------------ 11
II. 3. Les opérateurs standards en C --------------------------------------------------------------------------------- 12
II.3.1. L’opérateur d’affectation = ---------------------------------------------------------------------------- 12
II.3.2. Les opérateurs arithmétiques------------------------------------------------------------------------- 12
II.3.3. Les opérateurs de comparaison ---------------------------------------------------------------------- 13
II.3.4. Les opérateurs logiques -------------------------------------------------------------------------------- 13
II.3.5. Autres opérateurs particuliers ----------------------------------------------------------------------- 14
II. 4. Les conversions de types (Casting) --------------------------------------------------------------------------- 15
II. 5. Priorité des opérateurs ------------------------------------------------------------------------------------------ 15
III. Écriture et lecture des données ------------------------------------------------------------------------------------- 16
III. 1. Écriture formatée des données ------------------------------------------------------------------------------- 16
III. 2. Lecture formatée des données ------------------------------------------------------------------------------- 17
III. 3. Autres fonctions particulières de lecture et écriture ---------------------------------------------------- 19
IV. Instructions de contrôle ---------------------------------------------------------------------------------------------- 20
IV.1. Structures de choix ----------------------------------------------------------------------------------------------- 20
IV.1.1. L’instruction if…else ------------------------------------------------------------------------------------ 20
IV.1.2. L’instruction de branchement multiple switch -------------------------------------------------- 21
IV.2. Structures répétitives -------------------------------------------------------------------------------------------- 22

J. Alami Chentoufi 2
IV.2.1. La boucle for---------------------------------------------------------------------------------------------- 22
IV.2.2. La boucle while ------------------------------------------------------------------------------------------ 24
IV.2.3. La boucle do … while ----------------------------------------------------------------------------------- 25
IV.3. L’instruction break------------------------------------------------------------------------------------------------ 26
IV.4. L’instruction continue ------------------------------------------------------------------------------------------- 27
V. Les tableaux -------------------------------------------------------------------------------------------------------------- 28
V.1. Tableaux à une dimension--------------------------------------------------------------------------------------- 28
V.1.1. Déclaration ------------------------------------------------------------------------------------------------ 28
V.1.2. Initialisation d’un tableau ------------------------------------------------------------------------------ 29
V.1.3. Accès aux éléments d’un tableau -------------------------------------------------------------------- 29
V.1.4. Lecture des éléments du tableau -------------------------------------------------------------------- 29
V.1.5. Affichage des éléments du tableau ------------------------------------------------------------------ 30
V.2. Tableaux multidimensionnels ---------------------------------------------------------------------------------- 31
VI. Les chaînes de caractères -------------------------------------------------------------------------------------------- 32
VI.1. Déclaration et mémorisation d’une chaîne de caractères ---------------------------------------------- 32
VI.1.1. Déclaration ----------------------------------------------------------------------------------------------- 32
VI.1.2. Espace mémoire à réserver--------------------------------------------------------------------------- 32
VI.1.3. Mémorisation -------------------------------------------------------------------------------------------- 33
VI. 2. Initialisation de chaînes de caractères ---------------------------------------------------------------------- 33
VI.3. Déclaration, initialisation et mémorisation d’un tableau de chaînes de caractères ------------- 33
VI.4. Manipulation de chaînes de caractères --------------------------------------------------------------------- 34
VI.4.1. Fonctions de lecture et écriture --------------------------------------------------------------------- 34
VI.4.2. Autres fonctions de manipulation de chaînes de caractères -------------------------------- 35
VII. Les pointeurs------------------------------------------------------------------------------------------------------------ 36
VII.1.Introduction -------------------------------------------------------------------------------------------------------- 36
VII.2. Définition de pointeurs ----------------------------------------------------------------------------------------- 36
VII.3.Intérêts des pointeurs ------------------------------------------------------------------------------------------- 36
VII. 4. Déclaration et initialisation d’un pointeur ---------------------------------------------------------------- 37
VII.5. Opérations élémentaires sur les pointeurs ---------------------------------------------------------------- 37
VII.5.1. Opérateurs * et & -------------------------------------------------------------------------------------- 37
VII.5.2. Autres Opérations possibles ------------------------------------------------------------------------- 37
VII.6. Pointeurs et tableaux ------------------------------------------------------------------------------------------- 37
VII.7. Pointeurs et chaînes de caractères -------------------------------------------------------------------------- 39
VII.8. Allocation, Réallocation et libération dynamique de la mémoire ----------------------------------- 40

J. Alami Chentoufi 3
VII.8.1. Allocation dynamique de la mémoire ------------------------------------------------------------- 40
VII.8.2. Réallocation dynamique de la mémoire ---------------------------------------------------------- 41
VII.8.3. Libération de mémoire allouée --------------------------------------------------------------------- 42
VIII. Les fonctions ----------------------------------------------------------------------------------------------------------- 43
VIII.1. Définition de fonctions ---------------------------------------------------------------------------------------- 43
VIII.2. Retour d’une fonction ----------------------------------------------------------------------------------------- 44
VIII. 3. Déclaration et appel de fonctions -------------------------------------------------------------------------- 45
VIII. 4. Visibilité des variables ----------------------------------------------------------------------------------------- 46
VIII.4.1. Les variables globales -------------------------------------------------------------------------------- 46
VIII.4.2. Les variables locales ---------------------------------------------------------------------------------- 47
VIII. 5. Passage des paramètres de fonctions --------------------------------------------------------------------- 48
VIII. 5. 1. Passage des paramètres par valeur ------------------------------------------------------------- 48
VIII. 5. 2. Passage des paramètres par adresse ----------------------------------------------------------- 49
VIII. 6. Récursivité ------------------------------------------------------------------------------------------------------- 50
IX. Les structures ------------------------------------------------------------------------------------------------------------ 51
IX.1. Définition, declaration at accès aux champs d’une structure ------------------------------------------ 51
IX. 2. Rédéfinition de type avec typedef --------------------------------------------------------------------------- 52
IX.3. pointeurs et structures ------------------------------------------------------------------------------------------ 53

J. Alami Chentoufi 4
I. Introduction au langage C
Le langage C a été créé au début des années 70 par Dennis Ritchie et Brian Kernighan. Le
Langage C reste encore aujourd'hui un des langages les plus utilisé au monde.
Ce langage de programmation est à la base des systèmes d’exploitation ou au moins du
noyau de ces systèmes comme par exemple Unix/Linux. Le Langage C a justement été créé
pour un seul et unique but au départ, développer un système d'exploitation (Unix) mais au fil
du temps, grâce à sa puissance, il a été adopté par une large communauté de développeurs
ce qui a permis au langage d'évoluer et surtout d'être standardisé.
Le langage C fait partie des langages de programmation procédurale pour lesquels le
programme est divisé en blocs qui peuvent contenir leurs propres variables ainsi que
d’autres blocs.

I.1. Caractéristiques du langage C


Le langage C est l’un des langages les plus utilisés car il présente les caractéristiques
suivantes:

o Universel: permet aussi bien la programmation système que la programmation de


divers applications (scientifique, de gestion, …)
o de haut niveau: C est un langage structuré (offre plusieurs structures de contrôle) et
typé (déclarations obligatoires)
o prés de la machines de la machine : offre des opérateurs qui sont très proches de
ceux du langage machine…
o Portable : en respectant le standard ANSI-C, il est possible d’utiliser le même
programme sur d’autres compilateurs.
o etc

I.2. Définitions
Un programme C est un ensemble d'instructions qui se saisit dans un fichier dont
l’extension est .c à l'aide d'un éditeur, ce type de fichier s'appelle fichier source. Les
instructions qui y sont écrites s'appellent du code ou encore le code source.
Un compilateur est un logiciel qui lit le code source et le convertit en un code exécutable,
c'est-à-dire un ensemble d'instructions compréhensible par le processeur. La compilation
d'un code source se fait en deux étapes (figure 1): la compilation proprement dite et le
linkage (ou bien édition de liens). On utilise en général le terme compilation en englobant
les deux étapes précédemment citées.

J. Alami Chentoufi 5
Figure 1. Différentes phases de la programmation en C

1. La compilation : produit un fichier objet, dont l'extension est .obj.


2. Le code source peut être réparti dans plusieurs fichiers, ceux-ci doivent tous être
compilés séparément. Ensuite, le linkage a pour rôle de lier les fichiers objets (fichiers
simplement compilés) avec les fichiers précompilés d'une ou plusieurs bibliothèques
en produisant un fichier exécutable (.exe).

Certains environnements de développement servent d'éditeur, et prennent en charge la


compilation et le linkage (par exemple: dev-C++).

I.3. Premier programme en C


Le petit programme suivant permet d’afficher sur l’écran Bonjour, ceci est mon premier
programme en C !!
Ce programme contient une fonction principale appelée main dans laquelle on écrit
l’ensemble des instructions que la machine doit exécuter entre deux accolades ({ }). Dans cet
exemple l’instruction à exécuter est l’affichage de la phrase ‘Bonjour, ceci est mon premier
programme en C !!’ à l’aide de la fonction « printf ». La fonction printf est une fonction
prédéfinie dans la bibliothèque stdio.h (standard input output ou bien entrées sorties
standards). Pour cela, l’inclusion de la bibliothèque stdio.h (#include<stdio.h>) au début du
programme est nécessaire pour que le compilateur exécute les instructions correspondantes
à la fonction printf.

J. Alami Chentoufi 6
L'exécution d'un programme C entraîne automatiquement l'appel de la fonction main. Elle
constitue la fonction principale des programmes en C ; elle doit se trouver obligatoirement
dans chaque fichier source (.c).

#include<stdio. h>
int main ( )
{
printf ( "Bonjour, ceci est mon premier programme en C !!" ) ;
return 0 ;
}

Remarque :

En C, toute instruction simple est terminée par un point virgule (;). Dans le programme ci-
dessus, printf( "Bonjour, ceci est mon premier programme en C !!" ) est une instruction
simple qui correspond à un appel de la fonction d’affichage.

I.4. Commentaires
Pour rendre un programme plus compréhensible, on peut utiliser des commentaires. Un
commentaire est une séquence de caractères ignorée par le compilateur, on s'en sert pour
expliquer des portions de code.

On délimite un commentaire sur plusieurs lignes par /* et */. Si un commentaire s’étend


sur une ligne on utilise les caractères //

Exemple d’un programme commenté:

/* Ce programme vous affiche Bonjour !!


à l’écran */
/****************************************************/
/* Inclusion de la bibliothèque stdio.h pour utiliser la fonction
printf */
#include<stdio. h>
int main ( ) //Fonction principale
{
printf ( "Bonjour!!" ) ; //Affichage de Bonjour !!
return 0 ;
}

II. Les variables


Une variable est un emplacement de la mémoire dans lequel est stockée une valeur.
Chaque variable porte un nom et c'est ce nom qui sert à identifier l'emplacement de la
mémoire représenté par cette variable. Pour utiliser une variable donnée, la première étape
est la déclaration.

J. Alami Chentoufi 7
II.1. Déclaration d’une variable
Déclarer une variable, c'est prévenir le compilateur qu'un nom va être utilisé pour désigner
un emplacement de la mémoire.
Toute variable doit être déclarée avant les instructions et son type doit être spécifié dès la
déclaration.

II.1.1. Syntaxe
Pour déclarer une variable appelée var d’un type donné Type on utilise la syntaxe
suivante :

Type var ;

Pour déclarer plusieurs variables de même type, on utilise :

Type var1, var2, var3;

Remarque :

1. Un type définit l'ensemble des valeurs que peut prendre une variable, le nombre
d'octets à réserver en mémoire et les opérateurs que l'on peut appliquer dessus.
2. L’identificateur d’une variable peut être composé de lettres et de chiffres (le
caractère souligné _ est considéré comme une lettre). Le premier caractère d’un
identificateur doit être forcément une lettre.
3. Un identificateur d’une variable ne doit pas être l’un des mots réservés au
compilateur C. Ces mots réservés sont : auto, break, case, char, const, continue,
default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return,
short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile,
while.
4. Le compilateur C fait la différence entre minuscule et majuscule : VAR et Var sont
deux identificateurs différents

II.1.2. Types de base


Il existe 3 types de base en langage C : les caractères, les entiers et les réels.

a. Les entiers :

Trois types de base servent à représenter les entiers selon la taille mémoire nécessaire
pour stocker la variable :
Type Taille en octet Valeur Valeur
1. entiers courts : short int, minimale maximale
2. entiers longs : long int short int 1 -128 127
3. entiers standards : int int 2 -32678 32677
long int 4 -2147483647 2147483648

J. Alami Chentoufi 8
Remarque :

Si l’on ajoute le préfixe unsigned à l’un de ces types cités ci-dessus, alors on manipule des
entiers non signés (positifs).

1. unsigned short : implique des valeurs entre 0 et 255


2. unsigned int : entre 0 et 65535
3. unsigned long : entre 0 et 4294967295

b. Les caractères :

Les caractères disponibles dépendent de l’environnement mais on dispose des caractères


avec lesquels on écrit les programmes: les minuscules, les majuscules, les chiffres, les
séparateurs, et les signes de ponctuations.
Pour déclarer une variable de type caractère on utilise le mot clé char. Une variable de ce
type doit pouvoir contenir le code de n'importe quel caractère de l'ensemble des caractères
utilisé sur la machine.
Remarque :

 Une variable de type char occupe un octet en mémoire.


 Un caractère est un nombre entier (il s’identifie à son code ASCII). Par conséquent, une
variable de type char est associée à un nombre entre -128 et 127 et elle peut subir les
mêmes opérations que les variables de type entier.

Le lien suivant donne la liste complète de code ASCII des caractères


http://www.asciitable.com/.

c. Les flottants :

Les flottants servent à représenter les réels. Leur nom vient du fait qu'on les représente de
façon scientifique : un nombre décimal (à virgule) muni d'un exposant (un décalage de la
virgule). Trois types de base servent à représenter les flottants :

1. float : pour les nombres réels simple précision


2. double : pour les nombres double précision
3. long double : pour les réels très grande précision

Type Taille en Répartition des bits Domaine de valeurs


octet (signe, mantisse, exposant) approximatif
float 4 1, 23,8  1,1 10 38 à  3,4 1038
double 8 1, 52,11  2,2 10 308 à  1,7 10 308
long double 10 1, 64,15  3,4 10 4932 à  1,1 10 4932

II. 1. 3. Exemples de déclaration de variables


int a; /* déclaration de la variable a de type int */

J. Alami Chentoufi 9
int a, b; /* déclaration de deux variables a et b de type int */
char car_1, car_2 ; /* déclaration de deux variables car_1 et car_2 de type char (des
caractères) */
float r; /* déclaration de la variable r de type float */
double d_1, d_2; /* déclaration de deux variables réels d_1 et d_2 de type double */
Remarque:

1. La déclaration des variables au sein d’un programme C constitue une instruction


simple qui doit être terminée par un point virgule (exemple : int a;).
2. Quand il s’agit de déclarer plusieurs variables de même type en une seule instruction,
on sépare le nom de variables par des virgules (exemple : float a, b ; ).
3. Il est possible de donner une valeur initiale aux variables au moment de leur
déclaration. Par exemple :
int b = 4, c = 10;
float r = 1.2 ;
char car1 = ‘A’, car2= ‘b’;

II. 2. Les constantes


Dans un programme C, on peut manipuler les constantes littérales en nombre de 4:

1. Constantes entières
2. Constantes réelles
3. Constantes caractères
4. Constantes chaîne de caractères

II.2.1. Constantes entières


On dispose de 3 notations pour les constantes entières : décimale (base 10), octale (base 8)
et hexadécimale (base 16).
Les constantes décimales s'écrivent de la manière usuelle (ex : 15). Les constantes octales
(base 8) doivent commencer par un zéro et ne comporter que des chiffres octaux (ex : 0144,
0377). Les constantes hexadécimales (base 16) doivent commencer par 0x ou 0X et être
composées des chiffres de 0 à 9, ainsi que des lettres de A à F sous leur forme majuscule ou
minuscule (ex : 0X : 0x64, 0Xff).
II.2.2. Constantes réelles
Pour définir des constantes réelles, deux notations sont possibles : Notation décimale
(exemple : 12.5) ou exponentielle (exemple : 1 e-6, 3.1 e2)

Remarque :

Les constantes réelles sont par défaut de type double. Pour forcer le compilateur à
considérer la constante réelle comme étant un float on ajoute le suffixe f ou F, de même
pour utiliser une constante comme étant un long double on ajoute le suffixe l ou L.

J. Alami Chentoufi 10
II.2.3. Constantes caractères
Une constante caractère s'écrit entourée du signe apostrophe (‘). Par exemple, la
constante caractère correspondant au caractère C s'écrit 'C'.

Pour distinguer certains caractères spéciaux (les caractères de contrôle ou caractères non
imprimables), on utilise le signe \ (antislash).

Le tableau suivant donne les différentes constantes caractères spéciaux :

Constante Signification
'\'' une apostrophe
'\"' un guillemet
'\\' un back slash (anti slash)
'\a' un bip sonore
'\b' un retour arrière (backspace)
'\f' saut au début de la page suivante
'\n' saut de ligne (new line)
'\r' un retour chariot
'\t' une tabulation horizontale
'\v' une tabulation verticale
'\0' Caractère nul

II.2.4. Constantes chaînes de caractères


Une chaîne de caractères littérale est une suite de caractères entourés par des guillemets
(signe ").
Par exemple : "voici ma première chaîne de caractères constante"
Remarque :
Toutes les séquences d'échappement définies dans le tableau ci-dessus (paragraphe II.2.3)
sont utilisables dans le formatage les chaînes de caractères. Par exemple, l’instruction
suivante:

printf("Bonjour, \n\t celle-ci est ma première chaîne de caractères formatée") ;

Affiche à l’écran la chaîne suivante:


Bonjour,
Celle-ci est ma première chaîne de caractères formatée

II.2.5. Les constantes nommées


Pour donner un nom à une constante, on peut utiliser la ligne de commande au
préprocesseur : #define.
Lorsque le préprocesseur lit une ligne du type :
#define identificateur valeur
Il remplace dans toute la suite du code source, toute nouvelle occurrence
de identificateur par valeur. Par exemple on peut écrire :

J. Alami Chentoufi 11
#define PI 3.14159 et dans la suite du programme on pourra utiliser le nom PI pour désigner
la constante 3.14159.

II. 3. Les opérateurs standards en C


Le C est très riche en opérateurs. Parmi les opérateurs utilisés en C on peut citer:
l’opérateur d'affectation (=), les opérateurs arithmétiques (+, -, *, /, %), les opérateurs de
comparaison (==, !=, <, <=,>, >=), les opérateurs logiques (&&, ||, !), et d’autres opérateurs
spéciaux (++, --, sizeof).

II.3.1. L’opérateur d’affectation =


Il ne faut pas confondre l’opérateur d’affectation au symbole d’égalité. La syntaxe de
l’opération d’affectation est la suivante :

<variable> = <expression> ;

o Le membre à droite (c'est-à-dire expression) doit être une valeur ou une expression
calculée.
o Le membre à gauche (c'est-à-dire variable) doit être impérativement un élément
modifiable comme par exemple une variable on dit qu'il s'agit d'une « lvalue »
(contraction de left value).

Exemple:

int a, b ; // déclaration de deux variables a et b de type int


a = 30 ; //signifie que la valeur 30 est affectée à la variable a
b = a + 3 ; /* correcte, l’expression de droite (a+ 3) est évaluée et sera affectée à la variable b,
donc b aura la valeur 33*/
5 = a; /* Faux, on n'affecte pas une variable au nombre 5.*/
a+ 1 = 2; // est rejetée, le membre à gauche (a+1) n'est pas une variable mais une expression

II.3.2. Les opérateurs arithmétiques


Ils portent sur deux termes. Parmi eux : l’addition ( + ), la soustraction ( - ), la multiplication
( * ) et la division ( / ).

Exemple :

int a, b, c, d ;
b=2, c=1, d=4;
a = b * c + d + 8; // a= 2*1+4+8 =14
On ajoute les opérateurs parenthèses ( ) pour fixer l'ordre des calculs.
Exemple :
int a, b, c, d ;
b=2, c=1, d=4;

J. Alami Chentoufi 12
a = b * (c + d + 8); // a= 2*(1+4+8)=26
L'opérateur de division / retourne un quotient entier si les deux opérandes sont des entiers
et retourne un réel si l’un des deux opérandes est un réel.
Exemple:

24/6= 4
10/3= 3 (retourne un entier car les 2 opérandes sont des entiers)
10/4.0= 2.5 (retourne un réel, car l’un des opérandes est en notation réelle)
L'opérateur modulo, noté %, donne le reste de la division entière d'un entier par un autre.
Exemple :

10 % 5= 0, 22 % 7= 1, 25 % 19= 6

II.3.3. Les opérateurs de comparaison


Ils font des comparaisons sur des variables en utilisant la convention suivante: si la
comparaison est fausse l'opérateur renvoie 0, si elle est vraie l’opérateur renvoie une valeur
différente de 0.

= = : l'opérateur d’égalité
!= : l'opérateur différent de
< (resp. <=) : plus petit (resp. plus petit ou égal)
> (resp. >=) : plus grand (resp. plus grand ou égal)

Exemple :
x = 3, y = 0
x<y renvoie 0 (faux) et x > y renvoie 1(vrai)
x <= y renvoie 0 et x >= y renvoie 1
x != y renvoie 1 (vrai, x est différent de y)
x == y renvoie 0 (faux, x n’est pas égal à y)

Remarque : Il ne faut surtout pas confondre l'opérateur d'égalité == avec l'opérateur d'affectation =

II.3.4. Les opérateurs logiques


Le tableau suivant présente les 3 opérateurs logiques :

Opérateur Signification
! NON logique (négation)
&& ET logique
|| OU logique
Ces opérateurs logiques s'appliquent à des expressions booléennes (0 si faux et valeur non
nulle si vrai)
Le ET logique (&&) retourne la valeur 1 si les deux opérandes sont non nuls, et 0 si l’un des
opérandes est nul.
Le OU logique (||) retourne la valeur 1 si au moins un des opérandes est non nul, et 0 si tous les
opérandes sont nuls.

J. Alami Chentoufi 13
Table de vérité des 3 opérateurs logique :
X Y X&&Y X || Y ! X ! Y
1 0 0 1 0 1
0 1 0 1 1 0
1 1 1 1 0 0
0 0 0 0 1 1

II.3.5. Autres opérateurs particuliers


a. Les opérateurs d’incrémentation et de décrémentation :

Les opérateurs d’incrémentation « ++ » et de décrémentation « -- » permettent de


remplacer les deux instructions suivantes :

i = i + 1; <==> i++;
j = j - 1; <==> j--;
++ est :
–un opérateur de pré-incrémentation si la variable est placée à droite (++ i). Dans ce cas, la
valeur de la variable <i> est incrémentée, puis utilisée.
–un opérateur de post-incrémentation si la valeur est placée à gauche ( i++). Dans ce cas,
La valeur de la variable <i> est d'abord utilisée telle qu’elle, puis incrémentée.

Exemple

int m , n , p, q;
m =1 ; p=0 ;
n = m++ ; /* on passe d'abord la valeur de m à n et on incrémente après m */
/* Après cette instruction m vaut 2 et n vaut 1 */

q= ++p ; /*ici, on incrémente la valeur de p au début, puis on affecte la nouvelle valeur de


p à q. Après cette instruction, p vaut 1 et q vaut 1*/
- - est :
–un opérateur de pré-décrémentation si la variable est placée à droite (- - i). dans ce cas, la
valeur de la variable <i> est décrémentée, puis utilisée.
–un opérateur de post-décrémentation si la valeur est placée à gauche ( i - -). Dans ce cas,
La valeur de la variable <i> est d'abord utilisée telle qu’elle, puis décrémentée.

b. Les opérateurs combinés:

Le langage C autorise des écritures simplifiées lorsqu’une même variable est utilisée de
chaque côté de l’opérateur = d’affectation.

Exemple:

a = a + b est équivalent à a +=b


a = a – b est équivalent à a -=b
a = 2* a est équivalent à a *=2

J. Alami Chentoufi 14
c. L’opérateur sizeof:

L'opérateur sizeof retourne le nombre d’octets réservés en mémoire pour le type de


données ou la variable spécifiés entre parenthèses. La syntaxe de l’opérateur sizeof est la
suivante :
sizeof(<type>) ou sizeof(<variable>)
Exemple:
char var;
sizeof(char); /* retourne 1 (le type char occupe 1 octet en mémoire)*/
sizeof(var); /*retourne la valeur 1*/

II. 4. Les conversions de types (Casting)


Il est possible de forcer la conversion d’une variable (ou d’une expression) dans un autre
type avant de l’utiliser par une conversion implicite. Cette opération est appelée “cast”. Elle
se réalise de la manière suivante :

(<type>) <expression>

Exemple 1:

int a = 2;
float s, c = 89.9;
char d = (char)c;  d = 89, d= ‘Y’
s=(float) a;  s= 2.0

Exemple 2:

int a = 49;
int b = 4 ;
float c ;
c = a / b;  c = 12
c = (float) a/b  c = 12.25

II. 5. Priorité des opérateurs


Pour évaluer une expression, les opérateurs doivent respecter certaines lois de priorité et
d'associativité. Le tableau suivant présente la priorité des opérateurs.

J. Alami Chentoufi 15
Dans ce tableau, les opérateurs sont classés par priorité décroissante (même priorité pour
les opérateurs d'une même ligne). Les opérateurs les plus prioritaires seront évalués en
premier. L'associativité définit l'ordre d'évaluation des opérandes. La plupart se font de
gauche à droite (par exemple 20/2/5 donne (20/2)/5 donc 2 (et pas 20/(2/5))).

Exemples:

• La multiplication la division et le modulo ont la priorité sur l'addition et la


soustraction,
• La multiplication et l'addition ont la priorité sur l'affectation.
Remarque:

Dans une expression, les parenthèses forcent la priorité, car c’est l’opérateur qui a la
priorité la plus grande (tableau ci-dessus).

III. Écriture et lecture des données


Pour faire des entrées-sorties (lecture-écriture), il est nécessaire de faire appel aux
possibilités de la bibliothèque standard d’entrées sorties stdio.h. Celle-ci comporte des
fonctions permettant de réaliser des entrées sorties formatées. Il s'agit des fonctions printf
et scanf.

III. 1. Écriture formatée des données


La fonction printf est utilisée pour écrire du texte, des valeurs de variables ou des résultats
d'expressions vers la sortie standard stdout (par défaut l'écran).
La syntaxe de la fonction printf est : printf("<format>",<Expr1>,<Expr2>, ... )
"<format>" : est une chaîne de caractères qui peut contenir :
 du texte
 des caractères de contrôle (‘\n ‘, ‘\t’ , ‘\r’, etc)
 des spécificateurs de format indiquant la manière dont les valeurs des expressions
<Expr1,..> sont affichées à l’écran.
<Expr1>,... : variables et expressions dont les valeurs sont à afficher

J. Alami Chentoufi 16
Remarque :
1. Il est possible de faire l’écriture de plusieurs variables en utilisant une seule chaîne de
caractères contenant plusieurs descriptions de formats.
2. Il devra y avoir autant de variables dans la fonction printf qu'il y a de spécificateurs
de format.
3. Les spécificateurs de format commencent toujours par le symbole %.
Le tableau suivant présente les différents spécificateurs de format :

Spécificateurs de format

Exemple1 :
int jour = 7, mois = 10, annee = 2011;
printf (" On est le: \n \t%d / %d / %d" , jour, mois, annee);
Résultat:
On est le:
7 / 10 / 2011
Exemple 2:
int a = 5, b =2;
float d= (float) a/b;
printf (" La division de %d par %d est %f", a, b, d);
Résultat:
La division de 5 par 2 est 2.5

III. 2. Lecture formatée des données


La fonction scanf est la fonction symétrique à printf. Elle permet de récupérer n'importe
quelle combinaison de valeurs numériques, de caractères et de chaînes de caractères à
partir de l’entrée standard stdin (par défaut le clavier). La syntaxe de la fonction scanf est la
suivante : scanf("<format>",<&Var1>,<&Var2>, ...)

J. Alami Chentoufi 17
Où :
"<format>" : format de lecture des données,
<&Var1>,... : adresses des variables auxquelles les données seront attribuées.
Remarque :
1. Les spécificateurs de format de scanf sont identiques à ceux de printf, sauf qu'au lieu
de fournir comme arguments des variables à scanf, ce sont les adresses de ces
variables que l'on transmet.
2. L'adresse d'une variable est indiquée par le nom de la variable précédé du signe &.

Exemple1 :
#include <stdio.h>
int main ( )
{
int j, m, a; // déclaration de 3 variables de type entier
//printf affiche un message qui invite l’utilisateur à saisir des entiers
printf("Donner 3 entiers : \n ");
/*cette instruction lit 3 entiers séparés par des espaces, tabulations ou interligne.
Les valeurs sont attribuées respectivement aux 3 variables : j, m et a*/
scanf("%d %d %d", &j, &m,&a);
// Affichage des valeurs lues par scanf
printf("Voici les 3 valeurs lues : \n j : %d \n m : %d\n a : %d",j,m,a) ;
return 0 ;
}

Résultat :
Si on tape sur le clavier les valeurs suivantes : 7 ^10 ^2011
^ : est un espace entre les valeurs saisies au clavier
Le programme affichera :
Voici les 3 valeurs lues :
j:7
m : 10
a : 2011
Exemple 2:

#include <stdio.h>
int main ( )
{
int a,b;
float d;
char c;
/* printf affiche un message qui invite l’utilisateur à saisir des données de
différents type dans l’ordre */
printf("Donner 2 entiers, un réel et un caractère : \n ");
/*Cette instruction lit 2 entiers suivis d’un réel puis un caractère. Les valeurs sont
attribuées respectivement aux variables : a, b, d et c*/
scanf("%d %d %f %c", &a, &b,&d, &c);
// Affichage des valeurs lues par scanf
printf("Voici les différentes valeurs lues : \n a : %d \n b : %d\n d : %f \n c : %c",a,b,d,c) ;
return 0 ;
}

J. Alami Chentoufi 18
Résultat :
Si on saisit au clavier les valeurs : 1^2^2.5^A
On aura :
Voici les différentes valeurs lues :
a:1
b:2
d : 2.5
c:A

III. 3. Autres fonctions particulières de lecture et écriture


Ecriture d’un caractère :
La fonction "putchar()" manipule uniquement des caractères, c'est à dire des octets dont le
contenu correspond au code ASCII du caractère. Elle écrit le caractère passé en argument sur
la sortie standard (l'écran).

Soit car une variable de type char: putchar( car) est équivalente à printf(" %c " , c). La
forme générale de putchar est: putchar (<caractere>);

Exemple :

#include <stdio.h>
int main()
{
char car1 = ‘A’;
putchar(car1); // affiche la lettre A
putchar (‘L’); // affiche la lettre L
putchar(65); //affiche le caractère dont le code ASCII = 65 ç-à-d la lettre A
return 0;
}

Lecture d’un caractère :


La fonction getchar permet la récupération d'un seul caractère à partir du clavier. La
syntaxe d'utilisation de getchar est la suivante:
variable=getchar();
Où variable doit être de type char.
La fonction getchar est équivalente à scanf(" %c " , &variable).
Exemple :

#include <stdio.h>
int main()
{
char c;
c=getchar(); // Lecture d’un caractère
putchar(c); //Affichage du caractère lu
return 0;
}

J. Alami Chentoufi 19
IV. Instructions de contrôle
Les instructions de contrôle servent à contrôler le déroulement de l’enchaînement des
instructions à l’intérieur d’un programme. Ces instructions peuvent être des instructions
conditionnelles (de choix) ou itératives.

IV.1. Structures de choix


Les structures de choix permettent de déterminer quelles instructions seront exécutées et dans
quel ordre. En C , les structures de choix peuvent être exprimées par l’instruction de choix
conditionnel ( if … else) et l’instruction de branchement multiple (switch).

IV.1.1. L’instruction if…else


L'instruction if permet l'exécution d'une instruction ou d'un bloc d'instructions si et
seulement si la condition qui est une expression logique est vérifiée. La syntaxe est la
suivante:

if(condition)
instruction; /* ou un bloc d'instructions */

Exemple :
#include <stdio.h>
int main( ) {
int note = 16 ;
if (note> 14)
printf("Mention très bien ") ;
return 0 ;
}
if peut être aussi utilisée avec else (qui est optionnelle) et ainsi si la condition est
vérifiée l'instruction (ou le bloc d'instructions) directement après le if sera éxécutée
sinon (condition fausse) l'instruction (ou le bloc d'instructions) après le else sera
exécutée. La syntaxe complète de l'instruction if-else sera alors:

if(condition)
instruction1;
else // si condition n’est pas vérifiée
instruction2;

Exemple:

#include <stdio.h>
int main( ) {
int a = 1 ;
if (a> = 0)
printf("l’entier %d est positif ", a) ;
else
printf("l’entier %d est négatif ", a) ;
return 0 ;
}

J. Alami Chentoufi 20
Les instructions if…else peuvent être imbriquées l'une dans l'autre. Lors d'une telle situation
un else se rapporte toujours au premier if qui le précède.

Exemple:
if(condition1)
if(condition2)
instruction1;
else instruction2;
instruction3;
Lorsque condition1 n'est pas vérifiée c'est instruction3 qui est éxécutée sinon condition2
est testée si elle est vraie instruction1 est éxécutée et si elle est fausse instruction2 est
exécutée. Le else se rapporte ainsi au second if.

IV.1.2. L’instruction de branchement multiple switch


Cette instruction est appelée aussi instruction d’aiguillage. Elle teste si une expression
prend une valeur parmi une suite de constantes, et effectue le branchement correspondant
si c’est le cas. La syntaxe est la suivante:

switch(expression)
{
case cas1: i nstructions1;
case cas2: instructions2;
.
.
.
case casN: instructionsN;
default: instructions_par_défaut; /* le cas "default" étant facultatif */
}

Remarque :
Le type de ‘expression’ ne peut être qu’entier (char, int, long). L'expression est évaluée,
puis on passe directement au "case" correspondant à la valeur trouvée. Le cas default est
facultatif, mais s’il est prévu il doit être le dernier cas.

Le fonctionnement de switch est le suivant :


1. expression est évaluée ;
2. s'il existe un ‘case’ avec une constante qui est égale à la valeur de expression,
3. le branchement est transféré à l'instruction qui suit ce ‘case’;
4. si un tel ‘case’ n'existe pas, et si le cas default existe, alors le contrôle est transféré à
l'instruction qui suit le cas default ;
5. si la valeur de ‘expression’ ne correspond à aucun ‘’case et s'il n'y a pas de default,
alors aucune instruction n'est exécutée.

J. Alami Chentoufi 21
Remarque:

Lorsqu'il y a un branchement réussi à un ‘case’, toutes les instructions qui le suivent sont
exécutées, jusqu’à la fin du bloc ou jusqu’à une instruction de rupture (break).

Exemple :

Le programme suivant demande à l’utilisateur de saisir au clavier un numéro entier et


selon ce numéro le programme affichera le mois correspondant si l’entier saisi compris entre
1 et 12, sinon il affichera Mois invalide.
#include <stdio.h>
int main ( ) {
int mois;
printf(" donner le mois en numéro: " );
scanf(" %d " , &mois);
switch(mois){
case 1 : printf(" Janvier " ); break;
case 2 : printf(" Fevrier " ); break;
case 3 : printf(" Mars " ); break;
case 4 : printf(" Avril " ); break;
case 5 : printf(" Mai" ); break;
case 6 : printf(" Juin " ); break;
case 7 : printf(" Juillet " ); break;
case 8 : printf(" Aout " ); break;
case 9 : printf(" Septembre " ); break;
case 10 : printf(" Octobre" ); break;
case 11 : printf(" Novembre " ); break;
case 12 : printf(" Décembre" ); break;
default: printf(" Mois invalide " ); break;
}
return 0;
}

IV.2. Structures répétitives


Une structure répétitive ou boucle permet d'exécuter plusieurs fois de suite une même
séquence d'instructions. Cet ensemble d'instructions s'appelle le corps de la boucle. Chaque
exécution du corps d'une boucle s'appelle une itération.

En langage C, les structures répétitives peuvent être exprimées par les 3 boucles suivantes:

1. La boucle for
2. La boucle while
3. La boucle do … while

IV.2.1. La boucle for


La boucle for permet d'exécuter plusieurs fois la même série d'instructions. La syntaxe de
for est :

J. Alami Chentoufi 22
for ( expression1 ; expression2 ; expression3 )
{
liste d’instructions;
}

Le for s’utilise avec trois expressions, séparées par des points virgules, qui peuvent être
vides :

1. l’expression ‘expression1’ est réalisée une seule fois lors de l’entrée dans la boucle,
elle est appelée expression d’initialisation ;
2. l’expression ‘expression2’ est la condition d’exécution de la liste d’instructions. Elle
est testée à chaque itération, y compris la première. Si l’expression ‘expression2’
prend la valeur vraie la liste des instructions de la boucle for est exécutée, sinon la
boucle se termine ;
3. l’expression ‘expression3’ contrôle l’avancement de la boucle. Elle permet de
manière générale de calculer la prochaine valeur avec laquelle la condition de
passage va être testée, elle est exécutée après la liste des instructions à chaque
itération avant le nouveau test de passage.

La boucle for correspond à l’organigramme suivant (figure 2):

Initialisation Expression 1

Faux
Expression 2
Test
Vrai
Liste d’instructions

Expression 3

Figure 2. Organigramme de la boucle for

Remarque:

 ‘expression 1’ et ‘expression 3’ peuvent contenir plusieurs initialisations séparées


par des virgules.
 ‘expression 1’ et ‘expression 3’ peuvent être absente (dans ce cas les points
virgules doivent absolument apparaître). Exemple : for ( ; expression 2; )
 lorsque les 3 expressions sont absentes de la boucle for on parle de boucle infinie
for( ; ; )

J. Alami Chentoufi 23
Exemple 1: Exemple 2:

int main ( ){ /*programme qui affiche la table de


int i ; multiplication de 2 */
for(i=1 ;i<=10 ; i++) int main ( ){
printf("i=%d\n", i); int i=0;
return 0 ; for(i=0; i < 10; i++)
} printf("2 *%d = %d\n", i, i * 2);
return 0 ;
}

Exemple 3:

/*Ce programme calcule la somme des entiers de 1 à 100 :*/


int main ( ){
int n , somme ;
for( n=1, somme=0 ; n<=100 ; n++)
somme +=n;
printf("la somme des entiers de 1 à 100 est %d ",somme);
return 0 ;
}

IV.2.2. La boucle while


Avec cette structure de contrôle, tant qu'une condition est vraie, les instructions lui
correspondant sont exécutées. Sa syntaxe est la suivante :

while ( condition vraie)


{
liste d'instructions ;
}
Les instructions du corps de la boucle sont délimitées par des accolades. La condition est
évaluée avant chaque passage dans la boucle, à chaque fois qu'elle est vérifiée, on exécute
les instructions de la boucle.
Une fois que la condition n'est plus vérifiée, l'exécution se poursuit après l'accolade
fermante. La boucle while correspond à l’organigramme suivant (figure 3) :

Faux
condition

Vrai

Liste d’instructions

Figure 3. Organigramme de la boucle while

J. Alami Chentoufi 24
L’exemple suivant affiche tous les nombres de 1 à 5 dans l'ordre croissant :
int main( ){
int i = 1 ;
while ( i <= 5 )
{
printf ( "%d " , i ) ;
i ++;
}
return 0 ;}
Ce programme initialise i à 1 et tant que la valeur de i ne dépasse pas 5, cette valeur est
affichée puis incrémentée. Les instructions se trouvant dans le corps de la boucle sont donc
exécutées 5 fois de suite. La variable i s'appelle un compteur, on gère la boucle par
incrémentations successives de i et on sort de la boucle une fois que i a atteint une certaine
valeur (dans l’exemple la valeur 6).
Remarque :

 L'initialisation du compteur est très importante avant la boucle while.


 Il n'y a pas de point-virgule après le while.

IV.2.3. La boucle do … while


La syntaxe de la boucle do … while est la suivante :

do
{
instructions;
} while(condition vraie );

A l’inverse du while, le do… while place son test en fin d’exécution des instructions, d’où la
boucle do … while s’exécute au moins une seule fois même si la condition n’est pas vérifiée.
L’organigramme de la figure 4 représente le fonctionnement de la boucle do … while :

Liste d’instructions

Faux
condition

Vrai
Figure 4. Organigramme de la boucle do … while

J. Alami Chentoufi 25
Exemple 1:
Ce programme affiche les nombres de 0 à 9 :
int main( )
{
int i = 0;
do
{
printf("%d\n", i);
i = i + 1;
} while (i < 10);
return 0;
}

Exemple 2 :
Dans l’exemple suivant, la variable c est initialisée dans la boucle. On doit tester sa valeur à
la fin de l'itération. Tant que l'utilisateur n'a pas encore tapé la lettre ‘o’, le programme
redemandera de taper la lettre.
int main( )
{
char c;
do
{
printf("Veuillez taper la lettre 'o'\n");
scanf("%c", &c);
} while (c != 'o');
return 0;
}

IV.3. L’instruction break


L'instruction break peut être généralement employée à l'intérieur de n'importe quelle
boucle (for ; while ; do …while ; switch). Elle permet l’abandon de la structure et le passage
à la première instruction qui suit la structure.
En cas de boucles imbriquées, break fait sortir de la boucle la plus interne la première qui
contient le break.
Exemple 1 :
Ce programme affiche l’inverse des nombres lus au clavier à chaque fois on saisit un
nombre différent de zéro. Une fois on saisit un nombre nul on sort de la boucle for à l’aide
de break.
int n ;
for ( ; ; ) //boucle infinie
{
printf("donner un nombre (0 pour sortir) : ");
scanf("%d", &n);
if (n == 0) break;

J. Alami Chentoufi 26
printf("%f", (float)1/n) ;
}
Exemple 2:

l’exemple suivant permet d’effectuer la multiplication de i qui prend des valeurs de 1 , à 15


par j qui prend les mêmes valeurs sauf au cas où j a la valeur 5 la deuxième boucle est
abandonnée.
int main( )
{
int i, j ;
for (i = 1 ; i<=15 ; i++)
{
for (j = 1 ; j<=15 ; j++)
{
if (j == 5) break ;
else
printf("%d\t", i * j) ;
}
printf("\n") ;
}
return 0 ;
}

IV.4. L’instruction continue

Dans une instruction for, while ou do while, l'instruction continue provoque l'arrêt de
l'itération courante, et le passage au début de l'itération suivante.

Exemple :
Dans cet exemple, La boucle for s'effectue pour "i = 0,1,2,3,4,5,6,7,8,9". Si i est un multiple
de 3, c'est-à-dire que " (i%3) ==0 ", alors on effectue l'instruction « continue », qui permet
d’abandonner l’itération courante et de passer à l’itération suivante. Et donc, cette boucle
n'affiche pas les multiples de 3.
int main( )
{
int i;
for (i=0; i<10;i++)
{
if ((i%3)==0) continue;
printf("i = %d\t ",i);
}
printf("\nsortie : ");
printf("\n\ti = %d ",i);
return 0;
}
L’exécution de ce programme donne :
i=1 i=2 i=4 i=5 i=7 i=8
Sortie:
i = 10

J. Alami Chentoufi 27
V. Les tableaux

Les tableaux permettent de gérer un ensemble de variables de même type. Un tableau est
une zone mémoire constituée de cases contiguës où sont rangées des données de même
type. Les cases ont donc une taille identique. Un tableau possède un nombre fixé de cases
qui doit être connu quand il est créé. La zone mémoire possède donc un début et une fin.

On distingue deux types de tableaux : tableaux à une dimension et tableaux


multidimensionnels.

V.1. Tableaux à une dimension

V.1.1. Déclaration

La déclaration d’un tableau se fait selon la syntaxe suivante:

type nom_du_tableau [nombre_d'éléments];

Où :
« type » est le type de variable contenu dans de chaque case du tableau.
« identificateur » est le nom donné au tableau.
«nombre_d'éléments » est une valeur entière qui donne le nombre total de cases du
tableau.
Par exemple : int Tab [10] ; déclare un tableau appelé Tab qui contient 10 entiers. Cette
déclaration alloue donc en mémoire pour l'objet Tab un espace de 10 × sizeof(int)
consécutifs (sizeof retourne la taille d'un objet en octets).
Comme le montre la figure 5, le nom du tableau seul est une constante dont la valeur est
l’adresse du début du tableau.

Figure 5. Tableau de 10 entiers

J. Alami Chentoufi 28
V.1.2. Initialisation d’un tableau

L’initialisation d’un tableau se fait en mettant une accolade ouvrante, la liste des valeurs
servant à initialiser le tableau, et une accolade fermante. La figure 6 montre l’espace
mémoire correspondant à la définition d’un tableau de dix entiers avec une initialisation
selon la ligne :
int tab[10] = {9,8,7,6,5,4,3,2,1,0};

Figure 6. Tableau de dix entiers initialisé

V.1.3. Accès aux éléments d’un tableau


Les éléments d'un tableau à N éléments sont indicés de 0 à N- 1. On note T[i] l'élément
d'indice i du tableau T avec 0≤ i≤ N-1.
En C, le premier élément du tableau possède toujours l’indice 0. Par conséquent, un
tableau T déclaré de taille N possède les éléments T[0] , T[1] , ..., T[N −1] .
Exemple :
float Tab[3] ={1.3, 2.0,0.0} ; // déclaration d’un tableau de 4 réels avec initialisation
Tab[0] donne le premier élément du tableau c’est à dire la valeur 1.3. Dans ce cas l’indice
de case est égal à 0.
Tab [1] est le deuxième élément du tableau se trouvant dans la case 1 et qui a une valeur
égale à 2.0.
Tab[2] est le dernier élément du tableau qui a la valeur 0.0.

V.1.4. Lecture des éléments du tableau


Pour lire les éléments d’un tableau on utilise une boucle et on lit élément par élément
qu’on place dans leur espace mémoire approprié.
Le programme suivant donne un exemple de lecture d’un tableau de 10 réels :

J. Alami Chentoufi 29
int main()
{
int i ;
float Tab [10] ;
printf("Saisie des elements du tableau\n") ;
for (i = 0 ; i < 10 ; i ++)
{
printf("\ndonner l’élément d’indice %d : ", i) ;
scanf(" %f ", &Tab[ i ]);
}
return 0 ; }

« tab[i] » est une variable de type flottante et &tab[i] est l’adresse mémoire du ième
élément du tableau.

V.1.5. Affichage des éléments du tableau


Pour afficher les éléments du tableau, il faut parcourir tout le tableau. Pour cela on utilise
une boucle.
Le programme suivant donne un exemple de parcours d’un tableau en affichant ses
éléments.
int main()
{
int i ;
int Tab [10] ;
printf("Saisie des elements du tableau\n") ;
for (i = 0 ; i < 10 ; i ++)
{
printf("\ndonner l’élément d’indice %d : ", i) ;
scanf(" %d ", &Tab[ i ]);
}
printf("Affichage des elements du tableau\n") ;
for (i = 0 ; i < 10 ; i ++)
printf("\n Tab [ %d]=%d ", i, Tab[ i ]) ;
return 0 ;
}

Remarque :
Il est conseillé d'employer le plus possible des constantes dans les programmes (en
utilisant #define), notamment pour la taille des tableaux. Le code ci-dessus peut s'écrire
ainsi :
#include <stdio.h>
# define N 10
int main()
{
int i ;
int Tab [N] ;

J. Alami Chentoufi 30
printf("Saisie des elements du tableau\n") ;
for (i = 0 ; i < N ; i ++)
{
printf("\ndonner l’élément d’indice %d : ", i) ;
scanf(" %d ", &Tab[ i ]);
}
printf("Affichage des elements du tableau\n") ;
for (i = 0 ; i < N ; i ++)
printf("\n Tab [ %d]=%d ", i, Tab[ i ]) ;
return 0 ;
}

V.2. Tableaux multidimensionnels

De manière similaire, on peut déclarer un tableau à plusieurs dimensions. Par exemple,


pour un tableau à deux dimensions :
type nom_du_tableau[nombre_lignes][nombre_colonnes]

En fait, un tableau à deux dimensions est un tableau unidimensionnel dont chaque


élément est lui-même un tableau. On accède à un élément du tableau par l'expression
``tableau[i][j]''. Pour initialiser un tableau à plusieurs dimensions à la compilation, on utilise
une liste dont chaque élément est une liste de constantes.

Par exemple : int Tab[3][4] est un tableau bidimensionnel, est en fait un tableau
comportant 3 éléments, chacun d'entre eux étant un tableau de 4 éléments comme le
montre la figure 7 :

est stocké en mémoire


de la manière suivante
:

Figure 7. Représentation d’un tableau bidimensionnel

Exemple 1 :
#define M 2
#define N 3
/* Déclaration et initialisation d’un tableau d’entiers à deux dimensions*/
int tab[M][N] = {{1, 2, 3}, {4, 5, 6}};
/*Affichage du tableau*/
for (i = 0 ; i < M; i++)
{
for (j = 0; j <N; j++)
printf("tab[%d][%d]=%d\n",i,j,tab[i][j]);
printf("\n");
}

J. Alami Chentoufi 31
Exemple 2 :
#define M 2
#define N 3
/* Déclaration d’un tableau d’entiers à deux dimensions*/
int tab[M][N],i,j ;
/*Saisie des éléments du tableau */
for(i =0 ; i< M ;i++)
for(j =0 ; j< N ; j++)
{
printf("donner tab[%d][%d]: \n",i,j);
scanf("%d",&tab[i][j]);
}
/*Affichage du tableau*/
for (i = 0 ; i < M; i++)
{
for (j = 0; j <N; j++)
printf("tab[%d][%d]=%d\t",i,j,tab[i][j]);
printf("\n");
}
VI. Les chaînes de caractères

Il n'existe pas de type spécial chaîne de caractères ou string en C. Une chaîne de caractères
est traitée comme un tableau à une dimension de caractères (vecteur de caractères). Il
existe quand même des notations particulières et une bonne quantité de fonctions spéciales
pour le traitement de tableaux de caractères.

VI.1. Déclaration et mémorisation d’une chaîne de caractères

VI.1.1. Déclaration

Pour déclarer une chaîne de caractères en C, il suffit de déclarer un tableau de caractères.


Syntaxe : char NomVariable [Longueur];

Exemples :

char NOM [20]; /* Définit une chaîne de 19 caractères*/


char PRENOM [20]; /* on déclare une chaîne de 19 caractères*/
char PHRASE [300]; /* on déclare une chaîne de 299 caractères*/

VI.1.2. Espace mémoire à réserver

 Lors de la déclaration d’une chaîne de caractères, nous devons indiquer l'espace à


réserver en mémoire pour le stockage de la chaîne.
 La représentation interne d'une chaîne de caractères est terminée par le symbole '\0'
qui indique la fin de la chaîne de caractères. Ainsi, pour un texte de n caractères, nous
devons prévoir n+1 octets (un caractère occupe un octet en mémoire).

J. Alami Chentoufi 32
 Attention, il ne faut pas confondre le caractère de fin de chaîne de caractères ‘\0 ‘ avec
le caractère ’0’ dont le code ASCII est 48!
 Malheureusement, le compilateur C ne contrôle pas si nous avons réservé un octet pour
le symbole de fin de chaîne; l'erreur se fera seulement remarquer lors de l'exécution du
programme.

VI.1.3. Mémorisation
Le nom d'une chaîne est le représentant de l'adresse du premier caractère de la chaîne.
Pour mémoriser une variable qui doit être capable de contenir un texte de N caractères,
nous avons besoin de N+1 octets en mémoire.

Exemple:

char TXT[10] = "BONJOUR !";

VI. 2. Initialisation de chaînes de caractères

En général, les tableaux sont initialisés par l'indication de la liste des éléments du tableau
entre accolades:
char CHAINE[] = {'H','e','l','l','o','\0'};

Pour le cas spécial des tableaux de caractères, nous pouvons utiliser une initialisation plus
confortable en indiquant simplement une chaîne de caractère constante sans préciser la
dimension du tableau:
char CHAINE[] = "Hello";

Lors de l'initialisation par [], l'ordinateur réserve automatiquement le nombre d'octets


nécessaires pour la chaîne, c.-à-d.: le nombre de caractères + 1 (ici: 6 octets). Nous pouvons
aussi indiquer explicitement le nombre d'octets à réserver, si celui-ci est supérieur ou égal à
la longueur de la chaîne d'initialisation.
Remarque :
 Le symbole " peut être représenté à l'intérieur d'une chaîne de caractères par la
séquence d'échappement \" .(par exemple: "Affichage de \"guillemets\" \n")
 Le symbole ' peut être représenté à l'intérieur d'une liste de caractères par la
séquence d'échappement \' , exemple: {'L','\'','a','s','t','u','c','e','\0'}

VI.3. Déclaration, initialisation et mémorisation d’un tableau de chaînes de


caractères
Un tableau de chaînes de caractères correspond à un tableau à deux dimensions du type
char, où chaque ligne contient une chaîne de caractères.

J. Alami Chentoufi 33
La déclaration char JOUR[7][9]; réserve l'espace en mémoire pour 7 mots contenant 9
caractères (dont 8 caractères significatifs+ ‘\0’).

Lors de la déclaration il est possible d'initialiser toutes les composantes du tableau par des
chaînes de caractères constantes:

char JOUR[7][9]= {"lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"};

VI.4. Manipulation de chaînes de caractères

VI.4.1. Fonctions de lecture et écriture


La bibliothèque <stdio.h> nous offre des fonctions qui effectuent l'entrée et la sortie des
données. A côté des fonctions printf et scanf que nous connaissons déjà, nous y trouvons les
deux fonctions puts et gets, spécialement conçues pour l'écriture et la lecture de chaînes de
caractères.
a. Affichage d’une chaîne de caractères
printf avec le spécificateur de format %s permet d'intégrer une chaîne de caractères dans
une phrase.
puts est idéale pour écrire une chaîne constante ou le contenu d'une variable dans une ligne
isolée.

Syntaxe: puts( Chaine)


puts écrit la chaîne de caractères désignée par la variable Chaine sur stdout (écran) et
Effet:
provoque un retour à la ligne.
puts(Chaine); est équivalent à printf("%s\n",Chaine);
Exemple :
char TEXTE[] = "Voici une première ligne.";
puts(TEXTE);
puts("Voici une deuxième ligne.");

J. Alami Chentoufi 34
b. Lecture d’une chaîne de caractères
scanf avec le spécificateur %s permet de lire un mot isolé à l'intérieur d'une suite de
données du même ou d'un autre type.
scanf avec le spécificateur %s lit un mot du fichier d'entrée standard stdin (clavier)et le
Effet:
mémorise à l'adresse qui est associée à %s.

Exemple
char LIEU[25];
int JOUR, MOIS, ANNEE;
printf("Entrez lieu et date de naissance : \n");
scanf("%s %d %d %d", LIEU, &JOUR, &MOIS, &ANNEE);
gets est idéal pour lire une ou plusieurs lignes de texte (p.ex. des phrases) terminées par un
retour à la ligne.

Syntaxe: gets( Chaine)


gets lit une ligne de de caractères de stdin et la copie à l'adresse indiquée par la variable
Effet:
Chaine. Le retour à la ligne final est remplacé par le symbole de fin de chaîne '\0'.

c. Différence entre scanf et gets


En utilisant la fonction scanf, la lecture d’une chaîne de caractères se termine aussitôt
qu’un caractère séparateur est rencontré, alors que la fonction gets la lecture se termine à la
réception d’un retour à la ligne (touche return/ Entrée).

VI.4.2. Autres fonctions de manipulation de chaînes de caractères


La bibliothèque string.h comprend un grand nombre de déclarations de fonctions ayant
comme arguments des chaînes de caractères et retournant comme résultat un entier ou une
chaîne de caractères. Nous en citons ici quelques unes.
Fonction Rôle
char *strcat(char *dest, const char *src); concatène la chaîne src à la suite de dest
int strcmp(const char *, const char *); compare deux chaînes numériquement
char*strcpy(char *destination, const char *source); copie une chaîne de caractères source
dans la chaîne destination
int strlen(const char* ch); retourne la longueur de la chaîne ch
(sans compter le caractère \0 ).
char *strchr(const char *, int); cherche un caractère dans une chaîne et
renvoie un pointeur sur le caractère, en
cherchant depuis le début
char*strstr(const char *ch1, const char *ch2); trouve la première occurrence de la
chaîne ch2 dans la chaîne ch1
char *strlwr(const char *chaine) Convertit toutes les majuscules en
minuscules sans changer les autres
caractères.
char *strupr(const char *chaine) Convertit toutes les minuscules en
majuscules sans changer les autres
caractères.

J. Alami Chentoufi 35
VII. Les pointeurs

VII.1.Introduction
Toute variable manipulée dans un programme est stockée quelque part en mémoire
centrale. Cette mémoire est constituée d'octets qui sont identifiés par un numéro qu'on
appelle adresse.
Pour retrouver une variable, il suffit donc de connaître l'adresse de l'octet où elle est
stockée (ou, s'il s'agit d'une variable qui recouvre plusieurs octets contigus, l'adresse du
premier de ces octets : cas d’un tableau).
Pour des raisons évidentes de lisibilité, on désigne souvent les variables par des
identificateurs, et non par leur adresse. C'est le compilateur qui fait alors le lien entre
l'identificateur d'une variable et son adresse en mémoire. Toutefois, il est parfois très
pratique de manipuler directement une variable par son adresse.

VII.2. Définition de pointeurs


1. Un pointeur est une variable spéciale qui peut contenir l’adresse d’une autre
variable.
2. En C, chaque pointeur est limité à un type de données. Il peut contenir l’adresse
d’une variable de ce type.
3. Si un pointeur P contient l’adresse d’une variable A, on dit que ’P pointe sur A’.

VII.3.Intérêts des pointeurs


 En C, l’utilisation des pointeurs est indispensable car ils sont étroitement liés à la
représentation des tableaux.
 Les principaux intérêts des pointeurs résident dans la possibilité de :
1. Allouer de la mémoire dynamiquement, ce qui permet la gestion de
structures de taille variable, par exemple tableau de taille variable
2. Permettre le passage par adresse(ou par référence) des paramètres des
fonctions
3. Réaliser des structures de données récursives (Listes chainées)
4. etc

Remarque :
1. A la déclaration d’un pointeur p, il ne pointe a priori sur aucune variable précise : p
est un pointeur non initialisé. Toute utilisation de p devrait être précédée par une
initialisation.
2. Pour un pointeur sur une variable de type char, la valeur donne l’adresse de l’octet
où cette variable est stockée.
3. Pour un pointeur sur une variable de type float, la valeur donne l’adresse du
premier des 4 octets où la variable est stockée.’.

J. Alami Chentoufi 36
VII. 4. Déclaration et initialisation d’un pointeur

Déclaration : Un pointeur est une variable dont la valeur est égale à l’adresse d’une autre
variable. En C, on déclare un pointeur par l’instruction :
type *nom_du_pointeur ;
Où type est le type de la variable pointée, l’identificateur nom_du_pointeur est le nom de la
variable pointeur et * est l.opérateur qui indiquera au compilateur que c.est un pointeur.
Initialisation : Pour initialiser un pointeur, le langage C fournit l’opérateur unaire &. Ainsi
pour récupérer l’adresse d.une variable A et la mettre dans le pointeur P (P pointe vers A) :
P=&A

VII.5. Opérations élémentaires sur les pointeurs

VII.5.1. Opérateurs * et &


L'opérateur & (que nous avons déjà utilisé dans scanf) est un opérateur unaire qui fournit
comme résultat l'adresse de son opérande.
L'opérateur * désigne le contenu de l'adresse pointée ; par exemple, *iPointeur désigne le
contenu de la zone mémoire pointée par iPointeur.
int X=1, Y, *P Après l’instruction, P = &X ;
On a :
 Y = X + 1 équivalente à Y = *P + 1
 X += 2 équivalente à *P += 2
 ++X équivalente à ++ *P
 X++ équivalente à (*P)++

VII.5.2. Autres Opérations possibles

 On peut bien entendu affecter l’adresse d’un objet du bon type à un pointeur.
 Tous les opérateurs de comparaison sont utilisables avec les pointeurs, qui ne
peuvent toutefois être comparés qu’à un autre pointeur où à la constante NULL.
 Seules l’addition, la soustraction d’une valeur entière et la soustraction de deux
pointeurs sont des opérations arithmétiques sensées.
 Un entier, additionné ou soustrait à un pointeur est d’abord multiplié par la taille du
type pointé de façon à correspondre à un déplacement en nombre d’éléments de ce
type.
 Réciproquement, la soustraction de deux pointeurs sur un même type retourne le
nombre d’éléments de ce type situés entre les deux pointeurs.

VII.6. Pointeurs et tableaux


Tout tableau en C est en fait un pointeur constant. Dans la déclaration int tab[10]; tab est
un pointeur constant (non modifiable) dont la valeur est l'adresse du premier élément du
tableau. Autrement dit, tab a pour valeur &tab[0]. On peut donc utiliser un pointeur initialisé
à tab pour parcourir les éléments du tableau.
Les deux différences principales entre un tableau et un pointeur sont :

J. Alami Chentoufi 37
- un pointeur doit toujours être initialisé, soit par une allocation dynamique, soit par
affectation d'une expression adresse, par exemple p = &i ;
- un tableau ne peut pas figurer à gauche d'un opérateur d'affectation. En particulier, un
tableau ne supporte pas l'arithmétique (on ne peut pas écrire tab++;).

Exemple
En déclarant un tableau Tab de type int et un pointeur P sur int :
int tab[10];
int *P;
l'instruction:
P = tab; est équivalente à P = &tab[0];

Si P pointe sur une composante quelconque d'un tableau, alors P+1 pointe sur la
composante suivante. Plus généralement, nous avons :

P+i : pointe sur la ième composante du tableau elle est équivalente à &tab[i], avec i varie de
0 à9 dans l’exemple.
*(P + i) : est le contenu du ième élément du tableau tab c'est-à-dire tab[i].
Exemple :

#include <stdio.h>
main()
{
int T[6] = { 2, 6, 4, 5, 10, 8 }; //déclaration et initialisation d’un tableau de 6 entiers
int *p; //on déclare un pointeur sur int
p = T; // p pointe sur le premier élément de T (&T[0])
printf("%d\n", *p); // affiche 2
p++;
printf("%d\n", *p); // affiche 6
p += 4;
printf("%d\n", *p); // affiche 8
}

Ce programme peut être représentée par le graphique suivant :

J. Alami Chentoufi 38
VII.7. Pointeurs et chaînes de caractères

Les chaînes constantes de caractères sont, comme on l’avait déjà vu, un cas particulier de
tableaux de type char et possèdent une adresse mémoire. L’affectation suivante :
char *p= "Hello world"; (équivalente à char *p; p="Hello world";) : signifie que
l’on définit une variable pointeur ‘p’ de type char* à laquelle on affecte, comme valeur
initiale, non pas la chaîne "Hello world", mais l’adresse de cette dernière.
Le pointeur p peut être représenté par ce graphique:

Les instructions suivantes montrent comment afficher toute la chaîne de caractères en


utilisant le pointeur ‘p’ :
char * p = "Hello world";
for (p = p; *p != '\0'; p++) {
printf("%c ", *p); // Affiche : H e l l o w o r l d
}

Remarque :

Si ‘p’ change de valeur, le tableau de départ ne sera plus accessible, et donc la mémoire
qu'il occupait serait perdue.

Exemple :
char * p = "Hello world";
p= "Salut";
while (*p != '\0'){
printf("%c ", *p++); // Affiche : S a l u t
}
Voici un autre exemple où l’affectation d’une nouvelle valeur à un pointeur sur une chaîne
de caractères constante, fait perdre la chaîne constante initiale. D'autre part, un pointeur sur
char a l'avantage de pouvoir pointer sur des chaînes de n'importe quelle longueur:

char * p = "Hello world"; //(1)


char * q= "Salut"; //(2)
q=p ; //(3)
La représentation graphique de ces instructions est la suivante :

J. Alami Chentoufi 39
Les affectations discutées ci-dessus ne peuvent pas être effectuées avec des tableaux de
caractères. En effet, les chaînes de caractères déclarées comme pointeurs peuvent être
initialisées avec “=”, affectées avec “=”. Par contre, lorsqu’elles sont déclarées comme
tableaux de caractères, elles peuvent être initialisées avec “=”, mais pas affectées avec “=”.
Les instructions suivantes provoquent une erreur à la compilation :
char tab[] = "Hello world";
tab= "Salut"; // erreur
Dans ce cas, le compilateur alloue un tableau de 11 caractères qu'il initialise avec les
caractères H, e, l, l, o, ‘ ‘, w, o, r, l, d, et \0.
L’affectation dans ce cas doit être effectuée par la fonction déjà vue ‘’strcpy‘’. Le code
suivant corrige l’erreur précédente:
char tab[] = "Hello world";
strcpy(tab,"Salut");

VII.8. Allocation, Réallocation et libération dynamique de la mémoire


Pour pouvoir utiliser les fonctions de gestion dynamique de la mémoire, il est nécessaire
d’inclure le fichier d'en-tête standard stdlib.h.

VII.8.1. Allocation dynamique de la mémoire


Nous avons vu que l'utilisation de tableaux nous force à réserver plus de places en
mémoire que ce qu’il en faut. Si nous générons ces données, dont nous ne pouvons pas
prévoir le nombre et la taille lors de la programmation, il nous faut, en plus de l’utilisation
des pointeurs, des moyens pour réserver et libérer de la mémoire au fur et à mesure que
nous en avons besoin. Nous parlons alors de l'allocation dynamique de la mémoire.
Cette allocation dynamique de la mémoire est possible, grâce à la fonction malloc dont le
prototype est le suivant :
void *malloc(unsigned long taille);

Cette fonction prend en paramètre la quantité de mémoire, en octets, que l'on souhaite
allouer, et retourne, en cas de succès, un pointeur générique vers la zone mémoire qui nous
est nouvellement réservée. En cas d'échec, c'est un pointeur NULL qui nous est renvoyé.

J. Alami Chentoufi 40
Remarque :
Un pointeur générique est capable de pointer vers n'importe quel type d'objet. Il importe
alors de définir le type de donnée qui sera contenu dans cet espace mémoire. Cela est spécifié
devant l’appel de la fonction. Par exemple :
int *Tab = (int *) malloc(30 * sizeof(int));
La conversion est réalisée ici grâce à la mention (int *) qui transforme la valeur retournée
par malloc (void *)en une adresse vers un int. Cette instruction permet une ‘réservation
dynamique’ d'un espace mémoire de 30* sizeof(int)octets.
En plus de la fonction malloc, il existe d’autres fonctions pour réserver de la mémoire. Il
s’agit de la fonction calloc:
void * calloc( unsigned N, unsigned taille_type);
Elle réserve N éléments de taille_type octets chacun. Elle retourne un pointeur sur le
premier octet ainsi alloué ou NULL si l’allocation n’a pu être satisfaite. Les cases de l'espace
mémoire alloué par la fonction calloc contiennent des valeurs nulles.

VII.8.2. Réallocation dynamique de la mémoire


Il arrive que l'on ait besoin de modifier la taille d'un bloc mémoire que nous avions alloué
grâce à la fonction malloc. Pour cela, il nous faudra utiliser la fonction realloc, dont le
prototype est le suivant :
void *realloc(void *pointeur, unsigned long nouvelle_taille);

Le premier paramètre que prend cette fonction correspond à un pointeur vers un espace
mémoire qui avait été dynamiquement alloué au préalable, via la fonction malloc ou une
fonction équivalente. Si ce pointeur est nul, la fonction realloc agira de la même manière
que la fonction malloc, que nous avons étudié plus haut.
Le second paramètre correspond à la nouvelle taille que nous souhaitons pour notre bloc
mémoire. Celle-ci peut être inférieure, ou supérieure, selon vos besoins, à la taille d'origine.
En cas de succès, la fonction realloc retourne un pointeur sur la nouvelle zone mémoire, qui
peut ou non être la même que celle qui était déjà à votre disposition, selon l'état dans lequel
la mémoire se trouvait. En cas d'échec à la réallocation, la fonction retourne NULL ; notez
que, dans ce cas, vous ne devez pas considérer que les données pointées par le pointeur que
vous aviez passé en premier paramètre soient toujours valides !
A titre d'illustration, voici un petit exemple de réallocation de l'espace mémoire que nous
avons alloué un peu plus haut :
tab = realloc(tab, 60*sizeof(unsigned long));
if(tab != NULL){
// Utilisation de l'espace mémoire ré-alloué
}
else
{

J. Alami Chentoufi 41
// Echec de la ré-allocation mémoire
}

VII.8.3. Libération de mémoire allouée


Pour libérer un espace mémoire que vous aviez demandé précédemment à l'aide de la
fonction malloc, il convient d'utiliser la fonction free, dont le prototype est le suivant :
void free(void *pointeur);
L'emploi de cette fonction est plus simple : elle prend en paramètre un pointeur sur
l'espace mémoire à libérer, pointeur qui correspond à la valeur de retour de malloc, et ne
retourne rien.
Exemple :
main( )
{
int nombre_elements;
scanf(“%d”,&nombre_elements);
int *tab = (int *)malloc(nombre_elements*sizeof(int));
if(tab != NULL) {
int i;
for(i=0 ; i<nombre_elements ; i ++)
// Remplissage du tableau...
tab[i] =i;
for(i=nombre_elements-1 ; i>=0 ; i - -)
// Affichage du tableau (en ordre inverse)...
printf("%d\n", tab[i]);
free(tab);
}
else
printf("Echec de l'allocation mémoire\n");
}

J. Alami Chentoufi 42
VIII. Les fonctions
Jusqu'à cette partie, nous n’avons utilisé que des fonctions prédéfinies dans des librairies
standard (printf, scanf, …) et une seule fonction nouvelle: la fonction principale main().
Un programme C bien conçu contient en général de nombreuses petites fonctions plutôt
que peu de grosses fonctions. Chaque fonction contient une série d'instructions qui seront
exécutées à chaque fois que cette fonction sera appelée.
Cette façon de programmation par fonctions (programmation modulaire) permet de
rendre facile la compréhension de longs programmes, d'éviter les séquences d'instructions
répétitives et permet le partage d'outils communs qu'il suffit d'avoir écrits et mis au point
une seule fois.

VIII.1. Définition de fonctions


En général, le nom d'une fonction apparaît à trois endroits dans un programme:
1) lors de la définition
2) lors de la déclaration
3) lors de l'appel
Comme le langage C est déclaratif, normalement tout objet C doit être déclaré avant d'être
utilisé. Ceci s’applique à toute fonction qui pour pouvoir être utilisée, doit être définie voire
déclarée. Elle porte un nom, qui est un identificateur semblable à celui des variables. Elle
comprend un certain nombre de paramètres ou arguments (éventuellement aucun), qui ont
chacun un type spécifique. Elle renvoie rien (mot-clé void) ou une valeur de retour, dont le
type doit être également spécifié.
La structure d'une fonction en C est de la forme suivante :
Type_retourné nom_fonction(liste_paramètres_typés)
{
... /* Corps de la fonction. */
return(valeur); /* On peut aussi écrire : return valeur; */
}
Si Type_retourné vaut void (qui signifie "vide" en anglais), alors la fonction ne renvoie pas
de valeur. Dans ce cas, on n'utilise pas d'instruction return(valeur) comme dans l'exemple ci-
dessous.

Exemple :
Considérons une nouvelle fonction ‘’Affiche‘’, qui a pour rôle d’afficher les paramètres
passés en argument de la fonction. Sa définition peut se faire en C comme suit:
void Affiche( int x, int y) //En-tête de la fonction
{
printf("\nLes valeurs des parametres sont %d et %d", x, y) ;
}

J. Alami Chentoufi 43
Type_retourné : void,
nom_fonction : Affiche,
liste_paramètres_typés : int x, int y,
return : pas de return car Type_retourné : void.

La première ligne de la définition (en-tête de la fonction) contient :


• ‘’Affiche‘’ : un identificateur correspondant au nom de la fonction.
• ‘’void‘’ : le type de valeur renvoyée, cela indique que la fonction, après avoir effectué sa
tâche, ne renvoie rien à toute fonction qui a appelé ‘’Affiche‘’.
• ‘’ (int x, int y)‘’ : liste des paramètres, chacun a un type et un nom. Les parenthèses sont
obligatoires même s’il n y’a pas de paramètres.
 Le corps de la fonction dans, cet exemple, ne contient qu’une seule instruction ‘’printf ‘’.
Remarque :
L'imbrication de fonctions n'est pas autorisée en C: une définition de fonction ne doit pas
contenir d’autres définitions de fonctions. L’exemple suivant n’est pas autorisé :
void fonction1()
{
printf("\n fonction1 "\n);
int fonction2() //Incorrect, on peut pas définir une fonction au sein d’une autre
{
printf("\n fonction2 "\n);
}
}

VIII.2. Retour d’une fonction


Le type de la valeur retournée par une fonction peut être n'importe quel type (un type de
base ou même une structure). Dans le cas où la fonction ne retourne aucun résultat, le type
de retour sera void.
L’instruction return expression; n’importe où dans le bloc de la fonction, a pour effet de
sortir de la fonction et de retourner le résultat de l’expression au point d’appel de la
fonction.
Nous pouvons affecter la valeur retournée à une variable ou continuer à l’utiliser dans
l’expression qui a déclenché l’appel de la fonction. Nous pouvons aussi négliger ce résultat
complètement.
Exemple :
La fonction ‘’Somme ‘’, effectue la somme de deux entiers passés comme paramètres de la
fonction, et retourne la somme. La définition de cette fonction est:
int Somme(int x, int y) //En-tête de la fonction
{
return (x+y) ;
}

J. Alami Chentoufi 44
Type de retour : int,
Nom de la fonction : Somme,
Liste des paramètres typés : int x, int y,
return: x+y.
Le corps de la fonction dans, cet exemple, ne contient que la fonction ‘’return‘’.
La valeur retournée est une expression suivant le mot-clé return (ici : x+y).
Remarque :
Une fonction possède un seul point d'entrée, mais éventuellement plusieurs points de
sortie (à l'aide du mot return). L’instruction return met fin à l’exécution des instructions
d’une fonction et rend le contrôle du programme à l’instance appelante.
Exemple :
int Pas_print(int x, int y)
{
if (x>y)
return x;
else
return y;
printf("\x= %d, y=%d ", x, y) ;
}
Cette fonction possède deux points de sortie. Soit en retournant x si (x>y), soit en
retournant y dans le cas contraire. Notons que l’instruction ‘ printf ’ ne sera jamais exécutée.
En effet, comme nous venons de le dire, return met fin à l’exécution des instructions d’une
fonction et rend le contrôle à la fonction appelante.

VIII. 3. Déclaration et appel de fonctions


La déclaration d’une fonction (prototype) n’est pas obligatoire si sa définition se trouve
plus haut que son appel dans le programme. Mais au cas où nous souhaitons définir les
fonctions n’importe où dans le programme sans nous soucier de l’ordre de définitions et
appels, il faut déclarer les fonctions.
Une déclaration de fonction, qui se termine par un point-virgule, fournit des indications sur
le type, le nom et sur d’éventuels paramètres.
Les fonctions, une fois définies, peuvent être appelées dans le corps d'autres fonctions, ou
de la fonction elle-même (récursivité). La définition ou la déclaration d’une fonction doit se
faire avant l’appel de celle-ci. Pour cela, dans une expression, on placera le nom de la
fonction ainsi que les valeurs des paramètres réels (lors de l'appel) de cette fonction. Ces
paramètres peuvent bien sûr (mais ne doivent pas obligatoirement) avoir un nom différent
de celui employé lors de la définition de la fonction. Seul le nombre et le type des
paramètres doivent correspondre à celui de la définition.

J. Alami Chentoufi 45
Exemple de définition e t d'utilisation d'une fonction:

/ /Version1 / /Version 2
#include <stdio.h>
//Déclaration de la fonction Somme(prototype) #include <stdio.h>
int Somme (int x, int y); //Définition de la fonction Somme
void main(void) int Somme(int x, int y)
{ {
int i,s; return (x+y);
for (i=0; i<=10; i++) }
//Appel de la fonction Somme void main(void)
s= Somme(i,i)) ; {
} int i,s;
//Définition de la fonction Somme for (i=0; i<=10; i++)
int Somme(int x, int y) s= Somme(i,i)) ; //Appel de la fonction
{ Somme
return (x+y); }
}

VIII. 4. Visibilité des variables

Selon l'endroit où on déclare une variable, celle-ci pourra être accessible (visible) de
partout dans le code ou bien que dans un bloc d’instructions (à l'intérieur d'une fonction ou
d’une boucle par exemple), on parle alors de portée (ou visibilité) d'une variable.

En langage C, une variable peut être visible uniquement dans une fonction ou un bloc
d’instruction, on parle alors de variables locales. Lorsqu'une variable est déclarée dans le
code même, c'est-à-dire à l'extérieur de toute fonction ou de tout bloc d'instruction, elle est
accessible de partout dans le code (n'importe quelle fonction du programme peut faire
appel à cette variable). On parle alors de variables globales.

VIII.4.1. Les variables globales

Les variables globales existent pendant toute la durée de l'exécution du programme. Elles se
déclarent en dehors des blocs et des fonctions et sont visibles dans toutes les fonctions du
programme. On peut lire et modifier leur valeur n'importe où dans le programme.

Dans le programme suivant, n est une variable globale :


#include <stdio.h>
int n =0; //n est une variable globale
void fonction()
{
n++;
printf("Appel numéro %d\n",n);
}

J. Alami Chentoufi 46
main()
{
int i;
for (i = 0; i < 5; i++)
fonction();
}
La variable n est initialisée à zéro et il s'agit d'une variable à durée de vie permanente. En
effet, le programme affiche :
Appel numéro 1
Appel numéro 2
Appel numéro 3
Appel numéro 4
Appel numéro 5

VIII.4.2. Les variables locales

On appelle variable locale une variable déclarée à l'intérieur d'une fonction (ou d'un bloc
d'instructions) du programme. Par défaut, les variables locales sont temporaires.
Les variables locales n'ont en particulier aucun lien avec des variables globales de même
nom. Par exemple, le programme suivant :

#include<stdio.h>
int n = 10; // n est une variable globale
void Affiche()
{
int n = 0; // n est une variable locale à la fonction Affiche
n++;
printf("Appel numéro %d\n",n);
}
main()
{
int i;
for (i = 0; i < 5; i++)
fonction();
}
Affichera :
Appel numéro 1
Appel numéro 1
Appel numéro 1
Appel numéro 1
Appel numéro 1

J. Alami Chentoufi 47
VIII. 5. Passage des paramètres de fonctions
Il n'y a théoriquement pas de limite au nombre de paramètres (ou arguments) que peut
prendre une fonction. Chaque paramètre de la liste des paramètres doit être défini par son
type suivi de son nom. Comme pour le type du résultat, le type d'un paramètre peut être
soit un type de base soit un type composé comme par exemple une structure.

VIII. 5. 1. Passage des paramètres par valeur


Dans le cas d’un passage de paramètres par valeur, la fonction appelée reçoit plus
précisément une copie des valeurs de chaque variable passée comme paramètre. La
fonction travaille donc sur un duplicata et non sur l’original de la valeur transmise.
Autrement dit, les fonctions n'obtiennent que les valeurs de leurs paramètres et n'ont pas
d'accès aux variables elles-mêmes.

Donc, les paramètres d'une fonction sont à considérer comme des variables locales qui
sont initialisées automatiquement par les valeurs indiquées lors d'un appel. A l'intérieur de
la fonction, nous pouvons donc changer les valeurs des paramètres sans influencer les
valeurs originales dans les fonctions appelantes.

Exemple :

#include <stdio.h>
void Modifier(int a)
{
a += 5; // Modifie la copie de a
printf("à l’intérieur de la fonction: a= %d \n", a);
}
main()
{
int a = 5; // a est une variable locale à main
Modifier(a); // Appel de la fonction Modifier
printf("dans main: a = %d\n", a); /* Affiche la valeur de a après
appel à la fonction Modifier*/
}
L’exécution de ce programme s’effectue de la manière suivante :
1. le programme commence son exécution par la fonction main(),
2. la variable a est initialisée à 5 puis injectée dans la fonction ‘Modifier’ que l’on
appelle,
3. la copie de la variable a est modifiée dans la fonction a+=5,
4. à l’intérieur de ‘Modifier’ on affiche : à l’intérieur de la fonction: a=10,
5. on retourne à la fonction main() juste après l’appel de la fonction, le programme
affiche : dans main: a = 5
Ceci montre que le changement effectué sur la variable ‘a’ à l’intérieur de la fonction
‘Modifier’ (a=10) est resté local à cette fonction. Lorsqu’on retourne dans la fonction main(),
la variable a récupéré sa valeur originale (5). L’opération d’addition dans la fonction

J. Alami Chentoufi 48
‘Modifier’ n’a aucune influence sur cette variable. Cela montre bien que la fonction n’a
manipulé qu’une copie de la variable.

VIII. 5. 2. Passage des paramètres par adresse


Le passage des paramètres par valeur ne permet pas de modifier une variable de la
fonction appelante. Il semble donc que la seule interaction que puisse avoir une fonction
avec l'extérieur est soit le renvoi de son résultat soit l'utilisation de variables globales. Mais
en fait, grâce aux pointeurs, il est possible d'agir à distance. Il suffit pour cela d'envoyer non
pas la valeur d'une variable mais son adresse en mémoire (c'est à dire un pointeur sur cette
variable). La fonction appelée peut alors modifier le contenu de cet emplacement mémoire
(et donc le contenu de la variable elle-même).
Exemple :
#include<stdio.h>
void echange (int *a, int *b)
{
int t;
t = *a;
*a = *b;
*b = t;
}
main()
{
int a = 2, b = 5;
printf("debut programme principal :\n a = %d \t b = %d\n",a,b);
echange(&a,&b);
printf("fin programme principal :\n a = %d \t b = %d\n",a,b);
}

Remarque :
Rappelons qu'un tableau est un pointeur (sur le premier élément du tableau). Lorsqu'un
tableau est transmis comme paramètre à une fonction, ses éléments sont donc modifiés par
la fonction.
Exemple :
#include <stdlib.h>
void init (int *tab, int n)
{
int i;
for (i = 0; i < n; i++)
tab[i] = i;
}
main()
{
int i, n = 5;
int *tab;
tab = (int*)malloc(n * sizeof(int));
init(tab,n);
}

J. Alami Chentoufi 49
VIII. 6. Récursivité
Toute fonction récursive possède une condition d'arrêt et comporte dans sa définition au
moins un appel à elle-même.
Par exemple pour le calcul d'une factorielle nous avons ceci: n! = nxn-1x…x1. Dans cette
situation, nous pouvons déjà déterminer notre règle de sortie de notre fonction récursive: la
valeur 1 qui symbolise la fin de la récursivité.

Exemple de la factorielle :
int fact (int n ) {
if ( n < 0) return –1; //code d'erreur
else if ( n == 0 ) return 1; // 0! = 1
else
return n*fact( n-1 ); // n! = n*(n-1)!
}

J. Alami Chentoufi 50
IX. Les structures
IX.1. Définition, déclaration et accès aux champs d’une structure
Une structure est un ensemble de variables (de types éventuellement différents),
définissant un nouveau type sous un seul nom, adapté à une gestion spécifique et à une
manipulation facile des données.
Contrairement aux tableaux qui contiennent uniquement des valeurs de même type, une
structure peut comporter des variables de type long, char, int, double à la fois. Chaque
élément de la structure, appelé membre ou champ, est désigné par un identificateur.
Une structure se définit par un identificateur suivant le mot-clé struct, ainsi que par une
liste de champs ou membres définis par un identificateur d'un type donné. On distingue la
définition d'un modèle de type structure de celle de la déclaration d'un objet de type
structure correspondant au modèle défini.
 La définition d'un modèle de structure dont l'identificateur est modele suit la syntaxe
suivante :
struct modele {
type-1 membre1;
type-2 membre2;
...
type-n membren;
};

 La déclaration d’un objet de type structure correspondant au modèle précédent, se


fait de la manière suivante :
struct modele objet;
Remarque 1 : struct modele devient un nouveau type de données
Remarque 2 : On peut lors de la définition du nouveau modèle de structure faire la
déclaration d’un objet de ce modèle créé. Par exemple :
struct modele {
type-1 membre1;
type-2 membre2;
...
type-n membren;
} objet;

 L’accès aux différents champs des structures s’effectue en utilisant l’opérateur point
‘. ’ : objet.membre, objet.membre2, etc.
Exemples :
L’exemple suivant définit une structure d'objet d'un nouveau type Date, composé d'un
champ entier JOUR, un champ MOIS de 3 caractères, e.g. "jan" "fev" etc., et d'un champ
ANNEE qui est entier aussi.

J. Alami Chentoufi 51
struct Date {
int JOUR;
char MOIS[3];
int ANNEE;
};
La structure Date peut éventuellement être utilisée pour des champs d’une autre
structure. On peut donc imbriquer les structures les unes dans les autres. Par exemple la
structure suivante utilise la structure Date pour l’un de ses champs : Date_Nais. Ce dernier
est composé de JOUR, MOIS et ANNEE. Pour que cela fonctionne, comme pour les fonctions,
il faut déclarer la structure Date au-dessus de la déclaration de la structure Etudiant :

struct Etudiant{
char nom[30];
char prenom[30];
int numero;
struct Date Date_Nais;
};

 Pour déclarer des variables de type Etudiant, on peut écrire par exemple :
struct Etudiant E1, E2 ;
 Pour accéder aux champs de la variable E1, on peut écrire :
E1.nom ; // représente la chaîne de caractères nom
E1.prenom ; //représente la chaîne de caractères prenom
E1.num ; // l’entier num
E1.Date_Nais.JOUR ; // représente le JOUR de la date de naissance de E1
E1.Date_Nais.MOIS ;
E1.Date_Nais.ANNEE ;
 Pour déclarer un tableau de type Etudiant, on peut écrire : struct Etudiant Tab[20] ;

IX.2. Redéfinition de type avec typedef


Pour alléger l'écriture des programmes, on peut affecter un nouvel identificateur à un type
composé à l'aide du mot clé typedef :
Syntaxe : typedef type_existant Synonyme;
 typedef : indique que nous allons créer un alias d’un type existant.
 type_existant c'est le nom du type qu’on souhaite redéfinir.
 Synonyme : c'est le nom de l'équivalent.
Exemple :
struct complexe
{
double re; //partie réelle
double im; //partie imaginaire
};
typedef struct complexe Com;

J. Alami Chentoufi 52
main()
{
Com z; /* au lieu du struct complexe z ; */
...
}

IX.3. pointeurs et structures


Comme pour tout autre type de base, on peut définir un pointeur sur une structure.
Par exemple : Com *ptr déclare ptr comme un pointeur vers une structure complexe
(redéfinie en Com). Pour accéder aux champs de cette structure pointée par ptr on peut
écrire:
(*ptr).re
(*ptr).im
Il y a une autre notation possible pour accéder aux champs de la structure pointée par un
pointeur. Au lieu de (*ptr).re, on utilise l'opérateur -> pour écrire plus simplement :
ptr-> re
ptr->im

Exemple :
typedef struct Adresse{
int num_rue, code_postale ;
char ville[30],avenue[30] ;
} Adress ;

typedef struct personne {


Char nom[30], prenom[30] ;
Adress *a;
} Personne;
main(){
Personne P1, *P2 ;
printf(“ veuillez saisir les infos de la premiere personne \n ”) ;
printf(" donner nom et prenom : ") ;
gets(P1.nom) ; gets(P1.prenom) ;
printf(" donner son adresse num rue, code postale,ville, avenue ") ;
scanf(" %d% d%s%s ",&P1.a->num_rue,&P1.a->code_postale,P1.a->ville,P1.a->avenue);
printf(" veuillez saisir les infos de la deuxieme personne \n ") ;
printf("donner nom et prenom : ") ;
gets(P1.nom) ; gets(P1.prenom) ;
printf(" donner son adresse num rue, code postale, ville, avenue ") ;
scanf("%d% d%s%s ",&P1.a->num_rue,&P1.a->code_postale,P1.a->ville,P1.a->avenue);
}

J. Alami Chentoufi 53

Vous aimerez peut-être aussi