Vous êtes sur la page 1sur 90

Département Génie Informatique

Parcours Commun Mipc /GeGm

Informatique I : Initiation à la programmation

1
Contenu du cours :

 Les rudiments du C : déclarations, énoncés, expressions, E/S simples, …


 Énoncés de contrôle (sélection : if et switch, les 3 boucles de répétition)
 Tableaux à une et plusieurs dimensions.
 Fonctions et paramètres
 Recherche dans un tableau (séquentielle et dichotomique)
 Algorithmes de tri
 Récursivité
 Pointeurs
 Les fichiers

2
Sommaire
.

Structure d’un programme C :………………………………………….…………5


Les opérateurs et les expressions dans le langage C……………..……………….16
Les entrées–sorties conversationnelles……………………………………………30
Les instructions de contrôle…………………………………………………….…37
La programmation modulaire et les fonctions…………………………………….45
Les tableaux et les pointeurs……………………………………………………...57
Les pointeurs………………………………………………………………………65
Les fichiers…………………………………………………………………...……75
Exercices…………………………………………………………………………..80

3
Structure générale d’un
programme C :

4
Structure d’un programme C :

Un programme C est constitué d’une ou plusieurs fonctions.


Une fonction est une portion de code indépendante

La fonction main() ( dont le nom est imposé) est la seule fonction obligatoire d’un programme C. Elle
renferme la partie principale d'un programme.

Un programme C peut utiliser deux types de fonctions : les fonctions bibliothèques qui sont fournies par
le compilateur et les fonctions utilisateur qui sont créés par le programmeur.

Toute fonction est constituée d’un en-tête et d’un bloc.

Un bloc est délimité par des accolades ’’{‘’ et ‘’}’’. Il peut lui-même contenir d’autres blocs.

En revanche, une fonction ne peut jamais contenir d’autres fonctions.

<------bloc------------->

entête-Fonction { .............{.......}...{...{.......}..........}.......................}

<---------------corps de la fonction ---------------->

Tout programme C doit commencer par l’instruction d’appel #include indiquant au compilateur C qu’il
doit inclure le contenu de certains fichiers pendant la compilation

Exemple
#include <stdio.h> signifie ajouter le contenu du fichier <stdio.h> de la bibliothèque standard du
compilateur C. Ce fichier définit les fonctions se rapportant aux entrées-sorties .

Exemple de programme C :
#include <stdio.h>
void main()
{ printf ( “hello”);
}
printf est une fonction de la bibliothèque stdio.h qui affiche des information à l’écran.

Les éléments fondamentaux pour construire un programme C incluent :

1- L’ensemble des caractères de C

2- Les Identificateurs et les mots clés

3- Les types de données

4- Les constantes

5
5- Les variables et les tableaux

6- Les déclarations

7- Les expressions

8- Les instructions.

L’ensemble des caractères :

C utilise l’ensemble des caractères suivants :

1- Les lettres de A à Z et de a à z .
2- Les chiffres de 0 à 9
3- Les caractères : + - * / %
4- Les caractères jouant le rôle de séparateurs: ( ) [ ] { } ; < > + ! ? & # ` `` .
` : + = ?
5- C utilise aussi certaines combinaisons de ces caractères.

Exemple: \n new line


\t tabulation

Les identificateurs et les mots clés

1) les identificateurs
Les identificateurs servent à identifier les différents objets manipulés par le programme
( variables, tableaux, fonctions, étiquettes ...).

Un identificateur est formé d'une suite de caractères alphanumériques dont le premier


caractère doit être une lettre ou le caractère "souligné " ( _ ).

La longueur d’un identificateur peut aller jusqu’à 30 caractères ( certains compilateurs


reconnaissent seulement les 8 premiers caractères).

Exemple :
X
Somme_1
_temp
x

2) Les mots clés

Les mots sont des noms réservés par le langage et ne peuvent pas être utilisés comme
identificateurs par le programmeur.

6
auto default float register struct
break do for return switch while
case double goto short typedef
char else if signed union
const enum int sizeof unsigned
continue extern long static void

Remarque :

a) Dans C, une lettre majuscule n’est pas équivalente à la lettre correspondante en


minuscule.

b) Les mots clés sont tous en minuscule.

Les types des données :

La mémoire centrale est un ensemble de positions binaires ou bits.


Les bits sont regroupés en octets ou bytes ( 8 bits).
Un octet est repéré en mémoire par une adresse, il contient une valeur binaire qui correspond au code
d'une information traitée ( nombre, caractère,instruction de la machine...).
Si un octet de la mémoire centrale contient le code binaire suivant : 01000001
Ce code peut représenter soit :
+ le nombre entier décimale 65 ( en base 2)
+ le caractère `A` ( en code ASCII)
+ une partie d'une donnée codée sur 2 octets.
Il se pose donc un problème quant à la signification à donner à une information en mémoire.
La notion de type peut résoudre ce problème .

Le langage supporte plusieurs types de données. Chacun des types peut être représenté différemment en
mémoire.
Les types de base du langage C se répartissent en 3 grandes catégories :

Le type entier ( mot clé int) correspond à la représentation des nombres entiers relatifs.

Le type flottant ( mot clé float ou double) représente les nombres réels.

Le type caractère ( mot clé char).

Les types de base sont les types simples à partir desquels pourront être construits tous les autres types
tels que : les types structurés ( tableaux, structures...) et d’autres types simples ( pointeurs).

1) Le type entier

Les différents types d’entiers :

7
short int (abrégé short)
int
long int (abrégé long)

Chacun de ces types peut être signé ou pas . Il est précédé du mot clé unsigned ou signed.
Chaque type n’a pas la même taille en mémoire .

2) Le type flottant

Le type flottant représente de manière approchée les nombres réels.


On distingue 3 types de flottants correspondants á des tailles différentes et des précisions différentes:

float
double
long float
long double

L'erreur de troncature due à une représentation approchée d'un nombre réel ne dépasse pas : 10-6
pour le type float
10-10 pour le type long double
Dans le premier cas , on gagne en place et en contrepartie on perd en précision, inversement pour le
second type.

3) Le type caractère

On distingue :
les caractères imprimables qui se notent en écrivant entre quote le caractère voulu :

`a` `A` `6` `+`

Les caractères non imprimables qui ont une fonction particulière. Ils se notent en faisant précéder le
caractère voulu de `` \ `` ( backslash)

CARACTERE FONCTION

\b backspace
\n new line
\f form feed
\t horizontal tabulation
\v vertical ------
\r carriage return ( retour chariot)
\\ \ (backslash)
\` ` (apostrophe)
\`` ``( quote)
\? ?
\0 null ( fin d’une chaine de caractères)

8
Représentation en mémoire des différents types :

Type description Taille en mémoire valeurs représentées


de sur PC
donnée
int Valeur entière Sur 2 octets on représente des
- short et int sont représentés valeurs de – 32768 à 322768
sur 2 octets (16 bits)
Sur 4 octets, on représente
- long est représenté sur 4 des valeurs de –2147483 à
octets 2147483648

float Nombres réels - Float représente des réels de


float est représenté sur 4 octets
(32 bits) 3.4 10-38 à 3.4 1038
double représente des réels de
double - double est représenté sur 8 1.710-308 à 1.7 10308
octets

- double peut être équivalent


au long float.
char caractère
Un caractère est représenté sur
1 octet

Les constantes

Une constante représente une valeur d’un type donné dans un programme.
Il y a 4 types de constantes :

1) Les constantes entières : (de type entier )

Plusieurs représentations possibles :


a) représentation décimale :

1432
- 056

b) représentation octale :
Le premier chiffre doit être un 0 (zéro).

015 ( vaut 13 en décimal)

c) représentation hexadécimale :
la constante doit commencer par 0x

9
0xff ( vaut 255 en décimal).

2) Les constantes flottantes :

Notation décimale des flottants: 12.433


.27
4.
0.381234
- .38
Notation exponentielle : mantisse E exposant
42.5E4
4e13

3) Les constantes caractère :

`C` `c` `*` `0`

4) Les constantes chaînes de caractères :

Un chaîne de caractères est constituée dune séquence de caractères délimitée par des double quotes
`` .........``.
Toute de chaîne de caractères se termine en mémoire par le caractère null (\0) généré automatiquement
par le compilateur.

``chaîne`` ``$19.99`` ```` ( chaîne vide)

``A`` ( différent du caractère `A` ) car elle est représentée sur 2 octets.

``ab\``c`` interprété par ab``c (suivi d'un caractère de fin de chaîne)

``line1\nline2\n`` -------------> line1 ( aller à la nouvelle ligne)


line2

``\t to continue, press the \``Return \``key \n`` qui sera interprété par

(tabulation horizontale)to continue, press the``Return ``key (aller à la nouvelle ligne)

Les constantes symboliques

Une constante symbolique est un nom qui substitue une séquence de caractères dans un
programme C.
La séquence de caractères peut représenter :

10
• une constante numérique
• une constante caractère
• une chaîne de caractère
• A la compilation du programme C, chaque occurrence de la constante symbolique est remplacée par
la séquence de caractères correspondante.

Elle sera définie dans le programme suivant la syntaxe suivante:

#define nom text

où nom correspond au nom de la constante. Il doit être écrit en lettres majuscules.


text correspond à la séquence de caractères.

Exemple:
#include<stdio.h>
#define TAXE 0.34
#define TRUE 1
#define PAYS ``canada``
void main()
{
int c;
c = true ;
printf(``%f``, TAXE);
printf( "%s", PAYS)
}

6) Les constantes énumérées

Les constantes énumérées permettent de créer de nouveaux types puis de définir des variables dont
l’intervalle des valeurs est délimité.
par exemple, le type couleurs sera associé aux valeurs suivantes: rouge, noir, blanc, vert, et bleu.

Le mot clé enum permet de définir ce nouveau type dont le domaine des valeurs sera défini par
énumération

Exemple1: enum couleurs { rouge, noir, blanc, vert, et bleu }

A chaque constante correspond une valeur entière qui est soit définie explicitement soit 0,1,2 en
fonction de sa position dans l’énumération

dans l'exemple précédent:


rouge est une constante symbolique dont la valeur est égale à 0
noir est une constante symbolique dont la valeur est égale à 1 etc.…

Exemple2: enum couleurs {Rouge=10,noir=20, blanc, vert, bleu = 30}


Comme la constante symbolique noir a la valeur 20, bleu est associé à la valeur 21 et vert correspond à
22.

11
Les variables et les tableaux

Une variable est un identificateur qui est utilisé pour représenter une donnée d'un certain type
( numérique ou caractère) dans un programme C.

• On doit lui associer un certain type qui ne doit pas changer ( déclaration de la variable).

Exemple :
enum couleurs { rouge , noir, bleu, vert, blanc};
int a, b; ( variables entières)
char c; ( variable caractère)
couleurs clr ;
• Dans le programme, on doit lui affecter des valeurs du même type.

b = 5;
a = 3;
c = `A`;
a = a + b;
clr = rouge ;

Un tableau est un autre type de variable qui est utilisé pour représenter une collection de
données de même type.

Exemple :
Si X est un tableau de 10 éléments.
Le premier élement du tableau est noté X[0]
Le 10ème élément du tableau est noté X[9]

On distingue: - Les tableaux à une dimension


- Les tableaux à plusieurs dimensions.

• Les tableaux à une dimension de type caractère sont utilisés pour représenter les chaînes de
caractères. Chaque élément du tableau représente un caractère de la chaîne.

• Si le tableau contient n éléments, la valeur de l’indice du tableau varie de 0 à n-1.

• Une chaîne de n caractères est représentée par un tableau de n+1 éléments ( le n+1 ième élément
correspond au caractère null de fin de chaîne ).

``canada`` sera représentée dans le tableau tab par: tab[0]= `c`


tab[1]= `a`

tab[5]= `a`
tab[6]= `null`

12
Les déclarations dans C:

Une déclaration associe un type spécifique à un ensemble de variables.

Dans C, toutes les variables doivent être déclarées avant leur manipulation dans les instructions.
Une déclaration est formée d’un type suivi d’un ou plusieurs noms de variables séparés par des virgules.

Toute déclaration dans un programme doit se terminer par un point virgule ( ; )

