Introduction à la programmation en C
Introduction à la programmation en C
Langage C
2
Plan du cours
CHAPITRE 1 : Introduction. ___________________________________________________ 5
1.1) Les données et les résultats. ________________________________________________ 5
1.2) Le codage. _____________________________________________________________ 6
1.3) Le compilateur.__________________________________________________________ 6
1.4) Comment exécuter un programme avec Dev-C++ ? _____________________________ 7
1.5) Comment obtenir de l’aide lorsqu’on programme en C ? _________________________ 7
2
3
8.6) Retour d’une valeur. _____________________________________________________ 30
8.7) Passage d’un tableau en paramètre d’une fonction. _____________________________ 31
Chapitre 12 : Structure
1. Préambule
2. Déclaration d’une structure
3. Déclaration d’une variable de type structure
4. Utilisation d’une structure
4.1- Utilisation des champs d’une structure
4.2- Utilisation globale d’une structure
5. Initialisation de structures
6. La porté du modèle de structure
7. Imbrication de structures
7.1- Structure comportant des tableaux
7.2- Tableaux de structures
7.3- Structures comportant d’autres structures
Chapitre 13 : Pointeur
1. Préambule
2. Déclaration d’un pointeur
3. Désignation de l’adresse d’une variable (Operateur &)
4. Désignation de la valeur pointée par un pointeur (Opérateur *)
5. Les paramètres de type pointeur.
6. Retour d'une adresse à l'issue d'une fonction.
7. Désignation d'un champ d'une variable structurelle à partir de son adresse.
8. Précautions
3
4
4. Retour ou non du premier élément du tableau.
4
5
CHAPITRE 1 : Introduction.
Programmation :
La programmation c’est le moyen de créer sur un ordinateur une très grande variété d’outils
informatiques, c’est-à-dire des logiciels.
Programme :
Un programme c’est un enchaînement d’opérations élémentaires permettant d’atteindre un
résultat précis à partir de données précises.
Exemple : un professeur souhaite créer un programme qui calcule la moyenne de ses élèves en
fonction de leurs notes. Ici, le but recherché, c’est-à-dire le résultat, c’est la moyenne de chaque
élève. Quant aux données de départ, c’est-à-dire ce qui permettra de calculer cette moyenne, ce
seront les notes de chaque élève. Le professeur a donc deux possibilités pour introduire les notes
dans le programme. Première possibilité : il donne les notes au programme en les tapant au
clavier pendant que celui-ci est en cours d’exécution. C’est la technique dite de
« communication » (l’utilisateur va communiquer les données au programme). Deuxième
possibilité : il enregistre préalablement les notes des élèves dans la mémoire ou sur un disque de
l’ordinateur, puis il exécute le programme qui va aller chercher ses données là où elles ont été
stockées. C’est la technique dite d’« archivage » (les données ont été archivées dans la mémoire
ou sur le disque).
5
6
quasiment inévitable de faire suivre une communication d’un archivage, car il faut bien conserver
les données que l’utilisateur a donné au programme.
A la base, l’archivage se fait avec des variables auxquelles on affecte une valeur, mais on
verra qu’il existe d’autres façons d’archiver des données, par exemple en les inscrivant dans un
fichier (c’est-à-dire non plus dans la mémoire vive de l’ordinateur, mais sur un disque).
Puis va venir la question concernant les résultats du programme : que va-t-on en faire ? Là
encore, la question se pose en terme de communication ou d’archivage : va-t-on communiquer les
données à l’utilisateur (en les affichant, par exemple) ? Va-t-on les conserver dans un fichier pour
une utilisation ultérieure ? Va-t-on les mettre à disposition pour que d’autres programmes
puissent s’en servir ?
1.2) Le codage.
On sait que l’ordinateur code toutes les données sous la forme d’enchaînement de « 0 » et
de « 1 ». Chaque valeur (0 ou 1) est placée dans une cellule de la mémoire qu’on appelle un
« bit ». En rassemblant les bits par 4, on forme des quartets, et par 8, on forme des octets. Cette
façon est la plus courante de comptabiliser l’espace mémoire occupée par une donnée. Mais il y a
une notion très importante à bien comprendre dans le codage de l’ordinateur : un code seul
représente une valeur, mais on ne sait pas quel est le type de cette valeur (nombre entier, nombre
flottant, caractère, booléen, image, son, instruction de programme, etc ? ). C’est pour cette raison
que la programmation nécessite une grande attention dans la manipulation des types des données,
qui est d’ailleurs une grande source d’erreurs de logique.
D’autre part, il est bon de rappeler qu’un bit ne peut contenir que 0 OU 1. Il ne peut
contenir les deux en même temps et il ne peut pas ne rien contenir. Par conséquent, tout ce qui
fait référence à une partie de la mémoire ne peut pas être vide (une variable, par exemple,
contient obligatoirement une valeur, même si on ne lui a rien affecté, d’où un risque d’erreur
fréquent chez les débutants en programmation).
1.3) Le compilateur.
On appelle compilateur un logiciel qui traduit les algorithmes écrits dans un langage de
programmation en langage machine.
D’une certaine manière, on peut dire qu’il rend compréhensible pour l’ordinateur les programmes
écrits par un programmeur. Généralement, on lance le compilateur sur un programme pour
vérifier si l’ordinateur le comprend ou s’il repère des erreurs. Attention, l’ordinateur n’est pas
capable de repérer toutes les erreurs d’un programme, mais il s’aperçoit toujours des erreurs de
syntaxe, car ces erreurs rendent le programme inexploitable pour lui. D’autre part, il peut
également signaler tous détails qui lui paraissent douteux au moment de la compilation. C’est ce
qu’on appelle des « warnings » (ou avertissements).
6
7
HELLO_WORLD en C
#include <stdio.h>
main()
{
printf("hello, world");
}
- Lancer le compilateur pour vérifier la présence d’éventuelles erreurs (s’il n’y a pas
d’erreur, le compilateur va créer un fichier exécutable de votre programme dans le
répertoire de votre fichier source)
- Corriger les éventuelles erreurs
- Dans le menu Start, lancer « Run »
- Taper « command » ou « cmd » et cliquer sur « Ok », ce qui ouvrira une fenêtre DOS
- Ouvrir le répertoire dans lequel votre fichier source a été enregistré, avec la commande
« cd »
Utilisez quelques commandes DOS si nécessaire. (dir, cd, …)
- Lancer l’exécutable créé à partir de votre fichier source : c’est là que votre programme
s’exécute réellement
7
8
2.1) Le nom.
Bon à savoir
Il est laissé au choix du programmeur au moment de la déclaration, mais avec les contraintes
suivantes :
les caractères autorisés sont les suivants : toutes les lettres minuscules, toutes les lettres
majuscules, tous les chiffres et le symbole « _ ». Les lettres accentuées (é, è, à, etc…) ou
modifiées (ç, ï, etc…) ne sont pas admises.
le premier caractère ne doit pas être un chiffre.
un nom ne doit pas comporter plus de 32 caractères (si on le fait, le compilateur l’accepte,
mais sans tenir compte des caractères superflus, d’où un risque d’erreurs).
aucun espace n’est admis.
le langage C respecte la casse, c’est-à-dire que les lettres en minuscules sont différentes
des mêmes lettres en majuscules.
NB : De plus, il est conseillé d’utiliser des noms de variables explicites afin d’améliorer la
lisibilité du code.
Exemples
Identificateurs corrects: Identificateurs incorrects:
nom1 1nom
nom_2 nom.2
_nom_3 -nom-3
Nom_de_variable Nom de variable
deuxieme_choix deuxième_choix
mot_francais mot_français
2.2) Le type.
Le type d’une variable précise l’ensemble des valeurs qu’elle peut contenir, la taille de
son emplacement dans la mémoire de l’ordinateur, la manière dont l’information y est codée et
les opérations autorisées.
8
9
Bon à savoir
Les premiers types de variables disponibles en C sont les suivants :
int : nombres entiers signés (autrement dit, ce sont les nombres relatifs)
short : nombres entiers signés codés sur un nombre plus restreint de bits que le type int
long : nombres entiers signés codés sur un plus grand nombre de bits que le type int
float : nombres à virgule flottante (approximation des nombres réels)
double : nombres à virgule flottante codés sur un plus grand nombre de bits que le type
float
long double : nombres à virgule flottante codés sur un plus grand nombre de bits que le
type double
char : caractères
Les tailles varient selon les machines. Pour le type int, on peut trouver une taille allant de -
32768 à +32768 ce qui pour donne pour le type unsigned int une taille allant de 0 à 65535.
A lire
De plus, les types int, short et long peuvent être précédés du mot-clef « unsigned » qui
supprime la prise en charge des valeurs négatives. Ainsi, on dispose d’une marge deux fois plus
grande pour les valeurs positives.
D’autre part, on peut remarquer qu’il n’y a pas de type booléen. En fait, en C, les
variables booléennes doivent être simulées avec une variable d’un autre type dont on ne
considérera que deux valeurs possibles. Sachez cependant que le type booléen a été introduit dans
les compilateurs C, mais il est préférable de savoir simuler ce type à l’aide d’une variable d’un
autre type.
Le nombre de bits utilisés pour le codage dépend de la puissance de l’ordinateur utilisé
pour compiler et exécuter le programme, et il peut arriver que deux types proches comme short et
int ou comme float et double, disposent du même nombre de bits sur certaines machines.
Bon à savoir
Exemple : int X ;
A lire
La plupart des instructions du langage C sont terminées par un point virgule. L’oubli de ce
symbole engendre inévitablement une erreur de syntaxe lors de la compilation. Par contre, grâce
9
10
à cette contrainte syntaxique, il est tout à fait possible de mettre autant d’instructions que l’on
veut sur la même ligne.
Lors de sa déclaration, une variable reçoit forcément une valeur, mais une valeur complètement
imprévisible. Il est important de bien comprendre cet aspect, car si on oublie d’initialiser une
variable, on va travailler sur une valeur aléatoire, ce qui fait que l’on obtiendra forcément un
résultat également aléatoire, d’où un important risque d’erreur de logique.
Exemple : int X = 5 ;
Au cours du reste du programme, l’affectation de valeur aux variables se fait avec les
mêmes règles qu’en algorithmique, selon la syntaxe suivante :
Bon à savoir
Une variable peut recevoir une valeur de deux façons :
Par initialisation :
Syntaxe : <type> <nom> = <valeur d’initialisation> ;
Par affectation :
Syntaxe : <nom de la variable> = <valeur> ;
10
11
Chapitre 3
Communication entre le programme et
l’utilisateur.
Un programme est capable de communiquer à travers tous les périphériques dont dispose
un ordinateur de manière à obtenir les données dont il a besoin et à transmettre les résultats qu’il
trouve. Dans un premier temps, nous nous limiterons aux deux périphériques de communication
avec l’utilisateur les plus courants : l’écran et le clavier.
L’affichage de texte et de valeurs se fait grâce à la fonction printf. Cette fonction est
légèrement plus complexe que la fonction « ecrire » utilisée en algorithmique.
Bon à savoir
Affichage d’un texte :
Syntaxe : printf (« <texte> ») ;
Ex : printf(« Entrez une valeur entière quelconque : » ) ;
11
12
La fonction printf permet également d’afficher le résultat d’une ou plusieurs expressions.
Dans ce cas, il faut bien déterminer le type de l’expression pour pouvoir décider du code de
format que l’on va utiliser.
Exemple : pritnf (« %d », X+ Y) ;
Les valeurs de type float sont affichées par défaut avec 6 chiffres après la virgule. Il est
possible de modifier cette précision en indiquant le nombre de chiffres souhaités, précédé d’un
point, après le symbole « % »
Exemples :
x = 1.2345 ;
printf (« %.2f », x) ; On obtient : «1.24»
Une autre façon d’améliorer le gabarit d’affichage consiste à placer des sauts de ligne
grâce au symbole « \n » de manière à améliorer la présentation des données affichées.
La lecture des valeurs tapées au clavier par l’utilisateur se fait avec la fonction scanf. Son
rôle est de recevoir la valeur venant du clavier, de la convertir dans le type voulu et de la placer
dans la variable qui lui a été assignée.
syntaxe : scanf (« <type de la valeur lue> », &< variable d’accueil de la valeur lue>) ;
Exemple : scanf (« %d », &X) ;
Lorsqu’on veut lire plusieurs variables dans une seule instruction, il suffit de répéter la
syntaxe autant de fois que nécessaire.
Exemple : scanf (« %d%d%d », &X, &Y, &Z) ;
Cette instruction lit au clavier trois valeurs de type int et les affecte successivement aux variables
X, Y et Z.
Au moment de l’exécution du programme, si jamais l’utilisateur ne fournit pas
suffisament de données, le programme restera en attente jusqu’à ce que toutes les données
attendues soient fournies par l’utilisateur.
La lecture au clavier lors de l’exécution du programme est assez naturelle : l’utilisateur
tape sa valeur et la valide avec la touche « Entrée ». Tant qu’il ne l’a pas validée, il peut la
modifier à volonté. Lorsqu’il y a plusieurs valeurs à lire, l’utilisateur peut passer de l’une à
12
13
l’autre avec les touches « Entrée », « Espace » ou « Tabulation », mais la dernière doit forcément
être validée par la touche « Entrée ».
En ce qui concerne la lecture des valeurs de type flottant, les codes de format %e et %f
sont équivalents et les valeurs peuvent être lues sous n’importe quelle forme.
Pour la lecture des valeurs de type char, on rencontre certaines difficultés lorsque l’on
souhaite en lire plusieurs successivement : en effet, ici les caractères « Espace », « Tabulation »
ou « le saut de ligne entrée » sont considérés comme des séparateurs et en même temps comme
des caractères. Par conséquent, au moment de la lecture, lorsque l’utilisateur tape par exemple la
touche Entrée pour valider sa saisie, le caractère « saut de ligne » devient disponible pour la
lecture d’un caractère.
Il est possible dans certains cas d’esquiver ce genre de problème en prévoyant
l’introduction d’un séparateur entre deux caractère à lire. Pour cela, il suffit d’introduire un
espace entre les codes de format.
Exemple : scanf (« %c %c », &c1, &c2) ;
scanf (« %d %c », &N, &C) ;
13
14
CHAPITRE 4
Instructions et expressions
Une instruction est un ordre donné à l’ordinateur selon une syntaxe de programmation.
Une expression a souvent un type et une valeur qu’il faut savoir déterminer pour prévenir
ou corriger les erreurs de calcul ou de conversion de type.
Le symbole modulo n’est pas utilisable avec les valeurs de type Flottant / float.
Ordre de priorité :
* et / : la priorité se trouve dans l’ordre de placement des opérateurs
Ex : 10 * 2 / 4 = 5
20 / 4 * 2 = 10
+ et - : la priorité se trouve dans l’ordre de placement des opérateurs.
14
15
Ex : 10 + 2 - 4 = 8
14 - 2 + 1 = 13
10 + 5 * 2 = 20
15 – 5 * 2 = 5
20 – 8 / 2 = 16
11 + 6 / 3 = 13
Ces deux syntaxes ne présentent aucune différence si on les utilise seules en tant
qu’instruction. Ainsi, les instructions X++ ; et ++X ; sont équivalentes. Par contre, si on
utilise ces syntaxes en tant qu’expression à l’intérieur d’une autre instruction, on doit
prendre en compte la distinction suivante :
- ++ <variable> ou -- <variable> : la valeur de cette expression est celle de la variable après
l’incrémentation ou la décrémentation.
- <variable> ++ ou <variable> -- : la valeur de cette expression est celle de la variable avant
l’incrémentation ou la décrémentation.
Une expression simple est une opération ne contenant qu’un seul type de valeurs ou de
variables.
Exemple : l’instruction X = X * 2 ; où X est une variable de type int, est une expression
simple.
Par opposition, les expressions mixtes sont des opérations dans lesquelles on fait
intervenir des valeurs ou varaiables de type différent, en particulier int et float.
15
16
Il est important de tenir compte des conversions de type, car elles peuvent être à l’origine
d’une perte de données au niveau du résultat.
De façon générale, on parle du type d’une expression pour désigner le type du résultat
d’une opération.
- X+Y
- A*B
- X/Y
- X+A
- X+B*A
- X+Y*B
- X+5
- X + 2.3
- Y*3
- B/2
Une conversion de type a également lieue lors d’une affectation si la valeur affectée est de
type différent que la variable qui la reçoit. Là encore, il faut donc y être attentif, car cela
peut produire des effets inattendus si la conversion est faite involontairement.
Il est tout à fait possible de forcer la conversion d’une variable à l’intérieur d’une
expression de manière à éviter de perdre des données. Il existe un opérateur de conversion
forcée (appelé opérateur de « cast ») pour chaque type. En voici la syntaxe : <variable
d’accueil>= (<Type>) <expression ou variable> ;
Exemple : X= (float) Y / Z ; (on considère que les variables Y et Z sont de type int et X
de type float).
Par exemple, dans le programme suivant, la division est de type int et on risque donc
d’obtenir un résultat imprécis :
{
int n=5, p=9;
float x;
x = p / n;
printf ("%f\n",x); /* Ici, on obtiendra donc 1 comme résultat */
}
Pour éviter cette perte de données, on va forcer le type de l’expression en float en ajoutant
ce type entre parenthèses à l’intérieur de l’expression elle-même :
16
17
int n=5, p=9;
float x;
x = (float) p / n;
printf ("%f\n",x); /* Ici, on obtiendra 1.8 comme résultat */
}
Ce type correspond au type « car » utilisé en algorithmique pour les caractères. Les
valeurs constantes doivent impérativement être placées entre les symboles ‘ et ’ faute de
quoi elles seraient considérées par le compilateur comme des variables et non comme des
valeurs de type char.
Exemple : lorsqu’on écrit l’instruction « a = ‘e’; » , cela signifie que la variable a reçoit la
valeur « e », alors que l’instruction « a = e ; » signifie que la variable a reçoit la valeur de
la variable e.
4.6) Conversion int / char et char / int :
On peut remarquer que chaque lettre de l’alphabet possède une correspondance de type int.
Ainsi, il est tout à fait possible d’effectuer des opérations arithmétiques sur des valeurs ou des
variables de type char qui seront d’abord converties au type int.
17
18
Exercices
1. Ecrire un programme qui demande deux nombres entiers quelconques, puis qui affiche
leur somme.
2. Ecrire un programme qui lit au clavier deux nombres entiers et un nombre décimal et qui
affiche leur somme, puis leur produit.
4. Ecrire un programme qui demande deux nombres entiers et qui affiche, le premier après
son incrémentation unitaire et le second après sa décrémentation unitaire.
5. Ecrire un programme qui demande deux nombres entiers, qui incrémente le premier de
trois (3) et décrémente le second de cinq (5), puis qui affiche leur somme.
6. Ecrire un programme qui lit un nombre décimal et qui affiche uniquement la partie
entière.
7. Ecrire un programme qui lit au clavier deux valeurs de type caractère, qui affiche leurs
correspondances numériques, puis leur somme.
8. Ecrire un programme qui lit au clavier trois valeurs de type caractère, qui affiche leurs
correspondances numériques sous la forme décimale, avec deux chiffres après la virgule,
puis leur produit.
18
19
Chapitre 5
Structures conditionnelles
En c il existe deux sortes d’instructions : les instructions simples ( Exemples : affectation,
printf, scanf) et les instructions structurées( Exemples : if, for, while,…).
if (<Condition>)
{
bloc d’instructions 1
}
else
{
bloc d’instructions 2
}
Exemple :
if (x < y)
{
printf (« %d »,x) ;
}
else
{
printf (« %d »,y) ;
}
De plus, le bloc introduit par « else » peut tout à fait être ignoré, c’est-à-dire ce n’est pas
obligatoire d’avoir le bloc d’instruction « else »
Le bloc d’instruction « else » ne peut être utilisé que si le résultat d’un test a deux
valeurs.
Le bloc d’instruction « else » tient compte uniquement du « if » qui le précède.
La condition peut porter aussi bien sur des valeurs numériques que des caractères, à
condition de bien distinguer la signification des opérateurs de comparaison dans chaque
cas. Voici les opérateurs que l’on peut utiliser, avec leurs différentes significations :
19
20
== Egal identique
< strictement inférieur de code inférieur
> strictement supérieur de code supérieur
<= inférieur ou égal de code inférieur ou égal
>= supérieur ou égal de code supérieur ou égal
!= non égal différent
Tous ces opérateurs de comparaison sont moins prioritaires que les opérateurs
arithmétiques. Par conséquent, il est tout à fait possible de faire porter une condition sur
des expressions.
Exemple :
if (x / 3 == y + 2)
{
Bloc d’instruction
}
Remarque : il peut arriver qu’une comparaison utilisant l’opérateur « == » sur des valeurs
flottantes donne un résultat inattendu dû au fait que les flottants ne sont que des
approximations des nombres réels.
Autres syntaxes
if(condition)
Instruction1 ;
else
Instruction2 ;
if(condition)
{
bloc d’instructions1;
}
if(condition1)
{
bloc d’instructions1;
}
if(condition2)
{
bloc d’instructions 2;
}
if(condition n)
{
20
21
bloc d’instructions n;
}
if(condition)
{
bloc d’instructions 1;
}
else if
{
bloc d’instructions 2
}
else if
{
Bloc d’instructions 3;
}
else if
{
Bloc d’instructions n;
}
Conditions composées.
Encore comme en algorithmique, il est possible d’utiliser des opérateurs logiques pour
introduire plusieurs tests à l’intérieur d’une condition. Voici la notation de ces opérateurs :
Opérateur Signification
&& Et
|| ou (inclusif)
! Non
« && » et « || » sont moins prioritaires que les opérateurs de comparaisons, alors que « ! »
l’est plus.
21
22
Exercices
1- Ecrire un programme qui demande la moyenne d’un élève (sur 10) et qui affiche le
message « Admis » si sa moyenne est supérieure ou égale à cinq (5) ou « Echec » si sa
moyenne est inférieure à cinq (5)
2- Ecrire un programme qui lit au clavier un nombre entier quelconque et qui affiche le
message « le nombre est pair » si le nombre entré est pair ou « Le nombre est impair » s’il
est impair.
3- Ecrire un programme qui demande deux nombres entiers et qui affiche le plus grand des
deux s’ils sont différents f et un message indiquant s’ils sont égaux dans le cas contraire.
4- Même exercice que précédemment, mais en affichant le plus petit des deux.
5- Ecrire un programme qui demande deux nombres entiers et qui affiche leur différence.
(Eviter d’avoir un résultat négatif).
6- Ecrire un programme qui demande deux nombres entiers et qui affiche un message
indiquant si les deux nombres sont égaux ou différents.
7- Ecrire un programme qui demande deux nombres entiers, qui compare leur somme et leur
produit et qui affiche le résultat correspondant par les suivants :
- « La somme est supérieure au produit »
- « Le produit est supérieur à la somme ».
- « La somme est égale au produit ».
La condition doit être portée sur des expressions.
8- Ecrire un programme qui demande deux nombres entiers et qui affiche un message
indiquant si le plus grand des deux est pair ou impair.
10- Ecrire un programme qui demande deux nombres relatif à l’utilisateur, puis qui affiche un
message indiquant si le produit est positif, négatif ou nul.
11- Ecrivez un programme qui demande trois nombres entiers et qui affiche le plus grand des
trois.
12- Ecrivez un programme qui demande trois nombres entiers et qui affiche un message
indiquant si le plus grand des trois est pair ou impair.
22
23
Chapitre 6
Structures de répétition (les boucles)
Les structures de répétition appelées aussi boucles, sont considérées comme les structures les plus
puissantes de la programmation, elles consistent à exécuter à plusieurs reprises une suite
d’instructions. En langage C comme dans la plupart des autres langages de programmation, ces
répétitions sont de deux sortes :
Conditionnelle (ou indéfinies) ; la poursuite ou l’interruption de la répétition des instructions
concernées dépend d’une certaine condition ;
Inconditionnelles (ou avec compteur ou définies) : les instructions concernées sont répétées un
nombre donné de fois.
En langage C il existe deux instructions permettant de réaliser des répétitions conditionnelles
L’instruction do…while et l’instruction while.
L’instruction do…. while permet de réaliser une structure de répétition conditionnelle, dans
laquelle la décision de poursuite de la répétition est effectuée à la fin de l’exécution des
instructions concernées. En langage C il existe une autre instruction appelée while, analogue à la
précédente, dans laquelle la décision de poursuite de la répétition est effectuée avant l’exécution
des instructions concernées.
do
{
<Bloc d’instructions>
}
while (<Condition>) ;
while (<Condition>)
{
<Bloc d’instructions>
}
23
24
Comme nous l’avons dit l’instruction for permet de réaliser des répétitions inconditionnelles,
c’est-à-dire dont le nombre de tours est déterminé.
Exemple de boucle « for » utilisant comme compteur la variable comp de type int :
24
25
1. Ecrivez un programme qui affiche le message bonjour 10 fois.
3. Ecrivez un programme qui demande un nombre entier N à l’utilsateur, puis qui affiche le
message « Bonjour » N fois.
4. Ecrivez un programme qui demande deux nombres entiers X et Y, puis qui affiche le
nombre Y x fois.
5. Ecrivez un programme qui demande un nombre entier supérieur à 50, et qui le redemande
(s’il est incorrect) jusqu’à ce que l’utilisateur donne un nombre correct.
6. Ecrivez un programme qui demande un nombre entier quelconque, puis affiche les entiers
compris entre ce nombre et 100.
7. Ecrivez un programme qui demande un nombre entier quelconque, puis affiche les entiers
pairs compris entre ce nombre et 100.
8. Ecrivez un programme qui demande un nombre entier quelconque, puis affiche les entiers
impairs compris entre ce nombre et 100.
9. Ecrivez un programme qui affiche les carrés des nombres pairs compris entre 12 et 20.
25
26
Chapitre 7
Les tableaux
Introduction
Dans ce chapitre, nous allons essentiellement étudier les tableaux statiques à une dimension. Le
principe des tableaux en C est le même qu’en algorithmique, avec les mêmes notions de
dimension, de taille, de cellule et d’indice.
Cellule
C’est le plus petit élément constitutif d’un tableau
Dimension
C’est le nombre de cellules que contient un tableau.
Taille
C’est le nombre de cellules pleines dans un tableau
Indice
C’est un numéro qui identifie chaque cellulle d’un tableau.
1- Par l’initialisation
Syntaxe : <type> <nom> [<dimension>] = {<liste des valeurs séparées par des virgules>} ;
Exemple : int Tab [5] = {1,5,3,4,7}
N.B : L’initialisation peut ne remplir qu’une partie du tableau si on affecte moins de valeur qu’il
ne peut en contenir. Evidement, ce sont les premières cellules qui recevront ces valeurs.
2- Par affectation
Syntaxe : <nom du tableau> [<indice>] = <la valeur> ;
Exemple : tab[1]=9 ;
3- Par l’utilisateur
Syntaxe : scanf(« symbole du type du tableau », &<Nom du tableau> [<indice>]) ;
26
27
Exemple :
Exemple :
int Tab [10] = {1,2,3,4,5,6,7,8,9,10};
for (c = 0 ; c < 10 ; c++)
{
printf (« %d\n », Tab [c]) ;
}
27
28
Exercices
1. Ecrire un programme qui initialise un tableau d'entier de dimension 7 avec les valeurs
suivantes : 15, 23, 34, 12, 65, 34, 76 et qui affiche ensuite le contenu du tableau.
2. Ecrire un programme qui initialise un tableau de caractère de dimension 6 avec les valeurs
suivantes : d, e, q, t, y,s, puis qui affiche le contenu du tableau.
3. Ecrire un programme qui lit au clavier les valeurs d'un tableau de type flottant, de
dimension 5, et qui affiche ensuite les valeurs du tableau.
4. Ecrire un programme qui demande les valeurs d’un tableau d'entier de dimension 7, puis
qui affiche les valeurs de la 1ère, 3ème et dernière cellule.
5. Ecrire un programme qui demande les valeurs d’un tableau d'entier de dimension 8, puis
qui affiche les valeurs paires du tableau.
6. Même exercice que précédemment mais en affichant les valeurs impaires du tableau.
7. Ecrire un programme qui lit au clavier les valeurs d'un tableau d'entier de dimension 6 et
qui affiche ses valeurs dans l'ordre inverse, c'est-à-dire de la dernière cellule à la première.
8. Ecrivez un programme qui demande les valeurs d’un tableau d’entiers de dimension 5,
puis qui affiche la plus grande valeur du tableau.
9. Ecrivez un programme qui demande les valeurs d’un tableau d’entiers de dimension 5,
puis qui affiche la plus petite valeur du tableau.
10. Ecrivez un programme qui demande les valeurs d’un tableau d’entiers de dimension 6,
puis qui calcule et affiche la somme des valeurs du tableau.
11. Ecrire un programme qui lit au clavier les moyennes de 6 étudiants et qui affiche la
moyenne du meilleur étudiant.
12. Ecrire un programme qui demande en fin de session les notes finales de 8 étudiants. On
considère que les tous les étudiants qui étaient en reprise (c'est-à-dire qui avaient une note
comprise entre 59 et 65) ont réussi, c'est-à-dire qu'ils ont comme note finale 65. Donc il
faut modifier toutes les cellules qui ont une note comprise entre 59 et 65, en leur
attribuant la note 65.
28
29
Chapitre 8
Les fonctions
Introduction
Les fonctions en C reprennent le même principe qu’en algorithmique, mais il faut prendre
ici en compte l’organisation des fonctions par rapport aux programmes qui les appellent.
Il existe deux situations principales : l’appel d’une fonction prédéfinie et la définition
d’une fonction particulière en vue d’un appel ultérieur.
Les fonctions prédéfinies sont stockées dans des fichiers biliothèques qui possèdent
l’extension « .h ». L’appel d’une telle fonction nécessite donc que l’on sache à quelle
bibliothèque elle appartient et que l’on introduise cette bibliothèque au programme. Pour
cela, on utilise l’instruction « #include » au tout début du programme avec la syntaxe
suivante : #include < <chemin d’accès de la bibliothèque> >
Pour tout ce qui concerne la déclaration, la définition et l’utilisation (appel) d’une fonction, toutes
les notions suivantes sont à connaître et à maîtriser :
N.B :
On utilise le mot-clef « void » pour signaler l’absence de valeur de retour ou de
paramètre.
Exemples :
void Test (int x, int y)
int Test_2 (void)
29
30
8.2) Le corps de la fonction
L’ensemble du corps de la fonction doit être encadré par des accolades comme pour tout
bloc d’instructions.
Dans les deux cas, l’idée est la même : faire en sorte que le programme qui doit appeler une
fonction la connaisse ou ait au moins une référence à celle-ci.
la valeur de chaque paramètre doit être fournir dans l’ordre prévu dans la définition de la
fonction.
La valeur d’un paramètre peut être une varaiable, une constante, une expression ou le retour
d’une fonction.
8.6) Retour d’une valeur.
Une fonction peut en plus retourner une valeur du type défini dans son en-tête.
30
31
Contrairement à l’algorithmique, la valeur à retourner n’a pas forcément besoin d’être
placée entre parenthèses. Il peut s’agir là encore d’une constante, d’une variable ou de
n’importe quelle expression.
Par contre, le principe du retour est exactement le même qu’en algorithmique : lorsqu’on
exécute l’instruction return, on sort immédiatement de l’exécution de la fonction en cours,
ce qui empêche le retour simultané de plusieurs valeurs pour une même fonction.
Exemples
void somme (int x, int y)
{
printf(« La somme de x et y est : %d »,x+y) ;
}
int somme ()
{
int x, y ;
printf(« Donnez la valeur de x et y ») ;
scanf(« %d%d »,&x,&y) ;
return x+y ;
}
void somme ()
{
int x, y ;
printf(« Donnez la valeur de x et y ») ;
scanf(« %d%d »,&x,&y) ;
printf(« Leur somme est : %d »,x+y) ;
}
31
32
Lorsqu’on passe un tableau en paramètre, on le transmet sous la forme d’une adresse, ce qui
apporte comme avantage que le tableau n’a pas besoin d’être retourné : toute modification faite
sur le tableau à l’intérieur de la fonction appelée modifie le tableau qui a été fourni en paramètre
à cette fonction.
32
33
Exercices
1. Ecrire une fonction qui affiche le message bonjour.
NB : Placez la fonction avant et après la fonction principale.
2. Ecrire une fonction qui prend deux nombres entiers constants en paramètre, qui calcule et
retourne leur somme. Le résultat doit être affiché dans le main (ou la fonction principale).
3. Ecrire une fonction qui prend deux nombres décimaux en paramètre, qui calcule et retourne
leur somme. Le résultat doit être affiché dans le main.
NB : Les deux nombres doivent être fournis par l’utilisateur.
4. Ecrire une fonction qui prend deux nombres entiers quelconque, qui calcule et affiche leur
différence
NB : Les deux nombres doivent être fournis par l’utilisateur.
5. Ecrire une fonction qui prend en paramètre le taux par heure et le nombre d’heure de travail
d’un employé par mois, puis qui calcule et affiche :
Son salaire mensuel.
Son économie annuelle, sachant que l’employé dépense 40 % de son salaire.
Son salaire annuel
Son économie annuelle
6. Même exercice que précédemment, mais en affichant la dépense annuelle dans la fonction
principale.
7. Ecrire une fonction qui demande le prix d’un crédit et le nombre de cours choisi par
étudiant pour une session, puis qui calcule et affiche le montant à verser par l’étudiant dans
la fonction principale.
8. Ecrire une fonction qui demande le prix unitaire et la quantité vendue d’un produit, puis qui
calcule et affiche le prix de vente total.
Tableaux
9. Ecrire une fonction qui calcule et retourne la somme des valeurs d’un tableau de type
entier, reçu en paramètre. Le résultat doit être affiché dans la fonction principale.
NB : les valeurs du tableau doivent être fournies par l’utilisateur.
10. Ecrire un programme se servant d’une fonction pour incrémenter de 2, la valeur de chaque
cellule d’un tableau d’entier de dimension 5. Afficher les valeurs du tableau après
l’incrémentation dans la fonction principale.
NB : les valeurs du tableau doivent être fournies par l’utilisateur.
33
34
Chapitre 9
Notions de variable locale et globale.
9.1) Variable locale
Une variable « locale » est une variable déclarée à l’intérieur d’une fonction. Sa valeur ne
peut être considérée que lors de l’appel de la fonction et elle est indisponible pour tout autre
fonction, y compris pour le main. De plus elle perd sa valeur dès que l’on sort de la fonction et
elle ne peut en recevoir une qu’au moment de l’appel de la fonction.
Du fait de leur disponibilité très large, les variables globales risquent souvent de créer des
conflits. C’est pour cette raison qu’il faut limiter au maximum l’utilisation de ces variables et
privilégier l’utilisation des paramètres et des retour de fonction pour échanger des valeurs entre
les fonctions.
De plus, une variable globale peut être cachée : cela se produit lorsqu’une variable locale
d’une fonction possède le même nom qu’une variable globale. Dans ce cas, la variable globale
n’est plus accessible pour cette fonction.
34
35
Chapitre 10
Les erreurs
Lors de l’écriture d’un programme, il y a de fortes chances pour qu’on rencontre des
erreurs de différents types que l’on devra corriger pour que le programme fonctionne
correctement. Ces corrections supposent que l’on soit capable de distinguer les différents types
d’erreur et de diagnostiquer chacune d’entre elle (c’est-à-dire de repérer en quoi elles consistent
et où est-ce qu’elles se situent dans le programme).
Ce type d’erreur apparaît lors de l’exécution, mais pas sous la forme d’un message d’erreur. Elle
se traduit par l’apparition d’un résultat incohérent par rapport aux données sur lesquelles on a
exécuté le programme. C’est pour cette raison qu’il faut toujours tester un programme dans toutes
les situations possibles (en particulier sur les cas particuliers) pour vérifier s’il ne contiendrait pas
ce genre d’erreur.
Les cas les plus courants : se tromper de variable lors d’un l’afichage, se tromper de code de
format, se tromper dans l’utilisation des opérateurs ( +,-, *, /, ( = et = = dans les conditions)), etc.
35
36
Les cas les plus courants sont les suivants : omission du symbole « & » dans un « scanf »,
tentative d’accès à une cellule d’indice supérieur à la dimension d’un tableau et tentative d’accès
à une donnée d’un pointeur qui ne pointe sur rien ou sur NULL.
36
37
Chapitre 11
Les chaînes de caractères
11.1- Préambule
Une chaîne de caractères est une suite de caractères quelconques. Jusqu’ici nous avons
utilisé de telles chaînes dans les formats des instructions scanf et printf, qui permettent de lire ou
d’écrire un seul caractère.
Le langage C, quant à lui, ne comporte pas de véritable chaîne ; certes, avec ce que nous
avons étudié jusqu’ici, il est possible de placer une suite de caractères dans un tableau de
caractères (éléments de type char). Cependant, quelques contraintes apparaissent alors :
Nous ne savons pas comment gérer des chaînes dont la longueur serait différente de la
dimension du tableau.
La lecture des caractères de notre tableau devraient se faire caractère par caractère et non
globalement.
En fait, le langage C, vous offre quelques outils facilitant la manipulation de telles chaînes,
pour peu qu’on respecte une convention particulière pour leur représentation, à savoir
« marquer » leur fin d’un caractère spécial de code 0. Dans ce cas, en effet, vous pourrez :
lire ou écrire globalement une chaîne, en faisant appel, soit aux fonctions printf ou scanf
et à un code de format approprié, soit à de nouvelles fonctions spécifiques (gets et puts).
réaliser des traitements propres aux chaînes, tels que les comparaisons, la recopie, la
concaténation, en faisant appel à des fonctions prévues à cet effet.
va lire une suite de caractères au clavier pour les ranger dans le tableau nom, en
commençant à partir de nom[0] et en ajoutant automatiquement, à la suite, un caractère de fin de
chaîne (caractère de code nul).
37
38
Le code de format %s fonctionne comme les codes de format numériques et non comme
le code de format %c. Autrement dit, il commence par sauter les délimiteurs éventuels (espace ou
fin de ligne) et il s’interrompt à la rencontre de l’un de ces délimiteurs ; il ne permet pas donc de
lire des chaînes contenant des espaces. Nous verrons que, pour y parvenir, il faut utiliser une
fonction spécialisée nommée « gets ».
De même l’instruction :
printf(« %s »,nom) ; ou printf(« %s »,&nom[0]) ;
va afficher à l’écran, les caractères trouves dans le tableau nom, en commençant par
nom[0] et en s’interrompant à la rencontre d’un caractère de fin de chaîne.
Lorsque vous lisez une chaîne de n caractères, il faut prévoir un emplacement d’au moins
n+1 caractères, compte tenu du caractère supplémentaire de fin de chaîne.
Une chaîne peut bien ne contenir aucun caractère (autre que celui de fin) ; on parle de
« chaîne vide ».
Ne confondez pas le caractère de fin de chaîne, qui est introduit automatiquement par
scanf pour indiquer, en mémoire, la fin d’une chaîne, avec le délimiteur (espace ou fin de ligne)
qui a servi à détecter la fin de donnée fournie par l’utilisateur ; ce délimiteur n’étant d’ailleurs pas
recopié en mémoire.
Il existe deux fonctions manipulant exclusivement des chaînes (une seule à la fois), qui
complètent utilement les possibilités du code de format %s.
Lit une suite de caractères en la rangeant dans le tableau nom, terminée par un caractère
de fin de chaîne ; cette fois :
Aucun délimiteur n’est sauté avant la lecture.
Les espaces sont lues comme les autres caractères
La lecture se termine à la rencontre d’un caractère de fin de ligne.
De même, l’instruction :
puts(nom) ;
38
39
Cette fonction (Abréviation de STRing CoMParison, en français « comparaison de
chaînes » et dont le prototype figure, cette fois, dans string.h) permet de comparer deux chaines
de caractères et fournit en résultat une valeur entière qui peut être :
Exemple : x=strcmp(mot1,mot2) ;
Avec x une variable de type int et mot1, mot2 deux tableaux de caractères.
Cette fonction permet de recopier globalement (d’un coup) une chaîne de caractères dans
une autre. (Abréviation de STRing CoPY, en français « copie de chaîne »).
Comme on peut s’y attendre, il n’est possible d’affecter directement une chaîne à une
autre, pas plus qu’il n’était possible d’affecter globalement le contenu d’un tableau à un autre.
Il est nécessaire que la taille du tableau réservé pour la chaîne « destination » soit
suffisante pour accueillir la chaîne à recopier.
La fonction strcpy recopie tous les caractères qu’elle trouve, jusqu’à la rencontre d’un
caractère de fin de chaîne, et ceci quel que soit le nombre de caractère en question. Il existe une
autre fonction, nommée strncpy, analogue à strcpy qui permet, grâce à un paramètre
supplémentaire, de limiter le nombre de caractères pris en compte.
Syntaxe : strlen(chaîne) ;
x=strlen(mot) ;
Considérant x comme une variable de type int et mot comme une chaîne de caractères.
39
40
Dans tous les langages disposant d’un type chaîne, on nomme concaténation l’opération
qui consiste à fabriquer une chaîne en mettant bout à bout deux autres chaînes. Par exemple, le
résultat de la concaténation de « mon » et « sieur » serait « monsieur ».
La fonction strcat concatène à la chaîne destination tous les caractères qu’elle trouve dans
la chaîne source, et ceci quelle que soit la longueur de cette dernière. Il existe une fonction,
nommée strncat, analogue à strcat qui permet, grâce à un paramètre supplémentaire, de limiter le
nombre de caractères concaténés.
40
41
Exercices
1- Ecrivez un programme qui demande les informations d’un employé comme : son nom, son
prénom, sa fonction et son salaire, puis qui les affiche.
3- Ecrivez un programme qui demande le numéro de compte d’un client (d’une banque), son
nom, son prénom et son solde, qui les stocke puis qui les affiches.
4- Ecrivez un programme qui demande deux chaînes de caractère, qui les compare puis qui
affiche l’un des sages suivants :
Les deux chaînes sont identiques
Les deux chaînes sont différentes
5- Ecrivez un programme qui demande deux chaînes de caractères, qui les compare puis qui
affiche l’un des messages suivants :
:
Les deux chaînes de caractères sont identiques
La 1ere chaîne se trouve avant la 2eme, au sens de l’ordre défini par le code des
caractères
La 1ere se trouve après la 2eme, au sens de l’ordre défini par le code des caractères.
6- Ecrivez un programme qui demande deux chaînes de caractères, qui échange les valeurs des
tableaux contenant ces chaînes, puis qui les affiche.
7-Reprenez l’exercice trois (3), mais en utilisant à chaque lecture des chaînes de caractères le
tableau X (char x [20], puis le programme doit transmettre le contenu du tableau X dans un autre
tableau avant la lecture d’une prochaine chaîne de caractères.
8- Ecrivez un programme qui demande le nom et le prénom d’une personne, qui crée une adresse
électronique (yahoo) pour cette personne avec son nom, suivi de son prénom, puis qui affiche
l’adresse électronique.
Exemple : Nom : bateau Prénom : denis adresse électronique : bateaudenis@[Link]
9-Ecrivez un programme qui demande le nom et le prénom d’une personne, qui crée une adresse
électronique (yahoo) pour cette personne constituée des trois premiers caractères du nom et des
deux premiers caractères du prénom, puis qui affiche l’adresse électronique.
Exemple : Nom : bateau Prénom : denis adresse électronique : batde@[Link]
9- Ecrivez un programme qui demande une chaîne de caractères, puis qui affiche le nombre de
caractères de la chaîne.
10- Ecrivez un programme qui demande une chaîne de caractère, qui copie les trois (3) premiers
caractères dans un tableau Y ( char y [4]), puis qui affiche le tableau Y.
41
42
Chapitre 12
Les structures
12.1- Préambule
Une structure permet de designer sous un seul nom un ensemble de valeurs pouvant être de types
différents.
La structure est constituée d’un ensemble d’éléments appelés champ. Comme une variable
scalaire, chaque champ a un type et un nom.
L’accès à chaque élément (nommé champ) de la structure se fait, cette fois, non plus par une
identification de position, mais par son nom au sein de la structure.
Exemple :
struct article
{
int numero ;
int qte ;
float prix ;
};
Celle-ci définit un modèle de structure, mais ne réserve pas de variable correspondant à cette
structure. Une fois un modèle défini, nous pouvons déclarer des variables du type correspondant.
Bien que ce soit peu recommandé, sachez qu’il est possible de regrouper la définition du modèle
de structure et la déclaration des variables dans une seule instructions, comme dans cet exemple :
42
43
struct article
{
int numero;
int qte;
float prix;
} art1, art2, x ;
Dans ce dernier cas, il est même possible d’omettre le nom de modèle (article), à condition, bien
sûr, que l’on n’ait pas à déclarer par la suite d’autres variables de ce type.
[Link] = 15 ;
printf(« %f »,[Link]) ;
scanf(« %f »,&[Link]) ;
[Link] = [Link]+1 ;
43
44
La description des différents champs se présente sous la forme d’une liste de valeurs séparées par
des virgules, chaque valeur étant une constante (ou une expression constante) ayant le type du
champ correspondant. La encore il possible d’omettre certaines valeurs ; ces derniers seront
indéterminés, sauf dans le cas d’une structure définie en variable globale (auquel cas ces valeurs
seront placées à zéro).
Voici un exemple d’un modèle de structure nommé article déclaré de façon globale et accessible
depuis les fonctions main et calculmoy.
# include <stdio.h>
# include <conio.h>
# include <string.h>
struct article
{
int numero;
int qte;
float prix;
}
main( )
{
struct x;
…
}
void calculmoy ( )
{
struct article y, z;
…
}
Nous verrons que cette possibilité se révèle indispensable pour les structures transmises en
argument d’une fonction.
44
45
13.7- Imbrication de structures
Dans nos exemples d’introduction de structures, nous nous sommes limités à une structure simple
ne comportant que des trois champs d’un type de base. Mais chacun des champs d’une structure
peut être d’un type absolument quelconque : pointeur, tableau, structure … de même, un tableau
peut être constitué d’éléments qui sont eux mêmes des structures. Voyons ici quelques situations
classiques.
struct personne
{
Char nom[30] ;
Char prenom[20] ;
Float heures[31] ;
};
main( )
{
struct article
{
int numero ;
int qte ;
float prix ;
};
tab[0].numero = 20 ;
tab[0].prix = 20.66 ;
}
45
46
Supposons qu’à l’intérieur d’une structure, nous ayons besoin d’introduire deux dates (la date de
naissance et la date d’embauche d’un employé), et si ces dates sont elles-mêmes des structures
comportant trois champs correspondant au jour, au mois et à l’année, nous pouvons alors
procéder aux déclarations suivantes :
struct date
{
int jour ;
int mois ;
int annee ;
};
struct employe
{
char nom[20] ;
char prenom[20] ;
struct date datenaissance ;
struct date dateembauche ;
};
46
47
Exercices
N.B : Créez une structure pour chacun des exercices suivants, et utilisez une seule variable (de la
structure définie) pour stocker les valeurs qui seront fournies par l’utilisateur.
1. Ecrivez un programme qui demande le numéro d’un article, son prix unitaire et la quantité
en stock, puis qui les affiche.
2. Ecrivez un programme qui demande la date de naissance d’une personne (Jour, mois et
année), puis qui affiche la date.
3. Ecrivez un programme qui demande deux dates puis affiche le nombre d’années de
différence entre ces deux dates.
4. Ecrivez un programme qui demande le numéro d’un étudiant, puis une note pour chacun
des logiciels suivants : Windows, Word, Excel et Access, qui ajoute 5 sur chaque note,
puis qui affiche le code et les notes (après l’augmentation), puis la note totale.
5. Ecrivez un programme qui demande le numéro d’un employé, son taux horaire, son
nombre d’heures mensuel, puis qui affiche son salaire annuel, sa dépense annuelle et
économie annuelle, sachant qu’il dépense 30% de son salaire.
6. Ecrivez un programme qui demande le numéro d’un employé, son salaire mensuel, son
âge et son nombre d’enfants, puis qui affiche son nouveau salaire, sachant qu’on lui
donne une augmentation, selon les conditions suivantes:
7. Ecrivez un programme qui demande le code d’un employé, son nom, son prénom, sa
fonction et son salaire, puis qui les affiche.
8. Ecrivez un programme qui demande l’adresse d’une personne c’est-à-dire le pays, la ville,
le nom de la rue et le numéro de l’appartement, puis qui affiche l’adresse de la façon
suivante : Numéro, rue, ville, pays
Exemple : 8, Rue du quai, Cayes, Haïti.
9. Ecrivez un programme qui demande le code d’un article, son nom, son prix d’achat
unitaire et la quantité en stock, puis qui affiche le code et le prix d’achat total de l’article.
10. Même exercice que le précédent, mais qui affiche le prix de vente total et unitaire de
l’article, sachant que le vendeur veut gagner 10% de bénéfice.
11. Ecrivez un programme qui demande les caractéristiques d’un ordinateur c’est-à-dire la
marque, le modèle, la vitesse du processeur (en GHz), la capacité de la mémoire vive (en
Megabyte), la capacité du disque dur (en gigabyte), puis qui les affiche.
47
48
12. Reprenez l’exercice 1 mais en demandant un deuxième code, et qui affiche les
informations de l’employé si les codes sont identiques, ou un message indiquant que les
codes sont différents.
13. Ecrivez un programme qui demande le code d’un étudiant, son nom, son prénom, sa date
de naissance, sa date d’inscription et son adresse, puis qui les affiche.
14. Ecrivez un programme qui demande le nom d’un employé, son prénom, sa fonction, son
salaire, sa date d’embauche, son adresse, son numéro de tel et son NIF, puis qui les
affiche.
15. Ecrivez un programme qui permet d’enregistrer les informations suivantes concernant un
étudiant en bureautique à BTI,
Le code de l’étudiant, son nom, son prénom, son sexe, sa date de naissance, sa date
d’inscription, son adresse et ses notes (Windows, Word, Excel, Access).
16. Ecrivez un programme qui demande le code, le nom, le prénom et le nombre d’heures de
travail d’un employé, puis qui affiche le code, le nom, le prénom et le salaire mensuel de
l’employé, sachant qu’on lui paie 60 dollars par heure.
N.B : Le nombre d’heures doit être fourni par semaine. (Pour un mois)
48
49
Chapitre 13
Pointeur
13.1- Préambule
Toute variable quelle qu’elle soit, possède une adresse mémoire qui définit l’emplacement
qui lui a été alloué dans la mémoire de l’ordinateur lors de sa déclaration. A partir de cette
information, on peut définir le type « pointeur » : les variables de type pointeur (ou pointeur) sont
prévues pour recevoir comme valeur une adresse mémoire, c’est-à-dire généralement l’adresse
d’une autre variable (les pointeurs peuvent contenir leur propre adresse, mais cela ne représente
aucun intérêt). Lorsqu’un pointeur possède l’adresse d’une variable, on dit qu’il « pointe » sur
cette variable.
Exemples :
Déclaration d’un pointeur sur int : int * x ;
Déclaration d’un pointeur sur char : char * x ;
Déclaration d’un pointeur sur float : float * x ;
Attention, lorsqu’on déclare plusieurs pointeurs sur une même ligne (si ce sont des
pointeurs sur un même type), il faut mettre l’opérateur « * » à chaque pointeur.
Comme pour les autres variables, un pointeur non initialisé possède une valeur aléatoire et
donc imprévisible. D’autre part, il est très rare qu’on affecte à un pointeur une adresse sous la
forme d’une valeur constante puisqu’il est impossible de déterminer de façon sûre ce qui se
trouve à une adresse mémoire donnée.
Pour donner une valeur à un pointeur, on va donc généralement lui affecter soit l’adresse
d’une autre variable, soit la valeur contenue dans un autre pointeur.
Exemple : soit une variable x de type int, son adresse est : &x. On peut donc l’affecter au
pointeur sur int P de la façon suivante : P = &x ;
49
50
Quant aux échanges d’adresses entre pointeurs (sur même type), ils se font de la même
manière qu’entre n’importe quelles variables puisqu’il s’agit finalement d’un simple échange de
valeur.
Exemple : soit un pointeur sur int P, la valeur située en mémoire à l’adresse contenue dans P est :
*P. On peut donc l’utiliser ainsi comme une valeur désignée de n’importe quelle autre façon.
Exemples : x = *P + 10 ;
*P = 10 ;
printf(« %d », *P) ;
Remarque importante : lorsqu’on déclare un pointeur, on déclare une variable qui contiendra une
adresse mémoire et qui en contient actuellement une aléatoire. Par conséquent, si on affecte une
valeur à l’emplacement pointé par un pointeur sans avoir préalablement affecté une adresse à ce
pointeur, la valeur en question va donc être placée en mémoire à un endroit inconnu. Cette
anomalie n’est généralement pas détectée par le compilateur et, selon les circonstances, peut
aboutir à différents problèmes :
- dans le meilleur des cas, l’adresse en question n’est pas accessible au programme et une
simple erreur de mémoire bloque l’exécution.
- dans les autres cas, la valeur ainsi placée aléatoirement dans la mémoire va écraser une
autre valeur, entrainant des conséquences imprévisibles et plus ou moins désastreuses.
n 20 n 20 n 20 n 30 n 30
p ? p ? p 20 p 20 p 20
50
51
On ne peut pas attribuer une signification unique à une notion telle que *adi ; en effet :
Dans une expression, la notation *adi désigne la valeur de l’information pointée par adi ; c’est le
cas dans l’affectation p =*adi ;
A gauche d’une affectation, la notation *adi désigne l’information pointée par adi ; c’est le cas
dans l’affectation *adi=30 ;.
En revanche, il y a bien équivalence entre *adi et une variable entière, y compris au niveau de la
mise en place d’éventuelles conversions. Ainsi notamment, avec : *adi=4.6 ;
Il y a bien conversion de la valeur flottante 4.6 en entier, et le résultat (4) est affecté à adi . De
même, si x, est supposé de type float avec :
X=* adi ;
Il y a conversion en float de la valeur entière pointée par adi avant l’affectation à la variable x.
La priorité de l’opérateur unaire * est plus élevée que celle des opérateurs arithmétiques, de sorte
que, par exemple, l’expression :
2 * * adi
est interprétée comme :2 * (* adi)
C’est-à-dire comme le produit de * adi par 2. Pour rendre une telle expression plus lisible, en
général, plutôt que de placer des parenthèses inutiles comme dans la deuxième écriture, on joue
sur la présence ou l’absence d’espace supplémentaire, en l’écrivant ainsi (pour bien montrer que
les ecritures 2**adi, 2 **adi ou 2** adi sont correctes mais relativement illisibles.
Exemple :
En-tête : int test (int *x, int *y)
Prototype : int test (int *, int *) ;
D’autre part, lorsqu’on donne à une fonction des paramètres de type pointeur, cela signifie
que la valeur à donner à chacun d’entre eux au moment de l’appel de la fonction sera donc
l’adresse d’une variable de type correspondant. Ce genre de pratique comporte un avantage
important, car on travaille ainsi sur l’adresse d’une variable et non directement sur sa valeur, et
grâce à cette adresse, on va pouvoir accéder à la valeur et même la modifier. Autrement dit, dans
une fonction dont certains paramètres sont des pointeurs, lorsqu’on va modifier les valeurs
pointées par ces pointeurs, on retrouvera ces modifications dans la fonction (ou le main) qui a
effectué l’appel.
51
52
L’exemple du programme suivant permet de comprendre ce mécanisme : on va appeler
dans le main la fonction Echange dont le but est de prendre les adresses de deux variables de type
int et d’inverser leur valeur.
#include <stdio.h>
main()
{
void echange (int *, int *) ;
int a = 10, b = 20 ;
printf(« avant appel : a = %d b = %d\n », a, b) ;
echange (&a, &b) ;
printf(« apres appel : a = %d b = %d », a, b) ;
}
x = *P1 ;
*P1 = *P2 ;
*P2 = x ;
}
Avec cet exemple, on observe un important bénéfice apporté par l’utilisation des pointeurs
en tant que paramètres : comme on peut modifier depuis une fonction appelée la valeur des
variables d’une fonction appelante, on peut ainsi simuler le retour de plusieurs valeurs, alors
qu’on ne peut normalement retourner qu’une seule valeur à l’issue d’une fonction.
Pour désigner un champ d'une variable structurelle dont un pointeur contient l'adresse, on
utilise l'opérateur « -> » composé des symboles « - » et « > ».
Exemple :
52
53
int Mois ;
int Annee ;
};
main()
{
struct Date *P, D; /* On déclare une date D et un pointeur sur date P */
Alors que nous n’avons affectée aucune valeur à adi, nous allons donc demander de placer la
valeur 12 à un emplacement quelconque. Généralement, ce genre d’anomalie n’est pas détecté
par le compilateur et ça peut engendrer des conséquences imprévisibles et même désastreuses
(écrasement d’une autre donnée, destruction d’une instruction…).
53
54
Exercices
1. Ecrivez un programme qui se sert d’une fonction qui permet l’échange des valeurs de
deux variables.
N.B : Les valeurs des variables doivent être fournies par l’utilisateur et elles doivent être
affichées avant et après l’appel de la fonction, dans la fonction principale.
2. Ecrivez une fonction qui demande trois nombres entiers, qui affiche ces nombres dans la
fonction principale.
N.B : L’utilisation des tableaux est interdite.
3. Ecrivez un programme qui demande deux nombres entiers, puis utilisez une fonction (qui
ne retourne aucune valeur) qui permet d’incrémenter le premier de 5 et décrémenter le
second de 3.
N.B : - N’utilisez pas de tableau
-N’oubliez pas d’afficher les valeurs dans la fonction principale, après l’appel
de la fonction.
Dimension du tableau : 6
La plus grande et la plus petite valeur doivent être affichées dans la fonction principale.
54
55
Chapitre 14
Les tableaux dynamiques.
14.1- Préambule
Les programmes utilisant des tableaux dynamiques nécessitent avant tout de créer au
moins une structure pour définir le type des éléments qui formeront les tableaux dynamiques. Il
est tout-à-fait possible de créer plusieurs types d’éléments et de manipuler des tableaux
dynamiques contenant ainsi des éléments différents, mais il est préférable de commencer par des
tableaux simples à partir d’une seule structure. Généralement, une telle structure contient au
moins un champ de type « classique » (c’est-à-dire un champ qui contiendra une valeur
numérique ou un caractère) et au moins un pointeur sur l’élément suivant. Pour cette étude des
tableaux dynamiques, nous allons utiliser la structure suivante :
struct elem
{
int val ; /* Unique valeur de l’élément */
elem *suiv ; /* Pointeur sur l’élément suivant */
};
Exemple de création d’un élément de type struct elem : malloc(sizeof (struct elem))
La fonction malloc retourne une adresse que l’on dit « générique », c’est-à-dire qu’elle
peut être récupérée par un « pointeur sur n’importe quel type ». Il est donc préférable d’imposer à
ce retour le type du pointeur qui va recevoir cette adresse. Pour cela, il suffit d’indiquer entre
parenthèses devant l’appel de la fonction malloc le type qu’on souhaite lui imposer.
55
56
void Crea_liste (struct elem *l) /* on prend en paramètre un pointeur sur le premier élément
(considéré comme déjà
{ existant) à partir duquel on va créer le reste de la liste */
int x, c;
if (x != 0)
{
for (c=1; c<=x-1; c++) /* On va créer x – 1 éléments, car le premier existe
déjà */
{
printf ("Valeur de l'element %d ?\n",c); /* On commence par demander
la valeur de
scanf ("%d", &l->val); l’élément, car on considère que
le premier, même
s’il existe déjà, n’a pas encore reçu sa
valeur */
l->suiv = (struct elem*) malloc (sizeof (struct elem)); /* Création d’un
nouvel élément à la suite
de l’élément actuel */
l = l->suiv ; /* Positionnement de l sur le dernier élément créé */
}
56
57
D’autre part, lorsqu’on utilise des éléments qui ne contiennent qu’un pointeur sur
l’élément suivant (c’est-à-dire pour une liste unidirectionnelle), il est impossible de « revenir en
arrière » pendant un parcours de tableau. C’est pourquoi il faut parfois prévoir plusieurs pointeurs
répartis sur plusieurs éléments qui se suivent lorsqu’on doit avoir accès en même temps à un
élément et à celui ou ceux qui le précèdent. L’autre solution est de travailler sur une liste
bidirectionnelle plutôt que sur une liste unidirectionnelle.
Exemples :
- dans le cas d’une fonction qui se contente d’ajouter un élément à la fin d’un tableau, il
n’est pas nécessaire de retourner le tableau à la fin puisque le premier élément n’a pas
changé.
- dans le cas d’une fonction qui effectue le tri d’un tableau dynamique, il est possible que le
premier élément change de place selon le tri que l’on va effectuer, et il faut donc prévoir
de retourner à l’issue de la fonction, l’élément qui sera finalement en première place.
Dans ce cas, le retour final du premier élément nécessite de prévoir un pointeur qui aura
pour rôle de pointer constamment sur le premier élément, et qui ne changera donc de cible
que lorsqu’un autre élément deviendra premier.
57
58
Chapitre 15
Pour obtenir de l’aide sur la lecture et l’écriture dans les fichiers en C, chercher "file
handling" dans l'aide de Dev-C++.
On appel « stream » en C une interface logique liée à un fichier dans le but de le contrôler
selon des besoins précis. Le langage C distingue deux types de « stream » : le « stream texte »
utilisant les codes ASCII des caractères et le « stream binaire » permettant un transfert des
informations bit par bit entre le fichier et la mémoire.
FILE est un type défini dans stdio.h et dont l’objectif est de contenir diverses informations
au sujet d’un fichier. On voit donc que fopen retourne un « pointeur sur FILE » qui servira de
référence pour l’exploitation du fichier ouvert. Ce pointeur servira dans toutes les autres
fonctions de manipulation des fichiers, c’est pourquoi il ne doit pas changer de valeur tant qu’on
manipule le même fichier.
58
59
Le pointeur *fname représente une chaîne contenant l’adresse de fichier que l’on souhaite
ouvrir. Il peut s’agir de l’adresse absolue du fichier ou de son adresse relative par rapport au
programme lui-même. Attention, si jamais le fichier à ouvrir est déplacé après que ce genre
d’instruction ait été écrite, le programme ne parviendra plus à l’ouvrir si l’adresse indiquée n’est
plus la bonne.
Quant au pointeur *mode, il représente une chaîne déterminant de quelle façon on va
ouvrir le fichier. Voici la liste des différents modes d’ouverture :
Mode Signification
Pour fermer un fichier et le désassocier du stream auquel on l’avait attaché avec fopen(),
on utilise la fonction fclose() également définie dans « stdio.h » et dont voici l’en-tête :
Le pointeur *fp doit pointer sur un fichier précédemment ouvert avec fopen(), et la valeur
retournée sera 0 si l’exécution de fclose() s’est bien passée.
59
60
Une fois qu’un fichier a été ouvert, les fonctions suivantes permettent de lire les données
qu’il contient ou de le modifier selon le mode d’ouverture choisi. Pour comprendre le
fonctionnement de ces fonctions, on utilisera le concept de « curseur » : on appelle curseur la
position sur laquelle on se trouve dans un fichier à un instant donné. Quand on vient d’ouvrir un
fichier en lecture ou en écriture, le curseur se trouve sur le premier caractère du fichier. Quand on
vient d’ouvrir un fichier en ajout, le curseur se trouve après le dernier caractère du fichier. A
chaque fois qu’on lit un caractère ou une chaîne de caractères, le curseur se positionne sur le
caractère suivant.
int fgetc (FILE *fp) : permet de lire un caractère dans un fichier texte ouvert en lecture. Le
pointeur fp correspond au fichier à ouvrir, et le caractère lu obtenu par le retour de fgetc() peut
être interprété aussi bien comme un caractère que comme un nombre. Si une erreur se produit ou
si on essaie de lire un caractère alors qu’on a déjà atteint la fin d’un fichier, la valeur retournée
sera EOF, qui est une valeur spéciale indiquant la fin d’un fichier.
Note : lorsqu’on lit une donnée dans un fichier, le curseur avance automatiquement à la donnée
suivante. Il est donc impossible de lire plusieurs fois de suite la même donnée sans revenir au
début du fichier (cf. fonction rewind()).
int fputc (int ch, FILE *fp) : permet d’écrire le caractère contenu dans ch dans le fichier pointé
par fp ouvert en écriture ou en ajout. En cas de succès, cette fonction retourne le caractère écrit,
sinon, elle retourne EOF. Ici encore, même si ch est de type int, on peut lui fournir aussi bien un
caractère qu’un nombre, et il en va de même pour le retour.
Les deux derniers caractères de la chaîne ainsi lue sont systématiquement le saut de ligne
et NULL. Cette fonction retourne str en cas de succès et NULL en cas d’échec, mais compte tenu
du fait que str est un pointeur, il n’est généralement pas nécessaire de se préoccuper du retour.
Exemple : fgets (ph, 10, f) ; /* La chaîne ph reçoit les 10 premiers caractères de du fichier
associé au pointeur f à
60
61
partir du curseur */
int fputs (char *str, FILE *fp) : permet d’écrire la chaîne pointée par str dans le fichier pointé par
fp et ouvert en écriture ou en ajout. En cas de succès, fputs retourne une valeur positive ou nulle.
En cas d’échec elle retourne EOF.
Dans certains cas, on peut souhaiter lire dans un fichier un caractère que l’on utilisera en
tant que valeur numérique. De même, on peut souhaiter écrire dans un fichier une chaîne de
caractères comportant des valeurs de variable. Pour réaliser ce type d’opérations, on dispose de
fonctions semblables aux printf et scanf, et qui fonctionnent de façon similaire. Les différences
entre ces nouvelles fonctions et les fameux printf et scanf sont qu’il faut ici préciser dans quel
fichier on souhaite écrire (grâce qu pointeur fp) et que l’on récupère le résultat de l’opération
sous la forme d’une valeur numérique (on obtient une valeur positive ou nulle en cas de succès et
EOF en cas d’échec).
int fscanf (FILE *fp, char *control-string, ...) : permet de lire un ou plusieurs caractères dans un
fichier ouvert en lecture et de les interpréter selon les besoins.
int fprintf (FILE *fp, char *control-string, ...) : permet d’écrire dans un fichier ouvert en écriture
ou en ajout un ou plusieurs caractères fournis sous n’importe quelle forme.
Dans certains cas, il peut être utile de vérifier si on a atteint la fin du fichier ouvert ou pas.
Pour cela, on utilise la fonction suivante aussi bien avec les fichiers ouvert en mode texte qu’en
mode binaire.
int feof (FILE *fp) : retourne une valeur non nulle si on a atteint la fin du fichier, et 0 dans les
autres cas.
Lorsqu’on souhaite supprimer un fichier, il faut d’abord s’assurer que celui-ci n’est pas
actuellement ouvert par le programme qui doit le supprimer ou par une autre application, faute de
quoi la suppression sera impossible. D’autre part, il faut avoir conscience que la fonction utilisée
pour ce genre d’opération n’utilise évidemment pas le protocole de la corbeille de Windows et
que par conséquent, tout fichier supprimer par un programme l’est définitivement.
61
62
int remove (char *fname) : la chaîne de caractères fname doit contenir l’adresse du fichier à
supprimer. En cas d’erreur (mauvaise adresse, fichier en cours d’utilisation, etc…), cette fonction
retourne une valeur négative et le fichier n’est pas supprimé. En cas de succès, cette fonction
retourne 0.
main()
{
int x;
chdir("c:\\Exercices");
x=remove("[Link]");
getch();
}
void rewind (FILE *fp) : le pointeur fp correspond au fichier dont on souhaite réinitialiser le
curseur.
Renommer un fichier
62
63
Exercices
2. Ecrivez un programme qui permet d’écrire une chaine de caractère dans un fichier.
3. Ecrivez un programme qui demande un caractère quelconque, puis qui l’écrit dans un
fichier.
4. Ecrivez un programme qui demande un mot quelconque, puis qui l’écrit dans un fichier.
5. Ecrivez un programme qui demande un nombre décimal, puis qui l’écrit dans un fichier.
6. Ecrivez un programme qui demande un nombre entier, puis qui l’écrit dans un fichier.
7. Ecrivez un programme qui demande le code, nom, le prénom, la fonction et le salaire d’un
employé, puis qui les stocke dans un fichier.
10. Ecrivez un programme qui permet la lecture et l’affichage des infos du fichier de
l’exercice précédent.
11. Ecrivez un programme qui permet la lecture et l’affichage des infos d’un employé du
fichier de l’exercice 9.
63