Exemple:
int a,b;
char c,text[80];
float r;
Int i;
unsigned x; ( entier positif)

Dans une déclaration, on peut assigner une valeur initiale à une variable.

int a = 13;
const int b = 6; /* la valeur de b ne doit pas changer */

char c = `*`;
char text[ ] = ``canada``; // equivalent à char text[ 7 ] = ``canada``;

float r = 0;
Les expressions

L’expression la plus simple est constituée de :


• Une constante
• Une variable
• Un élément de tableau
• Une référence à une fonction
Les expressions complexes sont constituées d’expressions plus simples avec des opérateurs.

Exemple :
int a,b;
int x,y;
a + b est une expression arithmétique qui fait la somme des valeurs entières de
a et b.

x=5 L` opérateur d’affectation `=` assigne la valeur 5 à la variable x.

x <= y est une expression logique qui prend la valeur 1 (true) si la valeur de x
est inférieure ou égale à la valeur de y, et 0 (false) sinon.

x = = y est une expression relationnelle qui compare les valeurs de x et y.

13
i++ ( i=i+1) incrémente de 1 la valeur de i .
Les instructions

Une instruction permet d'exécuter une action dans un programme.

On distingue 3 types d’instructions dans C:


1) les instructions expression
• l'affectation a=3
• l'incrémentation b++
• l'appel de fonction printf( ``hello``)

2) les instructions composées


Une instruction composée est un ensemble d’instruction entre { et }

Exemple
{ a = 3;
c= 2 * a;
printf ( ``%f``,c);
}

3) les instructions de controle


Les instructions de choix
if ( condition ) instruction else instruction
switch ….. case…….default
Les instructions de branchement
goto etiquette
continue
break
Les instructions de boucle
do instruction while ( condition)

for ( initialisation, condition, incrémentation) instruction

while ( condition) instruction

14
Les opérateurs et les expressions
dans le langage C.

15
Les opérateurs et les expressions dans le langage C.

Les expressions en langage C peuvent être constituées de variables, constantes, éléments de


tableau et références à des fonctions combinés entre eux à l’aide d’opérateurs.
On distingue différentes catégories d’opérateurs:
• Les opérateurs arithmétiques
• Les opérateurs relationnels et logiques
• Les opérateurs d’affectation
• Les opérateurs d’incrémentation et décrémentation
• Les opérateurs d’affectation élargie
• L'opérateur Cast
• L’opérateur conditionnel
• L’opérateur séquentiel
• L'opérateur sizeof

De façon générale, une expression est une combinaison d’opérateurs et d’opérandes dont le résultat est
une valeur.
L’expression possède une valeur mais peut réaliser une affectation à une variable. Car les opérateurs
d’affectation et d’incrémentation peuvent non seulement intervenir dans une expression ( qui aura une
valeur ) mais agir sur le contenu des variables.

Exemple:

++i est une expression qui réalise une action: incrémenter la valeur de i.
Elle aura comme valeur, la valeur de i aprés incrémentation.

i = 5 est une expression de valeur 5 qui réalise une action : affecter à i la valeur 5.

k = (i = 5) La valeur de l’expression ( i = 5) est affectée á la valeur de k.

En fait, les notions d’expression et d’instruction sont étroitement liées.

Quand une expression possède plusieurs opérateurs, l’ordre dans lequel les opérations sont effectuées est
important.
Le but de ce chapitre est de présenter les opérateurs définis dans le langage C avec les règles de priorité
et de conversion de type intervenant dans les évaluations d’expressions.

16
II-1 Les opérateurs arithmétiques:

On distingue:

a) 5 opérateurs arithmétiques binaires

+ addition
- soustraction
/ division
* multiplication
% . reste de division entière (modulo)

• Les opérateurs binaires ne sont définis que sur deux opérandes ayant le même type : ( int,
long int, float, double,long double ). Ils fournissent le même type que leurs opérandes.

• L` opérateur modulo ( % ) ne peut porter que sur des entiers.

Exemple :

Etant données a et b des variables de type entier de valeurs respectives 10 et 3.

Expression sa valeur

a+b 13
a–b 7
a*b 30
a /b 3
a%b 1

Etant données v1 et v2 deux variables de type flottant et dont les valeurs respectives sont 12.5 et 2.0.

Expression sa valeur

v1 + v2 14.5
v1 – v2 10.5
v1 * v2 25.0
v1 / v2 6.25

Etant donnés c1 et c2 de type caractère représentant respectivement les caractères ‘P’ et ‘T’.

17
Expression sa valeur

c1 80 ( code ASCII de P)
c2 84 ( code ASCII de T)
c1 + c2 164
c1 + c2 + 5 169
c1 + c2 + `5` 215

Le code ASCII du caractère `5` est 53.

b) Un opérateur unaire : - qui ne porte que sur un opérande.

LES PRIORITÉS RELATIVES DES OPERATEURS

Lorsque plusieurs opérateurs arithmétiques apparaissent dans une expression, il est nécessaire de savoir
dans quel ordre ils sont mis en jeu.
L’ordre de priorité des opérateurs arithmétiques est le suivant:

- ( opérateur unaire ) plus prioritaire

* / %

+ - moins prioritaire

• L’évaluation des expressions arithmétiques suit l’ordre de précédence des opérateurs.


• Dans le cas où des opérateurs ont des priorités identiques, l’évaluation de l’expression se fait
de gauche à droite.
• L’utilisation des parenthèses dans une expression permet d’altérer les règles de priorité. Les
sous expressions parenthèsées sont évaluées en priorité.

LES CONVERSIONS IMPLICITES DANS UN CACUL D’EXPRESSION:


18
Une expression mixte est une expression, dans laquelle interviennent des opérandes de types
différents.
Dans une expression mixte, le compilateur met en place des instructions de conversion de la valeur d’un
opérande pour obtenir une expression dont tous les opérandes ont le même type. Le résultat de la
conversion sera exprimée dans le type de plus haute précision.

On distingue deux types de conversion :

1) la conversion d’ajustement de type

Une conversion de type suit un certain ordre qui permet de ne pas dénaturer la valeur initiale.
int ---> long ---> float ---> double ---> long double

Cet ordre permet de convertir par exemple, int en long ou double ou long double mais l’inverse n’est
pas possible.

Exemple:
int n;
long p;
float x;

L’expression n * p + x sera évaluée suivant ce schéma:


1) Conversion de n en long
2 )Multiplication du résultat par p
3) Le résultat de * est de type long
4) Il sera converti en float
5) Il est additionné á x
6) Le résultat de + est de type float

2) Les promotions numériques

Les opérateurs arithmétiques ne sont pas définis pour le type short et char. Le langage C prévoit que
toute valeur de l’un de ces types apparaissant dans une expression, est d’abord convertie en int. On
parle alors de promotion numérique.

Exemple

Cas du type short:

Si p1 et p2 sont de type short


et x de type float

p1 * p2 + x sera évaluée suivant le schéma qui suit:

1) Les variables p1 et p2 sont soumises à la promotion numérique short------->int


2) On applique les règles de conversion d’ajustement de type.

19
Cas du type char:

La promotion numérique permet de considérer le code du caractère ( sur 8 bits) comme la valeur de ce
caractère.
Dans ce cas, le langage C confond un caractère avec la valeur ( entier) du code qui le représente.

Remarque :
La valeur entière associée à un caractère donné n’est pas le même sur toutes les machines.

Exemple

Si c1 est de type char.


L’expression c1 + 1 sera évaluée suivant le schéma suivant:
1) promotion numérique de c1
2) l’additionner à 1.
3) Le résultat sera de type int.

II-2 Les opérateurs relationnels:

Le langage C permet de comparer des expressions à l’aide d’opérateurs de comparaison.

Le résultat de la comparaison est une valeur entière de valeur 1 ( si le résultat est vrai) ou 0 ( si le
résultat est faux).
Cette expression faisant intervenir des opérateurs de comparaison , sera alors de type entier et donc
pourra intervenir dans des calculs arithmétiques.

Les opérateurs relationnels sont :

< Inférieur à

> Supérieur à

<= Inférieur ou égal à

>= Supérieur ou égal à

== égal à

!= Différent de

Ils sont classés suivant cet ordre de priorité :

20
< , > , <= , >= ( plus haute priorité )
= = , et != ( moins prioritaire)

• Les opérateurs relationnels sont moins prioritaires que les opérateurs arithmétiques.

Exemple1:
int a,b,c,d;
a<b==c<d
l’expression a < b est de type entier; elle prend la valeur 1 ou 0.
c < d est de type entier; elle prend la valeur 1 ou 0.
Suivant les valeurs de (a < b ) et de ( c< d ) , l’expression donnée aura comme valeur 1 ou 0.

Exemple2:
int x, y, a;
L’expression x + y < a permet de comparer la valeur de l’expression ( x + y ) et la valeur de a.
Le résultat sera 1 ou 0.

Remarque importante:
Une comparaison peut porter sur sur 2 caractères
a) cas de comparaison d’égalité:
( l’existence d’une conversion de promotion numérique char ---> int n’a guère d’influence).

char c1,c2;
c1 = = c2 est vraie ( a la valeur 1) si c1 et c2 ont la même valeur ou bien c1 et c2
contiennent des caractères de même code ( le même caractère).

b) cas de comparaison d’inégalité:

char c1,c2;
c1 < c2 est vraie si le code du caractère c1 a une valeur inférieure au code du caractère c2.
Le résultat dépendra du codage employé.
II-3 Les opérateurs logiques:

Le langage C dispose de 3 opérateurs logiques:

&& et logique

|| Ou

! non

A priori ,les opérateurs logiques ne portent que sur des opérandes qui sont eux-mêmes des expressions
logiques.

21
Mais, ils acceptent des opérandes numériques ( types int et float) avec les règles de conversion
implicites.
Dans ce contexte, on considère que :
La valeur nulle ( 0 ) correspond à faux

toute valeur non nulle correspond à vrai.

Fonctionnement des opérateurs logiques dans C :

Opérande1 opérateur Opérande2 résultat

0 && 0 0
0 && Valeur non nulle 0
Valeur non nulle 0 0
Valeur non nulle && Valeur non nulle Valeur non nulle

0 || 0 0
Valeur non nulle || 0 Valeur non nulle
0 || Valeur non nulle Valeur non nulle
Valeur non nulle || Valeur non nulle Valeur non nulle

! 0 Valeur non nulle


! Valeur non nulle 0

Les règles de priorité des opérateurs logiques :

! plus prioritaire

&&

|| moins prioritaire

• les opérateurs arithmétiques sont plus prioritaires que les opérateurs logiques.
• L’opérateurs ! ( not) a une priorité supérieure à celle de tous les opérateurs arithmétiques
binaires et opérateurs relationnels.

Exemple1:

int a,b,c,d;

a < b && c < d équivaut à (a < b) && ( c < d )

! a == b équivaut à (! a) == b

22
int n,p;

n && p est accepté par le compilateur

!n est accepté par le compilateur

Remarque:

• Le second opérande de l’opérateur && n’est pas évalué si le premier opérande a été évalué á
0 car l’expression complète aura la valeur 0 ( faux).

• Le second opérande de l’opérateur || n’est pas évalué si le premier opérande a été évalué à
vrai ( valeur non nulle) car l’expression complète aura la valeur 1 (vrai).

Exemple2 :

int i = 7;
float f = 5.5;
char c = ‘w’;

L’expression valeur

( i >= 6 ) && ( c = ‘w’ ) 1

( i >= 6 ) || ( c = = 119 ) 1

(f < 11 ) && ( i > 100 ) 0

( c != ‘p’ ) || ( i + f <= 10 ) 1

Exemple 3 :

int i = 7;
float f = 5.5;

L’expression valeur

F> 5 1

!(f>5) 0

! ( i > f + 1) 0

23
II-4 Les opérateurs d’affectation :

L'opérateur d’affectation permet de former des expressions d’affectation .

La partie gauche de l’opérateur d’affectation `` = `` doit être une lvalue (left value).
lvue est une expression à laquelle on peut affecter une valeur.
Les variables sont des lvalues.

La partie à droite de l’opérateur est une expression dont la valeur est affectée à la lvalue.

La valeur finale de l’expression d’affectation est la valeur de la lvalue après affectation.

• La priorité de cet opérateur est inférieure à celle de tous les opérateurs arithmétiques et les
opérateurs de comparaison.

• Si les opérandes sont de types différents, il y a conversion systématique de l’expression


dans le type de la lvalue. Cette conversion peut entraîner à une dégradation de la valeur
convertie.
Par exemple, une valeur de type float peut être tronquée ( elle perd
sa partie décimale) si elle est affectée à une lvalue de type entier.

Exemple :

int i ;
int j =3;

i = j + 5 Cette expression évalue d’abord l’expression j + 5 avant d’en affecter la valeur


( 8 ) à la variable i.
La valeur finale de cette expression est 8.

II-5 Les opérateurs d'incrémentation et de décrémentation:

On distingue deux opérateurs unaires:

L’opérateur d’incrémentation noté ++

L’opérateur de décrémentation noté - -

1) L’opérateur d’incrémentation noté ++

Il a pour effet d’incrémenter de 1 la valeur d’une variable ( lvalue).


On dit que ++ est:

24
• un opérateur de pré-incrémentation, s’il est placé à gauche de la `` lvalue`` sur laquelle il
porte. La valeur de l’expression d’incrémentation est celle dela ``lvalue`` après
incrémentation.

• un opérateur de post-incrémentation, s’il est placé à droite de la `` lvalue`` sur laquelle il


porte. La valeur de l’expression d'incrémentation est celle de la ``lvalue`` avant
incrémentation.

Exemple:

int i = 5;

i++ équivaut à i = i + 1
i devient égale à 6
la valeur de cette expression est 5

++i équivaut á i = i + 1
i devient égal á 6
la valeur de cette expression est 6.

2) L’opérateur de décrémentation noté --

De la même manière, cette opérateur sera :

• un opérateur de pré-décrémentation s’il est placé à gauche de la `` lvalue`` sur laquelle il


porte. La valeur de l’expression décrémentation est celle de la ``lvalue`` après
décrémentation.

un opérateur de post-décrémentation s’il est placé à droite de la `` lvalue`` sur laquelle il
porte. La valeur de l’expression décrémentation est celle dela ``lvalue`` avant décrémentation.

Exemple:

int i = 4 ;

--i; équivaut à i = i - 1
i devient égale à 3
la valeur de cette expression est 3.

i--; équivaut à i = i - 1
i devient égale à 3
la valeur de cette expression est 4.

Priorités relatives à ces opérateurs:

Ces opérateurs sont de plus haute priorité que les opérateurs arithmétiques.

25
Exemple:

int i, j, k ;
L’expression 3 * i++ * j-- + k++ équivaut à 3 * (i++)n* (j--) + ( k++).

Si t est un tableau :
t[ i++] = 1 permet d’initialiser tous les éléments du tableau
_ affecte la valeur 1 à t[ 0]
_ puis incrémente de 1 la valeur de i.

II-6 Les opérateurs d’affectation élargie:

D’une manière générale, C permet de condenser les affectations de la forme :

lvalue = lvalue opérateur expression

en lvalue opérateur = expression


.
Ceci concerne seulement les opérateurs arithmétiques.

Liste des opérateurs d’affectation élargie:

+=, -=, *=, /=, %= ( concernant les opérateurs arithmétiques).

Exemple:

int i,k;

i += k équivaut à i = i + k

i *= k équivaut à i = i * k.

II-7 L’opérateur cast:

Le programmeur peut forcer la conversion d’une expression quelconque dans un type de son
choix à l’aide de l’opérateur `` cast ``.

Exemple:
int n , p;

(double)(n/p) la valeur de l’expression entière n/p sera convertie


en double.

26
• Sa priorité est élevée par rapport aux autres opérateurs.
• Toutes les conversions numériques sont réalisables par un opérateur `` cast ``.
II-8 L’opérateur conditionnel :

L’opérateur conditionnel est un opérateur ternaire mettant en relation 3 expressions ou opérandes . Il


évalue la première expression qui joue le rôle de condition :
- si sa valeur est non nulle, il y a évaluation du second opérande ( expression ) qui sera la valeur du
résultat.
- si sa valeur est nulle , il y a évaluation du troisième opérande qui sera la valeur du résultat.

Sa priorité est faible, relativement aux autres opérateurs.

Exemple :

int max, a, b;

max = a > b ? a : b; attribue à max la plus grande des deux valeurs de a et b.

a > b ? i++ : i- - suivant que la condition a > b est vraie ou fausse, on incrémentera
ou décrémentera la valeur de i.

II-9 L’opérateur séquentiel : noté ,

Il permet d’exprimer plusieurs calculs successifs au sein d’une même expression.

Exemple1:

L’expression a*b, i + j
- évalue a * b
- puis i + j
- prend comme valeur la dernière calculée ( la valeur de i + j )

L’expression i ++, j = i + k
- incrémente la valeur de i
- puis évalue j = i + k.

L’opérateur séquentiel permet de regrouper plusieurs instructions en une seule.

Exemple2 :

I ++, j = i + k, j -- équivaut à i ++;


j = i + k;

27
j--;

II-10 L’opérateur sizeof


Son emploi ressemble à celui d’une fonction. Il fournit la taille ( en octet ) de son paramètre.
Cet opérateur peut être appliqué soit à une variable soit à un type .

Exemple :
int n;
double z;

L’expression sizeof ( n ) aura comme valeur 2.


sizeof ( z ) vaudra 8

sizeof ( int ) vaudra 2.


sizeof ( double ) vaudra 8
sizeof ( char ) vaudra 1

28
Les entrées–sorties
conversationnelles

29
Les entrées–sorties conversationnelles

La bibliothèque standard du langage C offre une fonction de lecture scanf pour lire des informations du
clavier et une fonction d’écriture printf pour afficher des informations à l’écran.
Ces deux fonctions permettent de lire ou d'écrire des données selon un certain format.
Les données peuvent être converties sous forme de caractères, de nombres entiers , de chaînes de
caractères etc.… selon les spécificateurs de format utilisés.

III-1 Les possibilités de la fonction scanf

Sa syntaxe:
scanf ( “format” ,arg1,arg2,……argn)

arg1,arg2,….sont des arguments spécifiant les adresses des données en entrées

et format correspond à une liste de codes de conversion précisant le type de la donnée à lire.

Les principaux codes de conversion :

c indique que la donnée est de type : caractère imprimable

d entier décimal

0 entier en notation octale

x entier en notation hexadécimale

u entier décimal non signé

ld long int

hd short int

hu unsigned short

lu long non signé

f ou e float indifféremment en notation décimale ou


exponentielle
lf ou le double indifféremment en notation décimale ou
exponentielle.

Remarques importantes:

30
• L’information tapée au clavier est rangée dans un tampon ( un emplacement mémoire
temporaire ) manipulé par un pointeur vers le prochain caractère à prendre en compte.
1) Certains caractères comme l’espace (^) et la fin de ligne (\n) joue le rôle de séparateurs. La
rencontre d’un séparateur entraîne l’arrêt d’un traitement d’un code de format.

Exemple:

scanf(“%d%c”,&a,&b)

Caractères entrés au clavier:

45(retour chariot)
A (retour chariot) a=45 , b=’A’

Ou bien

45A ( retour chariot) a=45 , b=’A’

45 A (retour chariot) a=45, b= ( blanc)

2) Dans le cas des codes de format correspondants à un nombre ( excepté %c


et %s), scanf prend en compte tous les caractères entrés jusqu’à la rencontre d’un séparateur ou
un caractère invalide ( exemple: un point pour un entier….).

scanf(“%d%d”,&i,&j)

Caractères entrés au clavier:

34 12 (retour chariot) i = 34, j =12

34a12 (retour chariot) i =34, j= non defini


car il y arrêt prématuré de scanf

3) Le code format %c entraîne la prise en compte du caractère entré même s’il s’agit d’un
séparateur .

int a;
char c;
scanf(“%d%c”,&a,&c)

Caractères entrés au clavier

12^A a=12 c= ^

scanf (“%d , %c”, &a,&c); ( le blanc entre deux codes signifie que les blancs jouent le
rôle de séparateurs.

31
12 ^^^^ V a = 12, c=V

4) La rencontre d’un espace entre deux codes de format entraîne scanf à ignorer tous les
séparateurs entrés jusqu’à rencontre d’un caractère différent d’un séparateur ( ceci revient à
faire avancer le pointeur du tampon jusqu’au prochain caractère différent d’espace ou retour
chariot )

scanf(“%d %c”,&a,&b)

a=45, b=A

5) Dans les codes de format de scanf, on peut préciser un gabarit qui spécifie
le nombre maximal de caractères à utiliser pour le code du format traité. La rencontre d’un
gabarit atteint entraîne l’arrêt du traitement d’un code format.

int a;
float r;
char c;
scanf(“%3d %5f %c”,&a,&r,&c)

Caractères entrés:
123 34.5645 d a=123, r = 34.56, c= 4

scanf(“%3d %3.3f %c”,&a,&r,&c)

Caractères entrés:
123 34.5645 d a = 123, r = 34.564, c=5

6) synchronisation entre clavier et écran

char c1, c2 ; x[20];


scanf( “ %s %c ”, &x, &c1);
scanf( “%c ”, &c2);

caractères entrés :
123 retour chariot X=123 ; c1=blanc ( retour chariot) ; c2 =A
A

• Les informations frappées au clavier ne sont pas traitées immédiatement, mais elles sont
mémorisées dans le tampon. Le traitement commence à la rencontre de \n ( retour chariot)
• le tampon n’est pas vidé à chaque nouvel appel de scanf .

32
7) Le tampon peut être vidé explicitement par l’appel de la primitive
fflush( stdin) de la librairie <stdio.h >.

III-2 Les possibilités de la fonction printf

Sa syntaxe:
printf ( “format” ,arg1,arg2,……argn)

arg1,arg2,….sont des arguments spécifiant des expressions

et format correspond à une liste de codes de conversion précisant le format de la donnée à afficher.

Les principaux codes de conversion :

c indique que la donnée est affichée comme : un caractère imprimable

d un entier décimal

0 un entier en notation octale sans le 0

x un entier en notation hexadécimale

u un entier décimal non signé

ld long int

lu long non signé

f float ou double
en notation décimale avec 6 chiffres après le point

e float ou double
en notation exponentielle avec 6 chiffres après le point

s chaîne de caractères

Remarques:

• On peut associer des préfixes aux codes de format:


h permet de spécifier un short int
l permet de spécifier un long int
L permet de spécifier un long double

• les flottants sont affichés avec 6 chiffres après le point ( par défaut)

33
Exemple:

int a = 33;
float r = 4.351;

printf(“%f ”, r)

Les caractères affichés

r =4.351000

• Le gabarit qui est placé après le % indique le nombre minimal de caractères à afficher.

printf(%4d %4.7f” ,a,r)


affiche a = ^^33 r= 4.3510000

• Le signe – placé après % indique que le cadrage de l’affichage doit se faire à gauche ( par
défaut, il se fait à droite) et inversement pour le signe +.

printf(“%-4d % +3.7”, a,r)


affiche a=33^^ r = 4.3510000

III-3 Les entrées_ sorties non formattées :

Pour les entrées_ sorties du clavier et de l’écran respectivement : la bibliothèque standard


<stdio.h> offre des macros de lecture et d’écriture caractère par caractère.

1) La macro getchar()

Elle permet de lire un caractère du clavier et retourne soit l’octet lu soit la valeur -1 (EOF).

char c;
……
c= getchar()

Elle joue le même rôle que scanf( “%c ” , &c);


Quand le caractère lu est une fin de fichier , c= EOF ( correspond à la valeur _ 1 ).
Cette macro peut être utilisée pour lire une chaîne de caractères en lisant un caractère à la fois dans
une boucle.

2) La macro putchar()
Elle permet d’afficher le caractère c sur l’écran.

34
char c;
…….
putchar(c);

Elle joue le même rôle que printf(“%c”,c).


Cette macro peut être utilisée pour lire une chaîne de caractères en lisant un caractère à la fois
dans une boucle.

Elle offre aussi des macros de lecture et d’écriture de chaînes de caractères : gets(s) et
puts( s)

Exemple :

char nom[10] ;

puts ("hello");
gets(nom);
puts(nom);

35
Les instructions de contrôle

36
Les instructions de controle

Le langage C dispose d’instructions de controle permettant de réaliser :

• des choix : L’instruction conditionnelle if…else…

L’instruction d’aiguillage ou de choix switch…case…default…

• des boucles les instruction répétitives :


+ while…do…
+ do…while…
+ for…

• des branchements Les instructions de branchement inconditionnel


+ goto
+ break ( associé aux boucles)
+ continue ( associé aux boucles)
+ return

IV-1 L’instruction conditionnelle if…else

L’instruction conditionnelle permet de tester une condition puis d'exécuter une action parmi deux
actions possibles.

Syntaxe:
if ( expression) instructions1;
[ else instructions2 ; ]

instructions1 et instructions2 sont des instructions quelconques qui peuvent être soit une instruction soit
un bloc d’instructions placées entre { et }.
Si l’expression a une valeur non nulle ( vraie) alors instructions1 est exécutée sinon instructions2 est
exécutée.

Exemple1
Int a,b ;

if ( a<b ) max = b ;
else max = a ;

Exemple 2
Int a,b ;

if (( a < = b ) && ( b<= c)) printf( “ordonne”);

37
Cas d’imbrications des instructions if…else

Règle à appliquer:
Le premier else rencontré est associé avec le plus proche if qui le précède:

1ère forme d’imbrication :

if (a <= b) if ( b<= c) { printf( “ ordonnée” ); max = c; }


else printf( “ non ordonnée” );

dans cet exemple, dans le cas où l’expression ( a<=b ) est fausse, rien n’est affiché .

2ème forme d’imbrication :

if (heure >= 0) && ( heure < 12) printf( “ bonjour” );

else if ((heure >= 12) && ( heure < 18)) printf( “ bon pares midi” );

else if ((heure >= 18) && ( heure < 24 )) printf (“ bonsoir” );

else printf( “ erreur” );

IV-2 L’instruction switch:

L’instruction switch est une instruction de choix multiple. Elle permet d’évaluer une expression puis
d'exécuter une action parmi plusieurs actions étiquetées.
Si la valeur de l’expression correspond à une des étiquettes, l’action correspondante est exécutée .
Syntaxe:
Switch ( expression)
{
case constante1 : [ instructions; ]
………….
case constante : [ instructions; ]
………
[default : instructions; ]
}

« expression » doit être une expression entière


« la constante » doit être une constante entière( de type int, char, short,long,unsigned).
Par contre « default » est facultatif.

L’expression est comparée successivement( de haut en bas) aux constantes spécifiées après chaque
“case”.
S’il y a égalité, alors les instructions correspondantes à ce « case » sont exécutées.

38
Dans le cas où aucune égalité n’est vérifiée, les instructions spécifiées après “ default” sont exécutées.

Pour sortir de l’instruction switch délimitée par { et } et continuer en séquence, on doit utiliser
l’instruction break .

Exemple1:
Char c;

switch ( c)
{
case ‘a’ : printf( “ lettre a” ); break;

case ‘b’ : printf ( “ lettre b” ); break;

default : printf ( “ lettre inconnue” );


}

Exemple2: cas où plusieurs “case” font référence à la même action.

switch ( c = getchar() )
{
case ‘a’ :
case ‘o’ :
case ‘i’ :
case ‘e’ :
case ‘u’ :
case ‘y’ : printf ( " c’est une voyelle " ) ; break ;
}

Exemple3 : cas de plusieurs “switch " imbriqués :

int err;
char code ;

switch ( err)
{
case 1 : printf( " erreur grave" ) ;
switch ( code)
{
case ‘0’ : printf ( “ à l’ open " ) ; break ;
case ‘e’ : printf ( “ à l’ écriture " ) ; break ;
default : printf ( “ inconnue " ) ; break ;
} break ;
case 2 : printf( " erreur simple" ) ; break ;
default : printf( " erreur inconnue" ) ; break ;
}.

39
IV- 3 L’instruction do…while…

Elle permet de répéter une ou plusieurs actions tant que la condition spécifiée n’est pas vérifiée.

Syntaxe :
Do instructions while ( expression) ;

Instructions peut être soit une seule instruction suivie de ; soit un bloc d’instructions entre { et } .
L’expression est évaluée après avoir exécutée au moins une fois instructions.
Si la valeur de l’expression est non nulle ( vraie), cette exécution est répétée sinon l’itération se termine.

Exemple 1:
do x -- ;
while x > 0

Exemple2 :
do c = getchar() ;
while ( c != ‘\n’ )

peut être écrite : do {}


while ( c = getchar() , c != ‘n’ )

Remarque :
• l’instruction à répéter peut être vide , après le “do” on peut mettre soit le point virgule soit
{ et }.
• L’expression du “while” peut une expression avec un opérateur séquentiel et dont la dernière
sous- expression est évaluée à vrai ou faux.

IV-4 L’instruction while

tant qu’une condition spécifiée n’est pas vérifiée, elle permet de répéter une ou plusieurs actions.

Syntaxe :
while ( expression ) instructions

Instructions peut être soit une seule instruction soit un bloc d’instructions entre { et } .

L’expression est évaluée avant d'exécuter instructions.


Tant que la valeur de l’expression est non nulle ( vraie), instructions est exécutée, sinon l’itération se
termine.
Exemple 1:

while( x >0 )
x --;
Ou bien

40
while ( x--,x>0) ;

Est équivalente à : do x -- ;
While ( x > 0) ;

Exemple2 :

while ( c = getchar(),c != ‘\n’ ) {}

peut être écrite : do {}


while ( c = getchar() , c != ‘n’ )

Exemple3:

while ( printf( “ donnez un nombre ”) , scanf( “ %d”, &n) , somme<=100 )


somme += n;

IV-5 L’instruction for :

L’instruction for est une instruction de boucle faisant intervenir l’initialisation, le test de limite et
l’incrémentation en une seule action.

Syntaxe:
for ( [expr1] ; [expr2] ; [expr3] )
Instructions;

“ instructions” peut être une instruction composée, et sera dans ce cas délimitée par { et }.
“ expr1” est une expression ( une initialisation) qui ne sera exécutée qu’une seule fois au début.
“expr2” est une expression dont le résultat détermine la fin de la boucle.
Tant que sa valeur est non nulle ( vraie), la boucle continue.
“ expr3” est une expression ( en général) qui sera exécutée à chaque itération.

Exemple:

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


{ …..
if ( table[ I ] == ‘A’)
…….
}

1) chacune des 3 expressions est facultative.

for ( ; I<10; I++) /* pas d’initialisation

for ( I=0; k>1; ) /* pas de progression

41
for ( ; ; ) /* boucle infinie

2) Lorsque l’expression 2 est absente, elle est considérée vraie.

3) Il est possible de regrouper plusieurs actions dans une expression.

#define EOL ‘\n’


char text[80];
int i;
for ( i=0; text[ i ] = getchac() != EOL; text[i] = toupper (text[ i ]) , i++ );

IV-6 Les instructions de branchements inconditionnels

1) L’instruction break:

Elle peut être utilisée dans les boucles ou dans une instruction switch. Elle permet d’interrompre le
déroulement de la structure où elle a été appelée.

Dans le cas des boucles ou plusieurs ‘switch ’ imbriqués, l’instruction ‘break’ n’a d’effet que sur la
boucle où elle a été définie.

Exemple: programme qui permet de lire des caractères jusqu ‘à fin de fichier ou fin de ligne.

while ( ( c=getchar()) != EOF )


{ ….
If ( c == ‘\n’ )
break;
….
}

2) L’instruction continue:

Elle peut être utilisée dans les boucles .


Elle permet de sauter une sequence d’instructions sans sortir de la boucle.

Dans le cas des boucles imbriquées, l’instruction ‘continue’ n’a d’effet que sur la boucle où elle a été
définie.

Exemple:

for( I=0,k=0; I<10; I++)


{ ….
if( I == 4) continue; /* dès que I=4 , le programme saute la séquence
for qui suit et passe à l’iteration suivante I= I+ 1
for ( j=0; j<10; j++)
{ …
if ( j == 7) break;
k += j;
}
42
}

3) L’instruction goto:
Elle permet le branchement inconditionnel vers une instruction spécifiée par une étiquette dans le
programme.

Syntaxe:
Goto etiquette;
….

etiquette: …..

Exemple:

while ( ( c=getchar()) != EOF )


{ ….
If ( c == ‘\n’ )
goto fin;
….
};

fin: printf( “ fin de boucle” );

43
La programmation modulaire et
les fonctions

44
La programmation modulaire et les fonctions

Le principe de modularité dans les langages de programmation permet qu’un groupe


d’instructions regroupé dans un module puisse être accédé de façon répétitive de différents endroits dans
un programme.
Dans le langage C, un programme source peut être constitué d’ une ou plusieurs unités de programmes
appelés fonctions.
Il comporte au moins une fonction principale appelée " main " et peut avoir plusieurs autres fonctions.
La fonction " main " est la première fonction à partir de laquelle le système passe le controle lorsqu on
désire éxécuter le programme.
La programmation structurée s’appuie sur l’utilisation de fonctions afin d’obtenir un code modulaire.
Les programmes ainsi écrits sont plus performants et faciles à utiliser.
En effet , écrire un programme structuré revient à diviser un problème de programmation complexe en
tâches plus simples. Chaque tâche est réalisée par une fonction dont le code et les variables sont
indépendantes du reste du programme

Définition d’une fonction :

Une fonction en C est une unité de programme, référencée par un nom, qui dispose d’arguments
correspondants à des informations qui lui sont transmises. Elle peut fournir une valeur en retour.
La définition d’une fonction comporte :
• une partie en-tête constiuée de :

1) type de la fonction correspondant au type de la valeur retournée par la fonction.


( par défaut le type est int ).

2) Nom de la fonction ( identificateur).

3) Liste de paramètres formels qui lui sont passés lors de l’appel de cette fonction
d’un programme .
Les paramètres doivent être précédés de leur type.

Syntaxe :
Type nom ( [ type1 PF1, type2 PF2, …] )

• Une partie corps délimitée par deux accolades { et }. Le corps d’une fonction peut
contenir:

1) Des déclarations de variables locales utilisées par la fonction.

2) Des instructions dont l’une est l’instruction return ou instruction de retour .

Exemple :
int affich ( int nb) ; /* en-tête
{
int F ; /* declaration d’une variable locale
F=1 ; /* instuctions

45
While ( nb != 0 )
{
F = F * nb ;
nb = nb –1 ;
}
return F ;
}

Dans un programme, toutes les fonctions doivent être définies par l’utilisateur exceptées les fonctions de
la librairie commune ( printf, scanf ….) .

L’instruction return

Sa syntaxe :
Return ( expression)

Cette instruction est appelée à la fin de la fonction . Elle retourne la valeur de l’expression au
programme appelant cette fonction.

a) Dans le cas où le type de l’expression retournée est différent du type de la fonction déclaré
dans l’entête, le compilateur peut mettre en place des instructions de conversion.

b) Dans le cas des fonctions définies sans valeur de retour ou sans argument , le mot clé “ void”
permet de le préciser au niveau de la définition de l’entête et dans la déclaration de la
fonction.

Exemple :

void fsansval ( int a ; int b ) ; /* fonction sans valeur de retour

char fsansarg ( void); /* fonction sans arguments

Déclaration d’une fonction:

En C , un programme source peut être éclaté en plusieurs “fichiers sources”, comme l’autorise la
compilation séparée du langage.
Ces différentes parties du programme peuvent partager des informations.

Une fonction définie dans un fichier source et appelée dans un autre fichier source doit obligatoirement
être déclarée dans le fichier source où elle est appelée.

La déclaration d’une fonction facilite la vérification des types des paramètres à l’appel de la fonction et
des paramètres à la définition :

La forme générale d’une déclaration de fonction est:

Type nom ( [ liste des types des paramètres] )

46
1) dans le cas où les types ne sont pas spécifiés, on parle de déclaration partielle de la fonction.

float fact();

2) Dans le cas où les types et les noms des paramètres sont spécifiés on parle de déclaration complète
ou " prototype " de la fonction.

float fact( int n) ;

3) Dans le cas où une fonction est déclarée dans les déclarations de la fonction qui l’utilise , elle sera
considérée comme une variable locale dont la portée est limitée à la fonction où elle apparait .

void main()
{
float fact( int) ; /* prototype de fact
int a ;
float x ;
x = fact ( a ) ;
}

4) Dans le cas où une fonction n’est pas déclarée dans la fonction qui l’utilise , elle est considérée
comme une variable externe. Elle peut être appelée de n’importe quelle autre fonction.

float fact( int) ; /* prototype de fact


void main()
{
int a ;
float x ;
x = fact ( a ) ;
}

Appel d’une fonction :

Une fonction peut être appelée en spécifiant son nom suivi par une liste de paramètres.
En C, un appel de fonction par son nom est assimilable à une expression .

Syntaxe :
nom ( pe1,pe2,……………)

nom : correspond au nom de la fonction définie.

pe1,pe2,… : sont les paramètres d’appels de la fonction. Ils sont aussi appelés paramètres effectifs. Au
niveau de l’appel, un paramètre de la fonction peut être une constante, une variable ou une expression.

47
• Au niveau de l’appel, il doit y avoir un paramètre effectif pour chaque paramètre
formel.
• Chaque paramètre effectif doit être de même type que son paramètre formel
correspondant.

Exemple :

#include<stdio.h>
int max(int x ; int y) /* définition de la fonction*/
{ /* qui calcule le maximum de deux entiers*/
int m;
m = ( x > y) ? x: y;
return (m);
}

main ()
{
int a,b,c,d;
scanf( “%d %d %d “,&a,&b,&c);
d = max ( a , b ); /* 1er appel de la fonction max*/
printf ( “ %d”, max ( c,d)); /* 2ème appel de max */
}

programme main() max()


principal { {

appel max() }

Passage des paramètres à une fonction

En C, les paramètres sont transmis “par valeur " ce qui signifie que ce sont les contenus des
variables qui sont transmis à la fonction et non leur adresse.

En effet, les paramètres effectifs peuvent être n’importe quelle expression.


Au niveau de l’appel de la fonction , cette expression sera évaluée et sa valeur est recopiée localement
dans la fonction aux emplacements des paramètres formels correspondants .

Avec ce mode de transmission, les valeurs des paramètres formels peuvent être modifées dans le corps
de la fonction, mais les valeurs des paramètres effectifs ne doivent pas être modifiées.

Ce mode de transmission interdit à une fonction de produire une ou plusieurs valeurs de “retour’’ ( dans
les paramètres effectifs) autre que celle de la fonction elle-même.
Il représente un mode d’échange d’information dans un seul sens.

48
Exemple 1:

#include<stdio.h>
void main ()
{
void echange( int a,int b) ;
int n=10, p=20 ;
echange ( n,p) ; /* au retour de l’appel */
/*les valeurs de n=10 et p=20*/
}

void echange( int a, int b)


{
int c ;
c= a ;
a= b ;
b=c ;
}
Cet éxemple montre une des limitations de la transmission des paramètres par valeur.

pour altérer les valeurs des variables n et p appartenant à la fonction main(), il suffit de transmettre en
paramètre leurs adresses. La fonction echange pourra alors agir sur le contenu de ces adresses. Dans ce
cas , les paramètres formels a et b doivent être des variables qui peuvent contenir des adresses.

Exemple2:
void main()
{ int i;
char tab[ 10 ];
scanf ( “%d”, &i); /* la fonction scanf reçoit en paramètre la valeur de */
/* l’adresse de i. Le contenu de i peut alors être altéré
scanf ( “ %s”, tab); /* tab est l’adressse du tableau
tab[ i ] = 0 ; /* le contenu de l’élément i du tableau est mis a 0.
}

La programmation modulaire

En C, le principe de modularité permet qu’un programme soit constitué d’un ou plusieurs


modules. Un module pouvant contenir des fonctions et des déclarations de variables
Les fonctions peuvent échanger des informations entre elles :
• soit par le passage des paramètres.
• Soit par le partage de variables communes.

Au niveau de la définition des variables, on distingue deux types de variables:


• les variables communes appelées variables globales qui sont connues de toutes les
fonctions définies au sein du même programme source.

49
• Les variables locales qui sont définies au sein d’une fonction et qui ne sont connues
qu’à l’intérieur de cette fonction.

1) Les variables globales


De façon générale , une variable est définie en spécifiant son type et son nom.

Définition d’une variable globale

Dans le cas d’une variable globale, sa définition doit apparaitre en-dehors des fonctions qui
l’utilisent et doit précéder leur définition .
On dit que sa portée est limitée à la partie du programme qui suit sa définition.

• la définition d’une variable entraine l’allocation d’un espace mémoire permanent


pour cette variable et éventuellement l’initialisation de sa valeur.
• Quand la valeur de cette variable est modifiée dans le corps d’une fonction, sa
nouvelle valeur sera connue de toutes les fonctions qui
l’utilisent.

Exemple:
int i ; /* déclaration de la variable globale
main ()
{
void affich ( void); /* prototype de la fonction

for( i=1, i<=5, i++)


affich (); /* appel de la fonction
}
void affich( void); /* définition de la fonction
{
printf ( “i= %d”, i);
}
La variable i sera connue de toutes les fonctions compilées au sein du même programme.

Déclaration d’un variable globale :

1) La déclaration extern

En C, il est possible de faire la compilation séparée de plusieurs modules réparties sur différents
fichiers , et faisant partie d’un même projet .

La portée d’une variable globale étant limitée au fichier source dans lequel elle a été définie, le langage
C prévoit une déclaration qui spécifie que la variable a déjà été définie dans un autre fichier source en
faisant inclure une déclaration de cette variable spécifiant que la variable est externe .

Syntaxe de la déclaration :
Extern type nom

50
Exemple :
source 1 source2
int x ; extern int x ;
main () fonct2 ()
{ {
….. …..
} }
fonct1 ()
{……

Remarque :

La déclaration d’une variable externe n’effectue pas d’allocation d’espace mémoire , ni

d’initialisation de sa valeur .

2) La déclaration static

En C, il est possible de limiter la portée d’une variable globale et la rendre inaccessible à l’extérieur

du fichier source où elle a été définie, en la déclarant de classe static de la façon suivante :

static type nom

Cette notion généralise la notion de variable locale à tout un fichier source .

Exemple :

source 1 source2
Static int x ; extern int x ;
main () fonct2 ()
{ {
….. …..
} }
fonct1 ()
{
……
}

51
La variable x déclarée dans le fichier source2 n’aura aucun lien avec la variable x déclarée dans le

fichier source1. Car la déclaration static de la variable x demande qu’aucune trace de x ne subsiste en

dehors du fichier source1.

2) Les variables locales

Les variables définies au sein d’une fonction sont dites locales à cette fonction. Elle ne sont connues
qu’à l’intérieur de cette fonction.
On dit que leur portée est limitée à cette fonction.

Exemple:
int n; /* n est une variable globale
main ()
{
int p; /* p est une variable locale à main
::::
}

fonct1 ()
{
int p; /* p est une variable locale à fonct1
int n; /* n est une variable locale à fonct2
…..
}

Il est impossible à la fonction fonct1 d’utiliser la variable globale n.

Par défaut , une variable locale a une durée de vie limitée à celle d’une éxécution de la fonction dans
laquelle elle a été définie . Un espace mémoire lui est alloué à chaque entrée dans la fonction et
libéré à chaque sortie .
On dit que sa classe d’allocation est automatique.

Contrairement à cette classe d’allocation ; il éxiste une classe d’allocation statique ; qui permet
d’attribuer à une variable locale un emplacement permanent. Pour cela ; il suffit de spécifier le mot
clé static dans la déclaration de la variable :

Exemple :

#include<stdio.h>
main ()
{
void fonct();
int n; /* declaration de n (variable locale)

52
for( n=1; n,=5; n++)
fonct();
}

void fonct()
{
static int i; /* declaration de i (variable locale static)
i++;
printf( “ i = %d ” ,i) ;
}

à chaque appel de la fonction fonct , la valeur de la variablei progresse de 1

Remarque :
Les variables locales de classe statique sont par défaut, initialisées à 0.

Définition de fonctions récursives

La récursivité est le processus par lequel une fonction peut s’appeler de façon répétitive jusqu’à
satisfaction d’une certaine condition.

On distingue deux types de récursivité: directe ou indirecte.

Dans une recursivité indirecte, une fonction appelle une autre fonction qui appelle à son tour la
première.

Le langage C permet la récursivité qui s’avère trés utile dans certaines circonstances .

Par exemple pour calculer le factoriel d’un nombre entier ( utile en statistiques), la recursivité peut
être utilisée. Le factoriel d’un nombre x se note x! et se calcule de la façon suivante:
x! = x * (x - 1)* (x-2) ……2*1
x! = x * (x - 1)!
où (x-1)! = (x-1) * (x-2)!
Le calcul peut être continué de façon récursive jusqu’à la valeur 1.
Par définition la fonction qui calcule de manière récursive la factorielle d’un nombre entier positif
est : fact( n ) = n * fact( n - 1)

#include< stdio.h>
/* définition de la fonction factorielle
long int fact(int n)
{ if (n < =1) return ( 1) ;
else return ( n*fact(n-1)) ;
}

53
void main()
{ int n;
long int fact( int)m
printf(“n=\t ”);
scanf( “%d”, &n);

printf( “n! = %ld\n”, fact(n));


}

• Au niveau de chaque appel de fact, il y a allocation d’espace pour les variables


locales et le paramètre n de fact, sans libérer l’espace réservé lors des appels
précédents.
• A l’éxécution du corps de la fonction, une valeur doit être retournée.

Quand le programme est éxécuté, les appels de la fonction récursive ne sont pas éxécutés
immédiatement. Ils sont placés dans une pile ( en même temps que les espaces alloués aux variables
locales) jusqu’à ce que la condition d’arrêt est rencontrée.
Les appels de fonction seront dépilés ( en même temps que les epaces alloués aux variables locales) et
alors éxécutés dans l’ordre inverse de leur appel.

Pour évaluer la factorielle de façon récursive, les appels de la fonction se feront dans cet ordre :

fact(n ) = n* fact( n-1)


fact(n-1 ) = (n-1) * fact( n-2)
………………
fact(2) = 2* fact(1) il n’y a plus d’appels récursifs

Les valeurs seront retournées dans l’ ordre inverse des appels.

fact(1) = 1
fact(2) = 2* fact(1) = 2
fact(3) = 3* fact(2) = 6
………………
fact(n ) = n* fact( n-1)

Les fichiers en-tête

Les définitions de fonctions devraient se placer dans le même fichier source que la fonction
principale main() .
Ells peuvent aussi être placées dans un fichier source séparé de celui qui contient la fonction main().
Dans ce cas, il est possible de définir des fichiers d’extension h , appelés fichiers entête , dans
lesquels on regroupe les prototypes de fonctions définies dans un programme.

Soit header.h un fichier entête


La directive #include “header.h” doit être placé au début de chaque fichier source constituant le
programme . Elle permet d’incorporer automatiquement le prototype approprié de la fonction
utilisée.

54
Exemple

fichier source1 fichier souce 2


#include <stdio.h #include <stdio.h>
#include “header.h” #include “header.h”

main() définition de la fonction fonct1;


{…….. définition de la fonction fonct2;
…………..

:::::::::::::::::
}

/* fichier en-tête ‘header.h’

prototype de la fonction fonct1

prototype de la fonction fonct2

55
Les tableaux et les pointeurs

56
Les tableaux et les pointeurs

Définition
De façon générale, la notion d e tableau permet de définir une structure de donnée régulière
dans le sens où ele estconstituée d’un ensemble ordonné d’éléments de même type.
Un tableau permet de regrouper des données de même type sous un nom de groupe unique.

Les tâches de programmation qui impliquent un traitement répétitif des données conduisent à
l’utilisation des tableaux.

Un tableau représente un ensemble d’emplacements mémoire consécutifs qui portent le même nom et
contiennent des données de même type.

Les tableaux à une dimension

Un tableau à une dimension est un tableau n’ayant qu’un seul index. L’index indique la position
de la donnée ou l’élément dans le tableau.

1) Définition d’un tableau

Syntaxe :
Classe type nom [ expression ]

Classe spécifie la classe d’allocation possible pour ce type de variable : extern ,static ou auto.
Type spécifie le type des éléments de ce tableau (int, float ou char).
Expresssion spécifie la dimension du tableau ou le nombre d’éléments qui le constitue. Cette
expression peut être une constante ou une expression constante.

Exemple :
#define M 60 ;
main()
{
int x[100];
char text[80];
static float N[10];
int tab [ M];
float X[ 2*M];
……………………
}

2) l’indice d’un tableau

Un tableau peut être vu comme une succession de variables de même type, accessible par indexation.

57
Exemple :
Soit la déclaration suivante de tableau :
int tab [ 10 ];.
int i, j;
char c;

La dimension du tableau est 10


l’index est une valeur entière comprise entre 0 est 9.
Tab[0] est le 1er élément du tableau
Tab [9] est le dernier élément du tableau
L’indice peut prendre la forme de n’importe quelle expression arithmétique de type entier .

tab[i + j ]
tab [i + c ]
tab [ j ]

3) L’adresse d’un tableau

tab est considéré comme l’adresse du tableau. Cette adresse ne peut pas être modifiée.
En conséquence, les opérations suivantes ne sont pas permises: l’affectation,la comparaison etc….
tab = 0;
tab = tab + 1
tab == 0

Un élément de tableau est une lvalue ; il peut être à gauche d’un opérateur d’affectation : tab [ i ]
= 0;
tab [ i ] ++;

Reamarque :
Aucun controle de débordement d’indice n’est fait dans la plupart des compilateurs.

4) Initialisation d’un tableau

Comme les variables simples , un tableau peut être initialisé lors de sa déclaration partiellement
ou totalement avec des expressions constantes.

Les tableaux déclarés de classe static sont initialisés implicitement à 0.

Exemple :

int tab [ 6] = { 1, 4, 5, 4, 0, 1} déclaration totale du tableau


Cette déclaration place les 6 valeurs dans les éléments correspondants du tableau.

char t [ 10 ] = { ‘A’, ‘B’, ‘C’}; déclaration partielle du tableau

Quand un tableau est initialisé, il n’est pas obligatoire de spécifier sa dimension :


58
Int tab [ ] = { 4, 6, 3, 3}

Dans les tableaux numériques, la dimension du tableau correspondra au nombre de valeurs


initiales .
tab [ 0]=4 ; tab[1]=6; tab[2] = 3; tab[3] = 3;

Cas particulier des chaines de caractères :

Char couleur [ ] = "ROUGE"

Le caractere (\0 ) de fin de chaine est ajouté automatiquement à la fin de la chaine:


Couleur [ 0 ] = ‘R’; Couleur [ 1 ] = ‘O’;
Couleur [ 2 ] = ‘U’; Couleur [ 3 ] = ‘G’;
Couleur [ 4 ] = ‘E’; Couleur [ 5 ] = ‘\0’;

Char couleur [ 6] = “ROUGE”

Remarque:
Dans le cas d’un tableau déclaré comme une variable externe :
• la dimension peut ne pas être spécifiée
• le tableau ne peut pas être réinitialisé car il joue le rôle de variable globale
Exemple:
Source1 source2
Int c[ 4]= { 1,2,4,6}; extern int c[ ];
Char text [ ] = “HELLO”; extern char text [ ];
……………. ………………….

Les tableaux multidimensionnels


( à plusieurs indices)

1) Définition d’un tableau multidimensionnel

Syntaxe :
Class type nom [ dimension1] dimension2] [ dimension3]……

dimension 1, dimension2….. sont des constantes ou des expressions constantes.

Il n’y a aucune limitation sur le nombre de dimensions que peut avoir un tableau .

Exemple : tableau à 2 dimensions

int Tab [4][3];

Tab est défini comme un tableau de 4 *3 éléments .

En mémoire , ses éléments sont ordonnés comme suit :


Tab[0][0] le 1er élément du tableau

59
Tab[0][1] le 2ème élément
Tab[0][2]
Tab[1][0]
Tab[1][1]
……….
Tab[3][2] le dernier élément

2) Indexation et Initialisation d’un tableau multidimensionnel

Un tableau multidimensionnel peut être vu comme une succesion de tableaux à une dimension.

Exemple
int tab[ 4][3] = { { 1,1,1}, { 2,2,2}, {3,3,3},{ 4,4,4} }

int X [3][2} = { 1,2,1,2,1,2}

Les valeurs du tableau X seront exploitées en fonction de la manière dont les éléments sont rangés
en mémoire.

3) L’ adresse d’un tableau multidimentionnel

Dans le cas d’un tableau à une seule dimension : le nom du tableau représente son adresse de debut.

Dans le cas d’un tableau à deux dimensions :

Exemple :
int t[3][4]
t[0] représente l’adresse de début des 4 premiers éléments (le 1er bloc)
t[1] représente l’adresses de début des 4 éléments suivants ( le second bloc)

t[0} correspond a l’adresse de l’élément t[0][0]


t[1] ……………………..adresse de t[1][0]
t[2] correspond a l’adresse de l’élément t[2][0]

 t[ i ] ( i variant de 0 à 2 ) est une constante ( adresse fixe) . Elle ne peut pas être une lvalue. De
ce fait , l’affectation n’est pas autorisée sur t[ i ].

Exemple
t[ 1 ] ++ n’est pas autorisé
t[1][3] ++ est autorisé car t[1][3] est une lvalue

 t + 1 permet d’incrémenter l’adresse t de 4 fois la taille d’un entier ( le prochain bloc).

60
Les tableaux et les fonctions

Lors de l’appel d’une fonction, placer le nom d’un tableau comme paramètre effectif de la
fonction , revient à transmettre l’adresse du tableau à la fonction. La fonction pourra donc faire toutes
les manipulations sur les éléments de ce tableau.

1) Cas de tableau à une dimension


Dans une fonction , la définition de tableau comme paramètre peut se faire de plusieurs
façons :
a) Soit en specifiant la taille

Exemple :
void init ( int tab[10]} ; /* definition de la fonction
{
int I ;
for ( I=0 ; I<10 ; I++) t[ I ] = 1 ;
}

Les appels possibles de cette fonction :

int t1 [10 ], t2[ 10 ] ;


……….

init ( t1) ;
init ( t2 ) ;

ou bien

+ void init ( int t[ ] ) /* la réservation de l’espace memoire sera faite dans le programme appelant
{
int I ;
for ( I=0 ; I<10 ; I++) t[ I ] = 1;
}

b) Soit en specifiant un tableau de taille variable :


Dans ce cas , le nom du tableau ainsi que sa taille sont transmis comme paramètre à la
fonction.

Exemple :

Void init ( int tab[ ], int taille) /* en-tete de la fonction


61
{
int I ;
for ( I=0 ; I< taille ; I++ ) t [ I ] = 1;
}

Les appels possibles de cette fonction :

Int t1 [10 ], t2[ 20 ] ;


……….

init ( t1, 10) ;


init ( t2, 20 ) ;

2) cas de tableau à deux dimensions


Compte tenu des de la façon dont sont rangés les éléments d’un tableau à deux dimensions
( ligne par ligne), la deuxième dimension du tableau doit être connue à la compilation.
1) tableau de taille fixe

Exemple :

Void init ( int tab[ 2 ][ 3 ]) /* en-tête de la fonction


{
int I,j ;
for ( I=0 ; I< 2 ; I++ )
for ( j = 0 ; j < 3 ; j++ )
t [ I ][ j ] = 1;
}

Les appels possibles de cette fonction :

int t1 [2 ][ 3 ], t2[ 2 ][ 3 ] ;
……….

init ( t1) ;
init ( t2 ) ;

2) tableau de dimensions variables

Dans ce cas , le nom de la matrice tableau ainsi que sa taille sont transmis comme
paramètres à la fonction. Pour trouver l’adresse d’un élément quelconque, la seconde
dimension doit être connue par le compilateur.
62
Exemple :

Void init ( int tab[ ] [3] , int taille1) /* en-tete de la fonction


{
int I,j ;
for ( I=0 ; I< 2 ; I++ )
for ( j = 0 ; j < 3 ; j++ )
t [ I ][ j ] = 1;
}

Les appels possibles de cette fonction :

int t1 [2 ][ 3 ], t2[ 4 ][ 3 ] ;

……….

init ( t1 , 2) ;
init ( t2 , 4 ) ;

63
Les pointeurs

64
Les pointeurs

Un pointeur est une variable qui permet de manipuler des adresses.

Pointeurs et variables simples

Un pointeur peut contenir l’adresse d’une autre variable . Il permet donc d’accéder indirectement à
une variable.

1) Déclaration d’un pointeur

syntaxe :
classe type * nom ;

classe : spécifie la classe d’allocation de l’objet pointé


type : représente le type de l’objet pointé
“ * ” : permet de préciser que c’est une déclaration de pointeur.

Exemple :

int *ptr ; /* ptr est un pointeur vers un objet de type entier


char * ptrc ; /* ptrc est un pointeur vers un objet de type char
int v;
v

ptr =adresse de v Valeur de


v

2) Initialisation d’un pointeur

comme toute variable, un pointeur doit être initialisé. Un pointeur doit contenir l’adresse d’une
variable .

Les operateurs & et *

L’ adresse de v est donnée par l’expression &v ( & est l’opérateur d’adresse)
ptr = &v affecte à ptr l’adresse de v
On dit alors que ptr pointe vers la variable v.

L’opérateur * ou opérateur d’indirection qui est appliqué seulement aux variables de type pointeur,
permet de donner le "contenu de " la variable.
Si ptr = &v alors *ptr ( contenu de ptr) fait référence à la variable v qui est pointée .

65
v et *ptr représentent le contenu de v.
&v et ptr représentent l’adresse de v.

Exemple:
int var = 1m
int * ptr;
main()
{
ptr = &var;
/* acces direct et indirect à var
printf( “ acces direct, var = %d\n”, var);
printf( “ acces indirect, var = %d\n”, *ptr);
/* affichage de l’adresse avec les deux methodes
printf( “ adresse de var = %d\n”, &var);
printf( “ adresse de var = %d\n”, ptr);
}

3) Opération sur les pointeurs

a) l’initialisation d’un pointeur peut être faite lors de sa déclaration ou dans le programme.

Il n’est pas autorisé d’affecter une valeur entière à un pointeur exceptée :

• la valeur "NULL" ( prédéfinie dans la librairie stdio ) qui spécifie que le


pointeur ne pointe vers rien.

• L’adresse d’une autre variable .

• ou la valeur d’un autre pointeur


.

Exemple 1:
float u,v ;
float *p1 = &v , *p2;
p2 = p1 ;

Exemple2:
int n = 10,p;
int *ad1, *ad2, *ad3;

ad2 = &p;
ad1 = &n;

*ad2 = ad1 + 2; /*ad1et *ad2 sont des lvalues


/* elle équivaut à p = n + 2 = 12

b) Un pointeur peut être incrémenté ou décrémenté

66
Quand un pointeur est incrémenté , sa valeur est incrémentée en un multiple de la taille de
l’objet pointé.

Exemple :
Int * ptr ;
ptr = ptr + 1 : // le contenu de ptr sera incrementé de la taille d’un
entier.
// ptr va donc pointer vers l’objet suivant.

Remarque :
• Toutes les opérations arithmétiques lui sont interdites exceptées ( L’addition
et la soustraction).

• La soustraction de deux pointeurs de même type est autorisée. Le résultat de


l’opération est le nombre d’éléments situés entre les deux adresses.

• La comparaison de deux pointeurs de même type est autorisée.

Exemple:
Pour initialiser les éléments d’un tableau à 0.

int t[ 5];
int *p;
for ( p=t; p< t+5; p++) *p = 0;

Les pointeurs et les tableaux

En C, il y a une relation forte entre les tableaux et les pointeurs.

Cas des tableaux à un indice

Un tableau à une dimension peut être représenté à l’aide d’un pointeur.


Le nom du tableau peut être vu comme un pointeur vers le 1er élément du le tableau.

Exemple :
int tab [10]; /* définit un tableau de 10 éléments de type entiers.
int * p; /* definit un pointeur vers un objet de type int.

p = & tab[0]; /* assigne à p l’adresse du 1er élément de tab.


Peut être écrit :
p = tab ;
p + 1 pointe vers le prochain élément de tab .Il contient l’adresse de tab [1].

* (p + 1) est le contenu de l’élément tab[1] du tableau.

La seule difference entre le pointeur p et le nom du tableau tab est:

67
• le pointeur p est une variable
si p = tab alors p++ est permis

• le nom du tableau est une constante ( nest pas une lvalue)


les opérations suivantes ne sont pas autorisées
tab = p ;
tab ++ ;
p = &tab;

Pointeurs et index de tableaux:

Puisque un nom de tableau est un pointeur vers le 1er élément du tableau, on peut accéder au
premier élément de ce tableau avec l’opérateur indirect ( * ).
si tab [ ] est un tableau, l’expression *tab représente le premier élément de ce tableau
*tab == tab [ 0 ]
*(tab +1) == tab [ 1 ] etc….

Cas des chaines de caractères

En C , il n’éxiste de type de données pour les chaines de caractères. Les chaines de caractères
sont manipulées à l’aide de tableaux de caractères.
Soit la déclaration suivante :
char * t = “hello” est équivalente à char t[ 6 ] = “hello”

permet de déclarer le pointeur t vers le premier caractère de la chaine de caractères. Le pointeur t est
utilisé pour accéder à la chaine
t[0] correspond au caractère h
t[1] correspond au caractère e etc…
t[5] correspond au caractère null à la fin

Dans ce cas , La déclaration du pointeur t entraine la réservation implicite


( à la compilation) d’un espace mémoire fixe pour ce tableau de 6 caractères.

Allocation dynamique d’espace mémoire


Dans le cas, par exemple , où la chaine de caractères doit être lue du clavier, on ne peut pas prévoir
la taille de cette chaine, il faut alors allouer de l’espace de façon dynamique

Les fonctions malloc() et free() sont des fonctions de la librairie standard stdlib qui permettent
respectivement d’allouer et de libérer un espace mémoire de façon dynamique.

Exemple1:
int *t;
char *texte;
/* allocation mémoire pour un tableau de 10 entiers
t = (int *) malloc( 10 * sizeof( int) ) ;
/* allocation mémoire pour un tableau de 99 caractères plus un pour null
texte = (char*) malloc( 100);

68
malloc( ) est appelée pour faire une réservation d’un espace pour le nombre d’octets nécessaires
et nous renvoit un pointeur de type void vers le bloc de mémoire qui a été réservé.
L’avantage de la réservation dynamique : est la possibilité de réserver de l’espace mémoire durant
l’éxécution du programme . Le processus d’allocation peut être rappelé plusieurs fois durant l’éxécution
d’un programme .

Exemple2:Le programme qui affiche la chaine de caractères ABCDEF…….YZ


#include <stdio.h>
#include <stdlib.h>
char *ptr, *p;
main()
{/* allocation d’un bloc de 35 caractères
ptr = malloc( 35 * ( sizeof(char));

/* initialisation de la chaine de caractères


p = ptr;
for ( I=65; I < 91; I ++)
*p ++ = I ;
*p = ‘\0’;
/* afficher chaine
puts( ptr);
}

Cas de tableaux à 2 indices :

Un tableau à 2 indices peut être représenté à l’aide d’un pointeur.


1er cas :
Un tableau à 2 indices peut être vu comme une collection de tableaux à une dimension.

Etant donnée la déclaration suivante d’un tableau:


int multi [2][4]; multi[ 0 ]
multi

multi[0] [0]

le tableau multi contient 2 éléments pricipaux.

Chacun de ces deux éléments contient à son tour 4 éléments de type int.

69
Comme pour les tableaux à une dimension, le nom d’un tableau à 2 dimensions est un pointeur vers le
premier élément du tableau, on peut accéder au premier élément de ce tableau avec l’opérateur indirect
( * ).

Un tableau à 2 dimensions peut être défini à l ‘aide d’un pointeur à un groupe de tableaux ( à un indice)
contigus.

La declaration : type (*tab) [dimension2]

Remplace : type tab[dimension1][dimension2]

Exemple :

Un tableau de dimension 10 x 20 peut être déclaré comme suit :

int ( *x ) [ 20] plutôt que int x[10][20]

le nom du tableau x pointe vers le debut du tableau ou le 1er bloc de 20 entiers


x + 1 pointe vers le 2ieme bloc de 20 entiers
x + 2 pointe vers le 3ieme tableau…

le nombre de lignes n’est pas specifié.


x == &x[0]
x est un pointeur vers x[0], x[0] est un pointeur vers x[0][0]
x+1 == &x[1]
x+2 ==& x[2]

L’element x[1][2] peut etre accédé en écrivant *(* (x + 1) + 2)

Donc *(x) == x[0] est le pointeur vers le 1er élément du 1er tableau
*( x + 1 ) == x[1] pointe vers le 1er élément du 2ième tableau
*( x + 1) + 2 ==& x[1][2] est le pointeur au 3ième élément du 2ième tableau

son contenu *( *(x+1) + 2) == x[1][2] est le 3ième élément du 2ième tableau.

*( x + 2) == x[2] pointe vers le 1er élément du 3ième tableau …

Exemple:

#include <stdio.h>
void print_tableau( int (*ptr)[ 4 ]);
void print_tableau( int (*ptr)[ 4 ]);
main()
{
int multi[2] [4] = { {1,2,3,4},
{ 5,6,7,8},

70
{ 8,9,10,11},
{ 12,13,14,15}};

/* ptr est un tableau vers un tableau de 4 entiers


int (*ptr) [ 4 ], I;
ptr = multi;
for(I=0; I<3; I++)_
print_tableau( ptr++);
}
/* affiche les éléments d’un tableau de 4 entiers
void print_tableau( int (*ptr)[ 4 ])

{
int *p, j;
p = ( int*) ptr;
for (j=0; j < 4; j++)
printf( “%d”, p++)
}

2) Un tableau à deux dimensions peut être exprimé sous forme d’un tableau de pointeurs plutôt
qu’un pointeur vers un groupe de tableaux.
En C, les pointeurs constituent un type de données, on peut alors définir un tableau de pointeurs.

L’usage le plus fréquent de cette construction concerne les chaînes de caractères

Exemple1 :
char * x [ 10] ;

x[ 0] pointe vers le début de la 1ere ligne


x[ 1] pointe vers le début de la 2ieme ligne
x[ 2] pointe vers le début de la 3ieme ligne…

Le nombre d’éléments dans une ligne n’est pas spécifié

L’élément x[1 ][ 2] peut être accédé en écrivant * (x [1] + 2)

Exemple2:
#include <stdio.h>
void print_tableau( int (*ptr)[ 4 ]);
void print_tableau( int (*ptr)[ 4 ]);
main()
{ /* déclaration d’un tableau de 3 pointeurs de type char
char *message [3] = { “un”, “deux”, “trois”};
int j;
for(j=0; j<3; j++)
printf(“%s\n”, message[j]);
}

71
message[0] 1000 u n \0

1200
message[1] d e u x \0

1100
t r o i s \0
message[2]

Exemple3 calcul de la somme de deux matrices

main ()
{ int *A[20], *B[20], *C[20] ;
int i, j ;
/* lire ncol , nligne;

/* allouer mémoire

for( I = 1 ; I < nligne ; I++)


{ A[ I ] =( int*) malloc ( ncol * sizeof(int)) ;
B[ I ] =( int*) malloc ( ncol * sizeof(int)) ;
C[ I ] =( int*) malloc ( ncol * sizeof(int)) ;

/* lire A et B
/* calcul de la somme
for( j = 1 ; j < ncol ; j++)
*(C [ I ] + j) = *(A [ I ] + j) + *(B [ I ] + j) ;
}
}

Les pointeurs et les fonctions

En C, un pointeur qui représente une adresse peut être passé en paramètre à une fonction.

Etant donné qu’en C le passage des paramètres se fait par valeur, il est interdit à une fonction de
modifier la valeur de ses paramètres effectifs. les pointeurs permettent de résoudre ce problème.

En effet, au niveau de l’appel d’une fonction , si les paramètres effectifs sont des adresses, les
paramètres formels correspondants seront définis de type pointeur pour pouvoir recevoir les valeurs
transmises à l’appel .

Exemple 1
#include<stdio.h>
void main ()
{
void echange( int *a,int *b) ;

72
int n=10, p=20 ;
echange ( &n, &p) ; /* au retour de l’appel
/*les valeurs de n=10 et p=20
}

void echange( int *a, int * b)


{

int c ;
c= * a ;
*a= *b ;
*b= c ;
}

Exemple2 :
+ void init( int * t) ; /* definition de la fonction
{ /* la réservation de l’espace memoire sera faite dans le programme appelant

int I ;
for ( I=0 ; I<10 ; I++, t++) *t = 1;
}

Les appels possibles de cette fonction :

int t1 [10 ], t2[ 10 ] ;


……….

init ( t1) ;
init ( t2 ) ;

ou bien

+ void init( int * t) ;


{
int I ;
for ( I=0 ; I<10 ; I++) *( t+I ) = 1;
}

73
Exemple3
+ void init( int (* t )[3 ] , int nligne) ;
{
int I ;
for ( I=0 ; I< nligne ; I++ )
for ( j = 0 ; j < 3 ; j++ )
*(*(t + I ) + j ) = 1;

}
ou bien

+ void init( int * t [2 ], int ncol) ;


{
int I ;

for ( I=0 ; I< 2 ; I++ )


for ( j = 0 ; j < ncol ; j++ )
*( (t[ I ] + j ) = 1;

74
Les fichiers

75
Les fichiers
. ;

En C ,un fichier est un ensemble d’informations stockées sur une memoire secondaire ou un
peripherique donné.( disque,disquette, ecran clavier…)

• Dans le cas des entrées- sorties conversationnelles , l’échange d’information entre l e programme et
l’utilisateur se fait à l’aide de deux fichiers standards stdin ( fichier d’entrée associé au clavier) et
stdout ( fichier de sortie associé à l’écran).

• Dans les autres cas, un fichier sera identifié par un nom externe.

Les manipulations des fichiers s’effectuent par l’intermédiaire de fonctions de la bibliothèque standard.

Déclaration d’un fichier

La déclaration d’un fichier dans un programme se fait suivant la syntaxe suivante:

FILE ptr; / / ptr est un pointeur de fichier qui va correspondre au nom interne
associé au fichier.

Ouverture et fermeture d’un fichier

La fonction fopen permet d’ouvrir un fichier en mode lecture,d’écriture , ou ajout


avant de le créer
Syntaxe

fopen ( “Nom externe du fichier”, “mode” )

mode= “r” ouverture en lecture, si le fichier n’éxiste pas, il y a renvoi d’une erreur
mode = “w” ouverture en ecriture. Si le fichier n’éxiste pas, il sera creé
s’il existe deja , son ancien contenu sera inaccessible

mode = “a” ouverture en ajout

La fonction fopen retourne lepointeur associé au fichier ( ou son nom interne) si l’ouverture s’est bien
deroulée ,ou bien le pointeur nul, si erreur à l’ouverture ( dans le cas où on essaye d’ouvrir en lecture un
fichier qui n’éxiste pas).

La fonction fclose permet de fermer un fichier ouvert.

Syntaxe

fclose (ptr)

ptr est le pointeur correspondant au fichier initialisé à l’ouverture

76
Elle retourne un entier égal à 0 si la fermeture s’est bien deroulée et –1 sinon

Exemple
#include <stdio.h>
main()
{
FILE * ptr;
ptr = fopen( “fich.txt”, “w”);
if (ptr == null)
printf(“ erreur , ne peut pas ouvrir le fichier”);
…….

fclose(ptr);
}

Création d’un fichier

Un fichier peut être crée de deux façons:

• directement à l’aide d’un éditeur de texte


• par un programme qui écrit des informations dans ce fichier

1) Caractère par caractère

si le fichier est considéré comme une séquence de caractères, il peut être crée en lisant des caractères du
clavier à l’aide de getc et les écrire dans le fichier à l’aide de putc.

putc et getc sont les fonctions de lecture et écriture d’un caractère d’un fichier quelconque.

Syntaxe: getc ( ptr)


Valeur retournée: le caractère lu ou la valeur null dans le cas d’erreur

putc(octet , ptr)
Valeur retournée: le caractère écrit ou la valeur null dans le cas d’erreur

77
Exemple : Programme qui lit une ligne de texte du clavier caractère par caractère et la recopie caractère
par caractère sur le fichier

#include <stdio.h>
main()
{ char c;
FILE * ptr;
ptr = fopen( “fich.txt”, “w”);
if (ptr == null)
printf(“ erreur , ne peut pas ouvrir le fichier”);

do putc ( c=getch(), ptr);


while ( c1= “\n’);…….

fclose(ptr);
}

Remarque:

Les fonctions fgets et fputs premettent de manipuler plus facilement les fichiers formés de chaines de
caractères.

Syntaxe: fgets( chaine,lgmax,ptr)

fputs( chaine,ptr)

2) Création de fichiers formattés

Dans les fichiers texte , chaque octet représente un caractère, y compris le caractere ‘\n’.
Ils peuvent être vus comme une suite de lignes.

Les fonctions fscanf et fprintf permettent de réaliser des entrées sorties formattées.

Exemple

#include <stdio.h>
main()
{ char c;

typedef struct{
char nom[40],prenom[40];
int numero;
}record;
78
record person;

FILE * ptr;

ptr = fopen( “fich.txt”, “w”);


if (ptr == null)
printf(“ erreur , ne peut pas ouvrir le fichier”);

do scanf(“%[ \n], person.nom);


scanf(“%[ \n], person.prenom);
scanf(“%d, person.numero);
fprintf( ptr,” \n%s\n” , person.nom);
fprintf( ptr,” %s\n” , person.prenom);
fprintf( ptr,” %d\n” , person.numero);
while ( strcpm( person.nom,”fin”) != 0);…….

fclose(ptr);
}

79
Exercices

1) Lire à l’écran un tableau d'entiers positifs ou négatifs dont la fin est signalée par le nombre zéro. La
fonction parcourt ce tableau en transformant les négatifs en positifs.

2) Ecrire un programme qui lit la dimension N d'un tableau T du type int (dimension maximale: 50
composantes), remplit le tableau par des valeurs entrées au clavier et affiche le tableau.

Ranger ensuite les éléments du tableau T dans l'ordre inverse sans utiliser de tableau d'aide. Afficher le tableau
résultant.

Idée: Echanger les éléments du tableau à l'aide de deux indices qui parcourent le tableau en commençant
respectivement au début et à la fin du tableau et qui se rencontrent en son milieu.

3) Ecrire un programme qui lit les points de N élèves d'une classe dans un devoir et les mémorise dans un
tableau POINTS de dimension N.

80
* Rechercher et afficher:

- la note maximale,

- la note minimale,

- la moyenne des notes.

* A partir des POINTS des élèves, établir un tableau NOTES de dimension 7 qui est composé de la façon
suivante:

NOTES[6] contient le nombre de notes 60

NOTES[5] contient le nombre de notes de 50 à 59

NOTES[4] contient le nombre de notes de 40 à 49

...

NOTES[0] contient le nombre de notes de 0 à 9

Etablir un graphique de barreaux représentant le tableau NOTES. Utilisez les symboles ####### pour la
représentation des barreaux et affichez le domaine des notes en dessous du graphique.

Idée: Déterminer la valeur maximale MAXN dans le tableau NOTES et afficher autant de lignes sur l'écran.
(Dans l'exemple ci-dessous, MAXN = 6).

Exemple:

La note maximale est 58


La note minimale est 13
La moyenne des notes est 37.250000

6 > #######
5 > ####### #######
4 > ####### ####### #######
3 > ####### ####### ####### #######
2 > ####### ####### ####### ####### #######
1 > ####### ####### ####### ####### #######
+-------+-------+-------+-------+-------+-------+-------+
I 0 - 9 I 10-19 I 20-29 I 30-39 I 40-49 I 50-59 I 60 I

4) Ecrire un programme qui lit deux chaînes de caractères CH1 et CH2 et qui copie la première moitié de
CH1 et la première moitié de CH2 dans une troisième chaîne CH3. Afficher le résultat.

a) Utiliser les fonctions spéciales de <string>.

b) Utiliser uniquement les fonctions gets et puts.

81
5) Ecrire un programme qui demande l'introduction du nom et du prénom de l'utilisateur et qui affiche alors
la longueur totale du nom sans compter les espaces. Employer la fonction strlen.

Exemple:

Introduisez votre nom et votre prénom:


Mickey Mouse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Bonjour Mickey Mouse !
Votre nom est composé de 11 lettres.

6)
1.
Déclarer un entier i et un pointeur p vers un
entier ;
2.
Initialiser l'entier à une valeur arbitraire et faire pointer p vers i ;
3.
Imprimer la valeur de i ;
4.
Modifier l'entier pointé par p (en utilisant p, pas i) ;
5.
Imprimer la valeur de i.

7)
1.
Déclarer et initialiser statiquement une matrice [5,5] d'entiers (tab).
2.
Écrire une fonction (print_mat) qui admette en paramètre une matrice [5,5] et qui imprime ses éléments
sous forme de tableau.
3.
La procédure main fera un appel à print_mat pour le tableau tab

QUESTION #1

/* EX1 */
#include <stdio.h>

void main( void )


{
int i, x[6];
FILE *fich;

fich = fopen(«a:\\exer1.don»,
«r» );

82
for( i=5; i>=0; i-- )
fscanf( fich, «%d», &x[i]);

for( i=0; i<6; i++ )


if( x[i] == -1 )
printf( «***» );
else
printf( «%3d», x[i] );
}

Écrivez ce que le programme affichera lors de son exécution, sachant


que le fichier EXER1.DON contient l’enregistrement suivant :
50 -1 10 3 -1 7

QUESTION #2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* EX2 */
#include <stdio.h>

void main( void )


{
int b[5], i;

b[0] = 10;
for( i=1; i<5; i++ )
b[i] = b[i-1] * 2;

printf( «%5d», b[4] );


}

Écrivez ce que le programme affichera lors de son exécution.

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

/* EX3 */
#include <stdio.h>

void main( void )


{
int i, j, k;
int a[3], b[3], c[6];
FILE *fich;

fich = fopen(«a:\\exer3.don», «r» );

for( i=0; i<3; i++ )


fscanf( fich, «%d%d», &a[i], &b[i] );
j= 0;
for( k=0; k<3; k++ )
{
c[j] = a[k];
c[j+1] = b[k];
j = j + 2;
}
for( i=0; i<6; i++ )
printf( «%2d», c[i] );
}
Reproduisez l’affichage du programme, sachant que le fichier EXER3.DON contient:
1 2 3 4 5 6

QUESTION #4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* EX4 */
#include <stdio.h>

void main( void )


{
int j, k, n[10];

k = 2;
n[0] = 10;
n[1] = 2;
for( j=1; j<5; j++)
{
n[k] = n[k-1] / 2;
n[k+1] = n[k] * (k+1);
k = k + 2;
}
printf( «%5d%5d\n», n[n[2]], n[9] );
}
Écrivez ce que le programme affichera lors de son exécution.

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

QUESTION #5

/* EX5 */
#include <stdio.h>

void main( void )


{
int i, j, k, p, s[30];

FILE *fich;

fich = fopen( «a:\\exer5.don», «r» );

for( j=1; j<=3; j++ )


{
k = 1;

for( i=1; i<=2; i++ )


{
p = j * k;
fscanf( fich, «%d», &s[p] );
k = k + 3;
}
}

printf( «%3d%3d%3d\n», s[4], s[8], s[12] );


}

Écrivez ce que le programme affichera lors de son exécution,


sachant que le fichier exer5.don contient les deux
enregistrements suivants :
10 9 8 7 6 5 4 3 2 1
11 12 13 14 15 16 17 18 19 20

QUESTION #6
/* boucle8 */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include <stdio.h>

void main( void )


{
int k, n, som;

for( k=1; k<=3; k++ )


{
som = 0;
for( n=1; n<=4; n++ )
som = som + n;
printf( «%10d\n», som );
}
}
QUESTION #7

85
/* boucle9 */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include <stdio.h>

void main( void )


{
int k, n, som;

som = 0;
for( k=1; k<=3; k++ )
{
for( n=1; n<=4; n++ )
{
som = som + n;
printf( «%3d», som );
}
printf( «\n» );
}
}

QUESTION #8

- fonctions

1- Qu'affiche le programme suivant ? Expliquer.

void ma_fonction( int x ) {


int y;
x = 0;
y = -1;
}

void main(void) {
int x = 22;
int y = 23;
ma_fonction(x);
printf( "x=%d y=%d\n", x, y );
}

2- Donner le prototype d'une fonction nommée EstPremier testant si un nombre n est ou non
premier.

3- Donner le prototype d'une fonction DemandePoint qui demande à l'utilisateur les coordonnées
(x,y) d'un point.

QUESTION #9

Que fait la fonction C suivante ?

void Mystere( int *dp, int *sp ) {


if ( *sp > 0 )

86
*dp = *sp;
else
*dp = 0;
}

QUESTION #10

Dans le programme suivant, rayez les lignes illégales (erreurs). Qu'est ce qui s'affiche ?

void main( void ) {


int i = 0;
int *p;
float x = 3.14;
float *f;

p = &i;
*f = 666;
f = &x;
*f = *p;
*p = 34;
p = f
*p = *p + 1;
printf( "%d %f\n", i, *f );
}

QUESTION #11

- Passage par adresse

1- Écrire une fonction qui échange le contenu de deux variables entières.


Exemple d'utilisation :

...
int a = 10, b = 22;
Echanger( &a, &b );
/* ici a == 22 et b == 10 */

2- En utilisant la fonction Echanger, écrire une fonction Permute3 qui effectue une permutation
circulaire de trois variables, comme dans l'exemple ci-dessous :

...
int a = 10, b = 22, c = 33;
Permute3( &a, &b, &c );
/* ici a == 33, b == 10, c == 22 */

QUESTION #12

Dites ce qui sera affiché à l'écran à l'exécution du programme suivant:

87
#include <stdio.h>
void main( void )
{
int i, j, k;
int TabA[3], TabB[3], TabC[6];
FILE *Fich;

Fich = fopen(“a:\\exer.don”, “r” );

for( i=0; i<3; i++ )


fscanf(Fich, “%d%d”, &TabA[i], &TabB[i] );
j= 0;
for( k=0; k<3; k++ )
{
TabC[j] = TabA[k];
TabC[j+1] = TabB[k];
j += 2;
}
for( i=0; i<6; i++ )
printf( “%2d”, TabC[i] );
}

Reproduisez l’affichage du programme, sachant que le fichier EXER.DON contient:


123456
Ceci est l’écran avec les colonnes de 1 à 20

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

88
QUESTION #13

Supposons qu’on a les variables suivantes :

int a,b;
int *pta, *ptb;

a=5; b=8;
pta=&a; ptb=&b;

a) Que contient pta et ptb ?

b) Pour chaque instruction indiquer la valeur finale des variables impliquées (on
reprend toujours les valeurs de départ) :

a=a+(*ptb) ; a=

*ptb=*ptb+a a= b=

*pta=*ptb+*pta a= b=

c) Donner l’instruction pour placer la valeur 40 dans a en utilisant pta.


QUESTION #14

Dites ce qui sera affiché par chacun des printf après l’appel de la fonction AuSecours.

#include <stdio.h>
void AuSecours(int *k, int *g);

void main(void)
{
int a = 1,
b = 2;

AuSecours(&a, &b);
printf("A = %d, B = %d\n", a, b);
a=1; b=2;
AuSecours(&b, &a);
printf("A = %d, B = %d\n", a, b);

void AuSecours(int *k, int *g)


{
int nb;

nb = 5;
nb = (nb * (*k)) / *g ;
*k = nb * (*g) ;
}

References:
http://www.iro.umontreal.ca/

90

Vous aimerez peut-être aussi