0% ont trouvé ce document utile (0 vote)
10 vues169 pages

Logique et techniques de programmation

Le document présente un cours sur la logique et la technique de programmation, en se concentrant sur le langage C. Il couvre des sujets tels que la structure des instructions, la programmation structurée, les types de données, les opérateurs, et les fonctions, tout en fournissant des exemples pratiques. La table des matières indique une approche systématique pour enseigner les concepts fondamentaux de la programmation aux étudiants en informatique de gestion et systèmes.

Transféré par

muitjosias25
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
10 vues169 pages

Logique et techniques de programmation

Le document présente un cours sur la logique et la technique de programmation, en se concentrant sur le langage C. Il couvre des sujets tels que la structure des instructions, la programmation structurée, les types de données, les opérateurs, et les fonctions, tout en fournissant des exemples pratiques. La table des matières indique une approche systématique pour enseigner les concepts fondamentaux de la programmation aux étudiants en informatique de gestion et systèmes.

Transféré par

muitjosias25
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Logique et technique de programmation Notes provisoires

LOGIQUE ET LANGAGE DE PROGRAMMATION


1ÈRE INFORMATIQUE DE GESTION

TECHNIQUE ET LOGIQUE DE PROGRAMMATION


1ÈRE INFORMATIQUE ET SYSTÈMES

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 1


Notes provisoires Logique et technique de programmation

Page 2 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

TABLE DES MATIÈRES

Chapitre 1 : ____________________________________________________________________ 7
Vers un premier programme C _______________________________________________________ 7
1. Structure d'une instruction______________________________________________________ 7

2. Définitions __________________________________________________________________ 8

3. Premières instructions _________________________________________________________ 8

4. Lecture et écriture de et vers les périphériques ______________________________________ 9

5. Un ordinateur et ses périphériques.________________________________________________ 11

6. Le même programme en langage C _________________________________________________ 11

7. Remarque générale ____________________________________________________________ 14

8. Structure d'un programme ______________________________________________________ 15

Chapitre 2 : ___________________________________________________________________ 17
La programmation structurée_______________________________________________________ 17
1. Introduction _________________________________________________________________ 17

2. Comment introduire une fonction dans un programme ___________________________________ 18

3. Entête de programme __________________________________________________________ 19

4. Commentaires________________________________________________________________ 19

Chapitre 3 : ___________________________________________________________________ 21
Les types de données numériques et les opérateurs _____________________________________ 21
1. Déclaration des variables _______________________________________________________ 21

2. Conversion implicite / explicite entre types de données ________________________________ 22

3. Dépassements de valeur _______________________________________________________ 23

4. Imposer le format d’affichage d’un float ___________________________________________ 24

5. Les identificateurs ___________________________________________________________ 24

Chapitre 4 : ___________________________________________________________________ 27
Priorité des opérateurs ___________________________________________________________ 27
Chapitre 5 : ___________________________________________________________________ 29
Séquence ______________________________________________________________________ 29
Chapitre 6 : ___________________________________________________________________ 31
Les Alternatives ________________________________________________________________ 31
1. Utilité _____________________________________________________________________ 31

2. En pseudo-code ______________________________________________________________ 31

3. En utilisant un organigramme____________________________________________________ 32

4. En langage C ________________________________________________________________ 33

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 3


Notes provisoires Logique et technique de programmation

5. Règles d'écriture pour la lisibilité d'un programme____________________________________ 34

6. Exercice __________________________________________________________________ 34

7. Alternative incomplète ________________________________________________________ 35

8. Les conditions composées ______________________________________________________ 38

9. Remarques _________________________________________________________________ 39

10. La structure sélective _________________________________________________________ 41

Chapitre 7 : ___________________________________________________________________ 45
La répétitive : "TANT QUE ... FAIRE ... FIN TANT QUE" ______________________________ 45
1. En pseudo-code : _____________________________________________________________ 45

2. Organigramme : _____________________________________________________________ 45

3. En C : _____________________________________________________________________ 45

4. Exemple 1 :_________________________________________________________________ 46

5. Exemple 2: _________________________________________________________________ 47

6. Exemple 3: _________________________________________________________________ 48

7. Exemple 4 : ________________________________________________________________ 49

8. Exemple 5 : ________________________________________________________________ 50

Chapitre 8 : ___________________________________________________________________ 53
La répétitive : "FAIRE ... TANT QUE ..." ___________________________________________ 53
1. en pseudo code ______________________________________________________________ 53

2. Organigramme : _____________________________________________________________ 53

3. En C : _____________________________________________________________________ 53

4. Exemple : __________________________________________________________________ 54

Chapitre 9 : ___________________________________________________________________ 55
Le type CARACTERE : char _______________________________________________________ 55
1. Caractères ASCII normalisés ___________________________________________________ 55

2. Constante et variable de type caractère – char ______________________________________ 55

3. Caractères ASCII non normalisés ________________________________________________ 57

4. Formats d'affichage et de lecture _______________________________________________ 58

5. Lecture d'un caractère : GETCHAR()______________________________________________ 59

6. Lecture d'un caractère : GETCH() _________________________________________________ 61

7. Affichage d'un caractère : PUTCHAR() ____________________________________________ 63

8. Affichage d'un caractère : PUTCH() ______________________________________________ 63

Chapitre 10 : __________________________________________________________________ 65
Compatibilité et portabilité ________________________________________________________ 65
Chapitre 11 : __________________________________________________________________ 67
Les tableaux ___________________________________________________________________ 67
1. Définitions _________________________________________________________________ 67

2. Exemples : _________________________________________________________________ 68

Page 4 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

3. La boucle "for …" ____________________________________________________________ 70

4. Incrémentation et décrémentation ________________________________________________ 71

5. Illustration while, for, incrémentation … ___________________________________________ 73

6. Les tableaux à n dimensions ____________________________________________________ 75

Chapitre 12 : __________________________________________________________________ 77
Adresses et pointeurs ____________________________________________________________ 77
1. Les adresses________________________________________________________________ 77

2. Les pointeurs _______________________________________________________________ 77

3. Exemple ___________________________________________________________________ 78

4. Résumé ___________________________________________________________________ 78

5. Exemples __________________________________________________________________ 79

6. Le type des pointeurs ! ________________________________________________________ 80

7. Exemples ___________________________________________________________________ 81

Chapitre 13 : __________________________________________________________________ 87
Les fonctions ___________________________________________________________________ 87
1. Un premier exemple __________________________________________________________ 87

2. Le prototype de la fonction _____________________________________________________ 87

3. Commentaire d’en-tête ________________________________________________________ 88

4. Fonction qui reçoit un paramètre _________________________________________________ 88

5. Passage de paramètre(s) _______________________________________________________ 89

6. Valeur de retour______________________________________________________________ 91

7. Les variables globales _________________________________________________________ 93

8. Une fonction peut appeler une autre fonction________________________________________ 95

9. Les fonctions et les pointeurs ___________________________________________________ 96

10. Rappel concernant les pointeurs _________________________________________________ 99

11. Manipulation des tableaux dans les fonctions _______________________________________ 102

Chapitre 14 : _________________________________________________________________ 109


Tableaux de caractères__________________________________________________________ 109
1. Un premier exemple __________________________________________________________ 109

2. Afficher et lire le contenu d'un vecteur de caratères _________________________________ 109

3. Exercice 1 _________________________________________________________________ 110

4. Boucle et getchar()___________________________________________________________ 112

Chapitre 15 : _________________________________________________________________ 115


Les chaînes de caractères________________________________________________________ 115
1. Définition __________________________________________________________________ 115

2. Initialisation : chaîne / vecteur de caractères_______________________________________ 116

3. Instructions et fonctions relatives à la gestion des chaînes de caractères __________________ 116

4. Vecteurs de chaînes de caractères ou tableaux de caractères. ___________________________ 124

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 5


Notes provisoires Logique et technique de programmation

Chapitre 16 : _________________________________________________________________ 125


Tableaux de pointeurs ___________________________________________________________ 125
1. Rappel ____________________________________________________________________ 125

2. un vecteur de pointeurs _______________________________________________________ 125

3. Exercice 1 _________________________________________________________________ 126

Chapitre 17 : _________________________________________________________________ 127


Les structures _________________________________________________________________ 127
1. Définitions _________________________________________________________________ 127

2. Déclarations________________________________________________________________ 127

3. Accès aux champs d'une structure _______________________________________________ 128

4. Une structure comme paramètre à une fonction ______________________________________ 129

5. Un vecteur ou tableau de structures______________________________________________ 130

6. Une structure contenant une structure ____________________________________________ 131

7. Typedef ___________________________________________________________________ 131

8. structure et alignement des champs ______________________________________________ 132

Chapitre 18 : _________________________________________________________________ 135


Les fichiers sur disque __________________________________________________________ 135
1. Définition __________________________________________________________________ 135

2. Ouverture d'un fichier sur disque ________________________________________________ 135

3. Lecture d'un fichier sur disque __________________________________________________ 141

4. Ecriture d'information dans un fichier sur disque ____________________________________ 142

5. Fermeture d'un fichier ________________________________________________________ 142

6. Les fichiers structurés ________________________________________________________ 143

7. Déplacement dans un fichier ____________________________________________________ 144

8. Opérations supplémentaires dans le cadre des fichiers_________________________________ 145

Chapitre 19 : _________________________________________________________________ 149


L'allocation dynamique en mémoire _________________________________________________ 149
et les structures de données dynamiques ____________________________________________ 149
1. L'allocation dynamique_________________________________________________________ 149

2. Les structures de données dynamiques ____________________________________________ 153

Chapitre 20 : _________________________________________________________________ 159


Autres notions _________________________________________________________________ 159
1. Passage de paramètres à un programme ____________________________________________ 159

2. Fonctions ayant un nombre variable de paramètres____________________________________ 162

3. Union _____________________________________________________________________ 166

Chapitre 21 : _________________________________________________________________ 169


Bibliographie __________________________________________________________________ 169

Page 6 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 1 :

Vers un premier programme C

1. STRUCTURE D'UNE INSTRUCTION

Code opératoire opérande 1 opérande 2

Premier exemple : MOV AX,60

L'instruction est analysée par le µP. Le code opératoire est reconnu, ensuite les 2
opérandes sont lues et l'instruction est exécutée. Pour ce faire, le µP doit avoir accès aux
données dans les deux sens : lecture et écriture.
Dans cet exemple, 60 n'est pas une variable, il s'agit d'une valeur (constante).

Un programme est une suite d'opérations machine.


L'instruction est prise en charge par le µP et est analysée bit/bit.

Deuxième exemple : ADD AX,Z

Cette instruction permet d'additionner la valeur de la variable Z au contenu du registre


AX et de stocker le résultat dans ce même registre AX.
Le µP va donc se servir de l'ALU pour évaluer le résultat de l'addition du contenu du
registre AX et de celui de la variable Z et il va stocker le résultat dans le registre AX.

Troisième exemple : MOV Z,AX

La variable Z, c'est-à-dire la zone de mémoire désignée sous le nom Z reçoit la valeur


contenue dans le registre AX.

Quatrième exemple : instruction en langage C : r = a + b;

r, a et b sont des variables désignant une zone mémoire.

Cette instruction en langage machine s'exprime comme suit, après compilation :

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 7


Notes provisoires Logique et technique de programmation

MOV AX, a
ADD AX, b
MOV r, AX

a 12

b 16 AX 12 28

r 28

Cette instruction ne fonctionnera correctement que si chacune des variables contient


une valeur.

2. DEFINITIONS
Une variable est une zone de mémoire identifiée par un nom. Son contenu peut être
modifié.

Une constante est une valeur numérique, un texte, une valeur logique, … Sa valeur est
fixe.

Exemples : 4 est une constante numérique entière


4.25 est une constant numérique réelle

Le nombre d'octets qu'une variable ou une constante occupera en mémoire dépend de


son type et du compilateur utilisé. Sous Microsoft Visual C++ 6.0, un entier occupe 4 bytes,
de même qu'un réel.

Un octet est composé de 8 bits (d'où son nom).


La valeur de la variable dépend de l'état de ces bits.

1 octet = 1 byte = 8 bits

3. PREMIERES INSTRUCTIONS
Prenons par exemple une variable de type entier et appelons-la a.

a:

et soit a = 7; /* on appelle cette instruction une assignation */


/* a reçoit la valeur 7 */

Page 8 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Avant l'exécution de cette instruction, on ne connaît pas la valeur contenue dans la


variable a. Sa valeur est indéterminée. Il ne faut surtout pas croire qu'elle contient la valeur
0.

a: 0000 0000 0000 0000 0000 0000 0000 0111

Après l'exécution de cette instruction, la valeur de a est 7.

Si on effectue l'opération

b = 4;

On assigne la valeur 4 à la variable qui a pour nom b !

On peut également effectuer une opération mathématique dans la partie droite d'une
instruction d'affectation : le résultat de cette opération sera stocké à l'emplacement
mémoire désigné par la variable donnée à gauche du signe d'affectation (=).

Par exemple, si on effectue les 3 instructions suivantes :

a = 5;
b = 6;
r = a + b;

La variable r prendra pour valeur le résultat de la somme des valeurs stockées dans les
emplacements mémoires a et b. r prendra donc la valeur 11.

4. LECTURE ET ECRITURE DE ET VERS LES PERIPHERIQUES

4.1. Lecture
Il n'est pas très utile d'additionner toujours les mêmes valeurs. L'intérêt est de
demander à l'utilisateur de donner les valeurs qu'il veut additionner.
Il faudrait donc arriver à placer dans a et b des valeurs obtenues par "lecture" ou
saisie. Il faut pouvoir "lire" ou "saisir" des valeurs par l'intermédiaire d'un
périphérique.

Un périphérique est un matériel informatique connecté à la carte mère.


Il peut s'agir par exemple du clavier ou du disque dur.

L'instruction a = 5;
va devenir "lire a" et va correspondre à la saisie d'une valeur au clavier qui sera
affectée à la variable a.

On va donner une valeur aux variables a et b par l'intermédiaire du clavier, on va


donc effectuer un transfert d'information du périphérique vers la mémoire centrale.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 9


Notes provisoires Logique et technique de programmation

Les 3 instructions données précédemment deviennent :


lire a;
lire b;
r = a + b;

où "lire" consiste à saisir une valeur au clavier et à l'affecter à la variable a.

En C, on utilisera, pour l'instant la fonction "scanf" qui permet de saisir une


valeur au clavier et de la transférer vers la mémoire centrale. On parlera d'"entrée",
d'"input", de "lecture" ou de "saisie".

scanf ("xx", &a);

xx dépend du type de la variable a. (on verra en effet qu'il existe différents


types de variable en C : celles qui peuvent contenir un entier, un réel, un caractère, une
chaîne de caractères, …)

Remarque : scanf est une fonction peu professionnelle, on la voit en début d'année
car elle est simple à utiliser. Mais elle présente deux inconvénients majeurs :

Si on entre <ENTER> avant d'avoir tapé une valeur, l'instruction ne donne pas de
message d'erreur. Elle se contente de ne pas modifier le contenu de la variable.

Si le buffer (zone tampon entre les périphériques et l'unité centrale) n'est pas
vide, scanf va lire le contenu de celui-ci, avant que l'utilisateur n'ait entré quoi que ce
soit au clavier.

buffer Mémoire centrale


Unité
centrale

µP

Clavier

Autres solutions : utiliser gets ou faire une boucle avec l'instruction getchar.
Cette dernière solution est de loin la meilleure et sera abordée plus tard dans ce cours.

4.2. Ecriture
Maintenant qu'on a vu comment demander des valeurs à l'utilisateur afin de ne
pas toujours additionner les mêmes nombres, il peut être intéressant de pouvoir lui
communiquer le résultat.
Il s'agit ici d'un transfert d'information depuis la mémoire centrale vers l'écran,
ou plus exactement, vers la carte vidéo.

Notre petit programme devient alors :

Page 10 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

lire a;
lire b;
r = a + b;
afficher a;
afficher b;
afficher r;

En C, l'instruction "afficher" peut se faire grâce à la fonction "printf". On


parlera ici de "sortie", d'"output", d'"affichage" ou encore d'"écriture" de la mémoire
vers l'écran.

5. UN ORDINATEUR ET SES PERIPHERIQUES.

écran

Mémoire centrale

µP

A:
clavier

HD c:
B:

imprimante

NE PAS OUBLIER qu'il y a toujours un buffer entre le périphérique et la mémoire


centrale.

6. LE MEME PROGRAMME EN LANGAGE C

Si nous écrivons le programme vu dans les paragraphes précédents en C, nous obtenons


le code suivant :

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 11


Notes provisoires Logique et technique de programmation

main()
{
int a, b, r;

scanf ("%d", &a);


scanf ("%d", &b);

r = a + b;

printf ("a = %d", a);


printf ("b = %d", b);
printf ("c = %d", c);
}

Le problème est que le µP ne comprend pas les fonctions telles que scanf, printf, … Il
faut donc rendre notre programme compréhensible par l'ordinateur.

Voici donc les différentes étapes d'écriture d'un programme :


écriture d'un pseudo code sur papier,
traduction de ce pseudo code en langage informatique ou encodage,
compilation,
"link" : liaison avec les autres fichiers nécessaires,
exécution.

6.1. Ecriture du pseudo-code


C'est à ce moment que l'on réfléchit à la logique du programme sans se soucier de
la syntaxe rigoureuse d'un langage de programmation :
quelles sont les données dont je dispose ?
quelles sont celles qui doivent être demandées à l'utilisateur ?
quel résultat dois-je obtenir ?
comment arriver à ce résultat ?
que dois-je afficher comme information ?
...

6.2. Traduction en langage informatique : encodage


C'est l'étape qui consiste à écrire un programme via le clavier. Le programme
d'encodage s'appelle l'éditeur. Il permet d'introduire les instructions à l'aide du
clavier et de les enregistrer sur disque dur ou disquette. On sauvera ce fichier en lui
donnant l'extension ".c". Ce fichier est en réalité un simple fichier texte qui pourrait
être créé à l'aide de n'importe quel éditeur de texte.

Page 12 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

mémoire

µP test.c

programme
d'encodage : éditeur

6.3. Compilation
Le programme que l'on a écrit en C précédemment ne peut être compris tel quel
par la machine. On va, à cet effet, utiliser un autre programme qui s'appelle un
compilateur.
Le compilateur est capable de traduire un fichier .c en une autre entité, un autre
fichier, qui aura pour extension ".obj" et que l'on appelle le code objet.

Schéma de la compilation.

test.c  données fournies au compilateur

compilateur  programme capable de lire les données


et de traduire celles-ci

[Link]

Le compilateur traduit les instructions C en langage machine.


C'est lors de la compilation que sont détectées les erreurs de syntaxe. Le
compilateur indique l'endroit où il a trouvé l'erreur ainsi que la nature du problème.
Dans ce cas, il faut corriger l'erreur et recommencer la compilation jusqu'à ce que
celle-ci se passe sans problème.

6.4. Link
Le programme qui s'occupe du link est le linker. Il s'agit d'un éditeur de liens.
En fait, dans le fichier .obj créé par le compilateur, on trouve des informations
sur l'emplacement, le répertoire, où se trouvent les bibliothèques de fonctions à inclure
dans le programme.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 13


Notes provisoires Logique et technique de programmation

Schéma du link.

[Link] Bibliothèque de fonctions

Programme de link

[Link]

Le fichier [Link] contient maintenant des instructions machine et est donc


compréhensible pour l'ordinateur.

7. REMARQUE GENERALE
Comme on a pu le voir dans les paragraphes précédents, les problèmes surviennent
surtout lors de l'écriture du programme.

données => ? => résultat

Comment parvenir à écrire un programme qui apporte une solution correcte au


problème donné ?

C'est un problème de logique de programmation

Un des outils à notre disposition est le pseudo-code. C'est une écriture du programme
qui ne tient compte ni du langage de programmation, ni de la machine, ni des fautes de frappe
=> on se concentre uniquement sur la logique du problème.

La démarche qu'il faut observer pour construire un programme est la démarche de la


PROGRAMMATION STRUCTURÉE (voir chapitre suivant).

Page 14 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

8. STRUCTURE D'UN PROGRAMME

Tout programme C doit respecter une certaine structure sans quoi la compilation ne
pourra s'effectuer correctement.

#include <stdio.h>
#include <stdlib.h> Fichiers d'entête à inclure : prototype des fonctions

main () fonction principale


{ accolade d'ouverture : indispensable au
début de chaque fonction
float fCotePhys, fCoteChim, fCoteBio; déclaration des varia-
float fMoyenne; bles : ne crée aucun
code à la compilation
/* lecture des différentes valeurs */
printf ("Entrez les cotes sur 20");
printf ("\n\tphysique : ");
scanf ("%f", &fCotePhys);
printf ("\n\tchimie : ");
scanf ("%f", &fCoteChim); instructions du
printf ("\n\tBiologie : "); programme : le
scanf ("%f", &fCoteBio);
compilateur génère du
/* Calcul de la moyenne */ code
fMoyenne = (fCotePhys + fCoteChim + fCoteBio) / 3;

/* Affichage du résultat */
printf ("\nLa moyenne des trois cotes est : %f", fMoyenne);

} accolade de fermeture : indispensable à la


fin de chaque fonction

Voici les différentes parties que l'on peut analyser dans un programme écrit en
langage C :

#include <…> : demande d'inclure lors de la compilation certains objets au


début du programme :
les objets sont issus des bibliothèques
les objets sont des prototypes
par exemple : si le programme utilise la fonction printf, son
prototype, qui se trouve dans stdio.h doit être placé au début du
programme.
un prototype spécifie le nombre de paramètres que va recevoir une
fonction ou une instruction, leur type et le type de la valeur
retournée
Remarque importante : en C, tout est fonction, y compris le programme
principal !
=> main est une fonction particulière par laquelle le programme va toujours
commencer mais c'est quand même une fonction.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 15


Notes provisoires Logique et technique de programmation

Un bloc d'instructions commence toujours par { et se termine toujours par


}. En particulier, toute fonction commence par { et se termine par }.
Dans le main, on distingue deux parties principales :
la déclaration des variables : elle ne génère aucun code au moment de
la compilation;
les instructions qui vont donner naissance à du code lors de la
compilation

Page 16 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 2 :

La programmation structurée

1. INTRODUCTION

La programmation structurée consiste à diviser un problème en plusieurs modules (voir


aussi aux travaux dirigés).
Il s'agit d'une démarche rationnelle et non d'une recette.

Par exemple :

Gestion d'un club sportif

Membres Sponsors Infrastructure Résultats


s

Ajouter Retirer Afficher les


un membre un membre coordonnées
du membre
etc.

Cette façon d'aborder un problème présente un certain nombre d'avantages :


un module pris séparément est beaucoup plus simple à comprendre et à
résoudre (il est plus facile de lire un livre structuré en chapitres que si tout
le texte est tapé à la suite, sans aucune structure)
on peut modifier un module sans devoir toucher le reste du programme
la lecture et la maintenance d'un programme découpé en modules sont
beaucoup plus simples : cela coûte donc moins cher
la programmation peut-être répartie entre plusieurs programmeurs
...

 il faut absolument adopter cette façon de programmer

Or en C, tout est fonction


Donc chaque module devra être une fonction.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 17


Notes provisoires Logique et technique de programmation

La démarche de la programmation structurée est basée essentiellement sur une


approche rationnelle, celle-ci ne repose nullement sur des recettes.
La seule manière de s'imprégner de cette démarche est la pratique encadrée par un
guide averti.

2. COMMENT INTRODUIRE UNE FONCTION DANS UN PROGRAMME

Voyons un exemple :

#include <stdio.h>
#include <conio.h>

void affiche_message (void); prototype de la fonction

main ()
{
float a, b, m;

/* saisie des données */


printf ("Entrez la valeur du numérateur : ");
scanf ("%f", &a);
printf ("\nEntrez la valeur du dénominateur\n");
printf ("(different de 0) : ");
scanf ("%f", &b);

affiche_message (); appel de la fonction

/* calcul du résultat */
m = a / b;

/* affichage du résultat */
printf ("\nLe résultat de la division est : %f", m);
}

/*********************************************/
/* INPUT : */ description
/* Process : affiche un message */ de la
/* OUTPUT : */ fonction
/*********************************************/
void affiche_message (void) Entête de la fonction
{
printf("Bonjour, ce programme effectue une division"); le code de
} la fonction

Remarques :
il s'agit d'une fonction qui ne reçoit aucun paramètre et ne retourne aucune
valeur;
dans le prototype qui indique le nombre de paramètres reçus, leur type ainsi
que la valeur retournée, on inscrit le terme "void";
l'appel de la fonction n'est composé que du nom de celle-ci : effectivement, on
ne passe pas de paramètres;

Page 18 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

le prototype, l'appel et l'entête de la fonction doivent être cohérents !


la fonction débute par { et se termine par } ;
les règles d'écriture dans la fonction sont les mêmes que dans le main();

3. ENTETE DE PROGRAMME

/*********************************************************/
/* Auteur : NOM Prénom */
/* Groupe : 21XX */
/* Application : nom et description de l'exercice */
/* Date de la dernière mise à jour : 22/09/2005 */
/*********************************************************/

4. COMMENTAIRES
Un programme, ensemble de fonctions, doit contenir des commentaires pertinents dans
le but de rendre celui-ci lisible.
Le programme doit être lisible pour permettre au programmeur d'intervenir rapidement
au sein de celui-ci pour en assurer la mise au point et la maintenance.

Un commentaire commence toujours par /* et se termine par */


Un commentaire n'est pas une instruction, il n'est pas traduit en langage machine
Le compilateur ignore tous les commentaires

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 19


Notes provisoires Logique et technique de programmation

Page 20 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 3 :

Les types de données numériques et les opérateurs

1. DECLARATION DES VARIABLES

1.1. Variables entières

short int : 2 octets => de –32 768 à +32 767


int : 4 octets => de –2 147 783 648 à +2 147 483 647
unsigned int : 4 octets => de 0 à 4 294 967 295

Comment obtient-on se résultat ?


20 = 1
21 = 2
22 = 4
23 = 8
4
2 = 16
25 = 32
26 = 64
27 = 128
28 = 256
29 = 512
10
2 = 1024
211 = 2048
212 = 4096
213 = 8192
214 = 16384
215 = 32768
...
231 = 2 147 483 648
232 = 4 294 967 296

Dans la représentation interne des valeurs entières signées, le bit de poids fort
("bit de gauche") représente le signe. (0 positif et 1 négatif).

1.2. Variables en virgule flottante

float : 4 octets => de -3.4 * 1038 à –1.17 * 10-38 ;

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 21


Notes provisoires Logique et technique de programmation

de 1.17 * 10-38 à 3.4 * 1038 ;


et 0.0

double : 8 octets => de –1.8 * 10308 à –2.2 * 10-308 ;


de 2.2 * 10-308 à 1.8 * 10308;
et 0.0

Remarque : il faut surtout retenir les valeurs pour les entiers, pour les, autres
types il faut pouvoir rechercher l'information (limits.h et float.h).

2. CONVERSION IMPLICITE / EXPLICITE ENTRE TYPES DE DONNEES

Si on écrit ce qui suit :

int i ;
float r ;

i = 7/2; /* => i = 3 */
r = 7/2; /* => r = 3.0 */
i = 7.0/2; /* => i = 3 */
r = 7.0/2; /* => r = 3.5 */

Pourquoi ?
Si i est un int, il ne peut contenir qu’une valeur entière ! Donc quel que soit le résultat
du calcul effectué à droite du signe ‘=’, i ne pourra jamais prendre qu’une valeur entière !

Quant à r, qui est un réel, pourquoi prend-t-il aussi la valeur 3.0 dans le deuxième
calcul ?
7 et 2 sont des entiers ! En C, diviser un entier par un entier donnera toujours un
entier : il s’agit d’une division entière ! Même si on met le résultat dans un réel, le résultat
sera converti : 3 -> 3.0 !
Par contre, 7.0 est un float et 2 est un entier, et diviser un float par un entier donne un
float. Le résultat du calcul est donc un float et si on met ce résultat dans un variable de type
float, le type sera conservé.

De même, on obtient :

int i, a, b ;
float r, c;

a = 7;
b = 2;
c = 7.0;

i = a/b; /* => i = 3 */
r = a/b; /* => r = 3.0 */
i = c/b; /* => i = 3 */
r = c/b; /* => r = 3.5 */

Que faire si l’on veut diviser 2 entiers et mettre le résultat dans un réel tout en
conservant la partie décimale ?

Page 22 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

1. On peut "transformer" l’entier en réel :


r = a * 1.0 / b ;
La valeur contenue dans l’espace mémoire donné par a ne change pas, mais
le compilateur l’interprète comme un réel dans le cadre de ce calcul car le
résultat de l’opération "a * 1.0" est un float

2. Casting
r = (float)a / b ;
On dit explicitement au compilateur qu’il doit interpréter le contenu de la
variable a comme un float.
i = (int)c ;
Dans ce cas, le compilateur interprète c comme un entier pour copier sa
valeur dans la zone mémoire donnée par i. C’est donc syntaxiquement plus
correct même si l’instruction "i = c ;" donnera le même résultat !

3. DEPASSEMENTS DE VALEUR
Si on écrit :

short int i ;
i = 123456 ;

Que va-t-on trouver dans la variable ‘i’ ?


Rien de bon !!
Attention : le compilateur ne donne pas d’erreur, mais les résultats du programme sont
erronés : on peut obtenir à peu près n’importe quoi !!

Il est très important de faire attention aux limites permises par les types des
variables et aux résultats des expressions intermédiaires.

Exemple :
short int a;
int b;
double c, r;

a = 20000;
b = 2000000;
c = 5000000.0;

r = c + a * b;
printf ("%lf + %hd * % d = %lf\n", c, a, b, r);

Le résultat affiché est : 5000000.0 + 20000 * 2000000 = 1350294336.0 !


D'évidence, ce résultat est faux; cependant aucun message n'accompagne cette
exécution !

D'où provient l'erreur ?

Vu la priorité des opérateurs (cette théorie sera vue ultérieurement), les instructions
sont exécutées dans un certain ordre (semblable aux opérateurs utilisés en mathématique) :
1. exécution de a * b;
Le résultat de cette expression (short int * int) est du type int.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 23


Notes provisoires Logique et technique de programmation

Le résultat prévu 40 000 000 000 dépasse la capacité d'un int; donc, ce résultat
intermédiaire est dès lors faux (soit x)

2. exécution de c + x
Le résultat de cette expression (double + int) est du type double, ce qui implique une
conversion implicite de x int vers du double. La valeur entière de x fausse convertie
en double sera toujours fausse.
Le résultat de l'addition sera donc faux.

3. assignation à r
r reçoit ce résultat faux !

Comment corriger cette erreur ?


En introduisant un casting, on obtiendra le bon résultat :

r = c + (double)a * b;

Dès lors le programme affichera : 5000000.0 + 20000 * 2000000 = 40005000000.0 !


Qui est le résultat correct !

4. IMPOSER LE FORMAT D’AFFICHAGE D’UN FLOAT

Exemple :

float x ; /* ≈ 8 chiffres base 10 significatifs */


x = 24.12345678

printf("Toutes les décimales : %f", x); /* 24.1234567 */


printf("2 décimales : %.2f", x) ; /* 24.12 */
printf("8 positions, 4 décimales : %8.4f", x) ; /* _24.1235 */

5. LES IDENTIFICATEURS
Les identificateurs servent à identifier un objet, que ce soit une variable, une
constante, une fonction, un type, etc.

Tous les identificateurs sont constitués de lettres, chiffres et ‘_’ (underscore).


Attention, le C fait la distinction entre les majuscules et les minuscules.

Convention :
variables, fonctions : ensembles de majuscules et minuscules avec une prédominance de
minuscules
Constantes : entièrement en majuscules

Le premier caractère doit être impérativement une lettre ou un ‘_’ (pas de chiffre en
première position)

On ne peut pas utiliser d’espace, sinon, seule la première partie du nom est prise en
considération, le reste étant perdu (et provoquant une erreur à la compilation).

Page 24 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Prendre l’habitude d’utiliser des noms représentatifs afin d’augmenter la lisibilité du


programme. On peut également mettre comme premier caractère la première lettre du type
de la variable. Exemples : int : iRayon ; float fSurface ;

On ne peut pas utiliser les mots réservés du C !

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 25


Notes provisoires Logique et technique de programmation

Page 26 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 4 :

Priorité des opérateurs

Exemple :

int c ;

c = 4 + 6 / 2 ; /* => c = 7 */
c = (4 + 6) / 2 ; /* => c = 5 */
c = 4 + 6 * 2 ; /* => c = 16 */
c = (4 + 6) * 2 ; /* => c = 20 */

La priorité des opérateurs arithmétiques est donc :

()
fonctions
*, /, %
+, -
=

Pour rappel, ‘%’ est l’opérateur modulo, il donne donc le reste de la division entière du
premier nombre par le second.

Exemples : 5%2=1 en effet, 5/2 = 2, 2 * 2 = 4 , 5 – 4 = 1


4%2=0
10 % 3 = 1
14 % 11 = 3

Dans l'aide du logiciel "Visual C++ 6.0" de Microsoft, vous trouverez un tableau complet
reprenant l'ordre de priorité des opérateurs.
Voir la rubrique "Precedence and order of evaluation".

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 27


Notes provisoires Logique et technique de programmation

Page 28 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 5 :

Séquence

De manière implicite, nous avons vu jusqu’à présent que l’ordinateur exécute les
opérations les unes après les autres dans l’ordre dans lequel elles apparaissent dans le
programme. Il exécute donc le programme en séquence.

Exemple :
DEBUT
Module X
Module Y
Module Z
FIN

Dans ce cadre, un module = une instruction,


un appel de fonction
une instruction composée : { ... }

Lors de l’exécution du programme, on exécute d’abord le module X, puis le module Y et


enfin le module Z.
Un module est un ensemble d’instructions caractérisé par un seul point d’entrée et un
seul point de sortie.
Les instrucitons qui constituent ce module sont des instructions "structurées"
(séquence, alternative, répétitive).

Module => elles forment un ensemble structuré

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 29


Notes provisoires Logique et technique de programmation

Page 30 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 6 :

Les Alternatives

1. UTILITE
On veut parfois conditionner l'exécution d'une ou plusieurs opérations au résultat de
l'évaluation d'une condition.
Exemple : s'il fait beau, j'irai me promener.

On peut aussi décider de faire une ou plusieurs autres actions dans le cas où la condition
n'est pas vraie.
Exemple : s'il fait beau, j'irai me promener, sinon, j'irai au cinéma.

2. EN PSEUDO-CODE

Si (condition)

alors
Quand la condition est vraie
sinon
Quand la condition est fausse

La condition testée est soit vraie soit fausse, il n'y a pas d'autre possibilité !
Il y a un seul point d'entrée et un seul de sortie.

exemple :
a b

Si (a > b)

alors
Quand a > b
sinon
Quand a < b ou a == b

L'exécution de ce module est caractérisé par le résultat de la comparaison :


soit a > b, soit a < b, soit a == b

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 31


Notes provisoires Logique et technique de programmation

3. EN UTILISANT UN ORGANIGRAMME

a, b

oui a > b non


?

traitement X traitement Y

Autre exemple :
a, b

oui a > b non


?

traitement X oui non


a < b
?

traitement Y traitement Z

Ce deuxième exemple donne en pseudo code :

Si (a > b)

alors Traitement X

sinon
Si (a < b)
alors
traitement Y
sinon
traitement Z

Page 32 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

4. EN LANGAGE C

Syntaxe :

if (condition)
{
... /* la condition est vraie */
}
else
{
... /* la condition n'est pas vraie */
}

Remarques :
le "alors" n'est pas traduit en C
les accolades ne sont nécessaires que si il y a plusieurs instructions à
exécuter

4.1. Exemple 1

system ("cls");
if (a < b)
{
printf ("a est plus petit que b");
}
else
{
printf ("a n'est pas plus petit que b\n");
printf ("c\'est-à-dire : a est plus grand ou égal à b");
}

4.2. Exemple 2

system ("cls");
if (a < b)
{
printf ("a est plus petit que b");
}
else
{
if (a > b)
{
printf ("a est plus grand que b\n");
}
else
{
printf ("a est égal à b");
}
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 33


Notes provisoires Logique et technique de programmation

5. REGLES D'ECRITURE POUR LA LISIBILITE D'UN PROGRAMME

un bloc d'instructions doit toujours commencer par une '{' et se terminer


par une '}'
une paire d'accolades doit toujours se trouver sur la même colonne :
{
...
}
note : pour ne pas oublier : fermer la paire d'accolades dès qu'on l'ouvre,
avant de la remplir !!!
toutes les instructions appartenant à un même bloc d'instructions doivent
commencer à la même colonne.
lorsqu'on passe dans un sous-bloc d'instructions, il faut décaler les
instructions du sous-bloc par rapport aux instructions du groupe
précédent :

Exemple :
a = 4;
b = 5;
system("cls");
if (a < b)
{
printf ("a est plus petit que b\n");
}
else
{
if (a > b)
printf ("a est plus grand que b\n");
else
printf ("a est égal à b\n");
}

6. EXERCICE
Soient a et b, deux données entières à saisir au clavier.
Le programme doit afficher l'un des deux messages suivants :

a = … b = … résultat = …
a = … b = 0 message d'erreur

Le résultat est la valeur donnée par la division entière de a par b.


La division par 0 étant impossible, quand b vaut 0, on donne un message d'erreur de
type : "la division par 0 n'est pas permise !".

Page 34 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Pseudo code :

DEBUT
lire a, b
afficher a
afficher b
SI (b == 0)
ALORS afficher "la division par 0 n'est pas permise !"
SINON résultat = a/b
afficher résultat
FIN SI
FIN

7. ALTERNATIVE INCOMPLETE
Jusqu'à présent, nous avons considéré des alternatives du genre :

SI (condition)
ALORS
Traitement X

SINON
Traitement Y

FIN SI

On peut imaginer des cas où le traitement Y serait inexistant, c'est-à-dire où il y a un


traitement X à exécuter si la condition est vraie et rien si elle ne l'est pas.

Dans ce cas, on peut écrire :

SI (condition)
ALORS
Traitement X

FIN SI

ce qui donne sous forme d'organigramme :

a, b

oui non
a > b
?

traitement X

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 35


Notes provisoires Logique et technique de programmation

Et qui se traduit en langage C par :

if (condition)
{
...
}

7.1. Exemples
L'exemple d'alternative donné précédemment permettait de séparer les 3 cas suivants :
"a < b", "a == b" et "a > b".
Nous avions écrit le pseudo-code suivant :

SI (a > b)

ALORS Traitement X

SINON
SI (a < b)
ALORS traitement Y

SINON
traitement Z

FIN SI
FIN SI

Autre proposition :

SI (a == b) oui non
a == b
ALORS Traitement Z ?

FIN SI traitement Z
SI (a < b)
ALORS
traitement Y

oui a < b non


SINON
traitement X ?

FIN SI
traitement Y traitement X

Cette méthode n'est pas valable !!!


! Si "a == b", alors le module destiné à "a > b" est quand même exécuté !!!
Dans cet exemple, la logique est mauvaise !!

Page 36 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Nouvelle proposition :

SI (a == b)
oui a == b non
ALORS
Traitement Z ?

FIN SI
SI (a < b) traitement Z
ALORS
traitement Y
FIN SI oui a<b non
SI (a > b) ?
ALORS
traitement X
traitement Y
FIN SI

oui a>b non


?

traitement X

Ce qui donne en C :

if (a == b)
{
...
}
if (a < b)
{
...
}
if (a > b)
{
...
}

Avantage : on est certain de traiter tous les cas


Inconvénient : le programme passe par les 3 alternatives, il évalue les 3 conditions,
même si "a == b" et que le traitement Z a été effectué, il va vérifier si "a < b" et si "a > b" =>
cet algorithme n'est pas très efficace et est donc à éviter !!!

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 37


Notes provisoires Logique et technique de programmation

7.2. Importance des '{' '}'


Revenons sur l'importance des accolades lorsque le traitement correspondant au cas où
la condition de l'alternative est évaluée vraie, et que ce traitement comporte plus d'une
instruction.

Exemple : écrire un pseudo-code qui permet d'intervertir la valeur des deux variables a
et b dans le cas où a est supérieur à b.

SI (a > b)
ALORS c <- a
a <- b
b <- c
FIN SI

ce qui donne en C :

if (a > b)
{
c = a;
a = b;
b = c;
}

Si on oublie les accolades,


dans le cas où la condition est vraie, les 3 instructions seront exécutées et le
programme se comportera comme attendu.
dans le cas où la condition est fausse, l'instruction c = a; ne sera pas
exécutée; par contre, les instructions a = b; et b = c; seront exécutées.
Le programme ne se comportera pas comme attendu !!!

8. LES CONDITIONS COMPOSEES


On a déjà abordé les conditions simples telles que : "a < b", "a > b" et "a ==b". Il faut
bien faire attention au fait que pour tester l'égalité, on doit mettre deux fois le signe '='. Le
signe '=' est, en effet, l'opérateur d'affectation.

SI (a <= b) est équivalent à if (a < b || a == b)


if (a <= b)
if ((a < b) || (a == b))
mais pas à if (a < b || == b) => syntaxe incorrecte !!!

On peut combiner plusieurs conditions au moyen des opérateurs "ET" et "OU".

Quand on utilise le ET, c'est-à-dire '&&' en C, les deux conditions données doivent être
vraies pour que la condition globale soit vraie.
Il suffit donc qu'une des deux soit fausse pour que la condition composée soit fausse.

Quand on utilise le OU, c'est-à-dire '||' en C, au moins une des deux conditions données
doit être vraie pour que la condition globale soit vraie.

Page 38 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Il faut donc que les deux conditions soient fausses pour que la condition composée soit
fausse.

8.1. Exemples :
1. if (0 < m && m < 13)
 m doit être compris entre 0 et 13 pour que la condition soit vraie : m doit
être à la fois plus grand que 0 ET plus petit que 13.
 pour que la condition soit fausse, il suffit que m soit inférieur ou égal à 0
OU supérieur ou égal à 13.
 ! (0 < m && m < 13) équivaut à (0 >= m || m >= 13)
/* ! représente l'opérateur NOT */

2. if (a > b || a > c)
 a ne peut pas être le plus petit des trois nombres : il doit soit être plus
grand que b, soit plus grand que c. Pour que la condition composée soit
vraie, il suffit qu'une au moins des deux sous-conditions soit vraie
 pour que la condition soit fausse, il faut que les deux sous-conditions soient
fausses : a doit être inférieur ou égal à b ET inférieur ou égal à c.
 ! (a > b || a > c) = (a <= b && a <=c)

9. REMARQUES

9.1. 0 ou différent de 0
une condition est fausse si elle vaut 0
une condition est vraie si elle est différente de 0

 une condition du type "if (i != 0)" peut s'écrire "if (i)" mais est moins lisible
 "if (i == 0)" peut donc aussi s'écrire "if (! i)"

9.2. assignation n'est pas comparaison !


Si on écrit
if (a = b)
{
...
}

c'est correct au niveau de la syntaxe, mais cela ne réagit pas exactement comme
if (a == b).
En effet, il ne s'agit pas d'un test d'égalité mais d'une affectation !
La valeur de b est copiée dans a, ensuite, vu la remarque précédente, si a vaut 0,
la condition est considérée comme fausse, sinon,dans tous les autres cas, elle est
considérée comme vraie !!

Il faut donc être prudent et bien utiliser "==" quand on veut effectuer un test
d'égalité !!

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 39


Notes provisoires Logique et technique de programmation

9.3. lisibilité
Si on écrit en C :

if (cond1) instr1; else if (cond2) instr2; else if


(cond3) instr3; else instr4;

Le compilateur va accepter et comprendre ce code, mais cela risque de ne pas être le


cas du programmeur ! Il est donc de loin préférable d'écrire le même code de la façon
suivante :

if (cond1)
{
instr1;
}
else
{
if (cond2)
{
instr2;
}
else
{
if (cond3)
{
instr3;
}
else
{
instr4;
}
}
}

Dans cet exemple, toutes les accolades sont facultatives et peuvent donc être
supprimées, rien que l'indentation aide beaucoup à une meilleure compréhension !

Page 40 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

10. LA STRUCTURE SELECTIVE


Considérons l'exemple suivant :

afficher "donnez le numéro du jour de la semaine (0 à 6)"


lire iJour
SI (iJour == 5)
ALORS afficher "Enfin vendredi"
SINON
SI (iJour == 6)
ALORS afficher "Génial, c'est samedi !"
SINON
SI (iJour == 1)
ALORS afficher "Hélas, nous ne sommes que lundi"
SINON
SI (iJour == 0)
ALORS afficher "Enfin un dimanche reposant !"
SINON afficher "J'attends le week-end prochain !"
FIN SI
FIN SI
FIN SI
FIN SI

On peut voir que l'on arrive rapidement à un certain nombre de "if" imbriqués. Dans ce
cas, si on avait prévu un message différent pour chaque jour, on en aurait encore eu plus !!!
Il existe une structure sélective qui permet de simplifier la notation de ce type de
code. En pseudo-code, cela donne :

SELECTIONNER
POUR x = y : Traitement 1
POUR x = z : Traitement 2
POUR x = w : Traitement 3
AUTREMENT : Traitement 4
FIN SELECTIONNER

En C, cela donne, dans le cadre de l'exemple précédent :

printf ("Donnez le numéro du jour de la semaine (0 à 6) : \n");


scanf ("%d", &iJour);
switch (iJour)
{
case 5 : printf ("\nEnfin vendredi !\n");
break;
case 6 : printf ("\nGénial, c\'est samedi !!!\n");
break;
case 1 : printf ("\nHélas, nous ne sommes que lundi !\n");
break;
case 0 : printf ("\nEnfin, un dimanche reposant !\n");
break;
default : printf ("\nJ\'attends le week-end prochain !\n");
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 41


Notes provisoires Logique et technique de programmation

10.1. Fonctionnement et remarques :


on écrit entre les parenthèses qui suivent le switch, l'expression dont il
faut vérifier la valeur
le switch passe directement au traitement associé à la valeur de
l'expression
lorsque le traitement associé à une valeur est terminé, le programme
exécute le traitement associé à la valeur suivante : il exécute les
instructions en séquence !!! => pour éviter cela, on utilise l'instruction
"break" qui force le programme à passer à la fin du bloc associé à
l'instruction switch : on passe donc à l'instruction qui suit '}'
la valeur "default" permet d'associer un traitement commun à toutes les
autres valeurs possibles de la variable pour lesquelles on n'a pas défini de
traitement particulier.
le choix de l'exécution d'un traitement se fait sur base de l'égalité avec
une valeur. Cela a pour conséquence immédiate que l'on ne peut utiliser la
structure du "switch" qu'avec un integer (valeur entière) ou un caractère
(char). On ne peut pas l'utiliser avec un float. L'utilisation d'un float
provoque l'erreur de syntaxe suivante "switch expression not integral".

10.2. Exemple 1

printf ("Donnez l\'heure courante (0 à 23) : \n");


scanf ("%d", &iHeure);
switch (iHeure)
{
case 6:
case 7:
case 8:
case 9:
printf ("Bonjour !");
break;
case 10:
case 11:
printf ("Bonne journée !");
break;
case 12:
printf ("Bon appétit !");
break;
case 13:
case 14:
case 15:
case 16:
printf ("Bon après-midi !");
break;
case 17:
case 18:
printf ("Bonne fin de journée !");
break;
case 19:
case 20:
case 21:
printf ("Bonne soirée !");
break;

Page 42 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

case 22:
case 23:
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
printf ("Bonne nuit !");
break;
default:
printf ("Heure impossible !")
}

Dans cet exemple, on peut voir qu'en n’utilisant pas systématiquement l'instruction
"break" dans tous les cas, cela nous permet d'exécuter la même instruction quand la valeur de
la variable est, par exemple, 6, 7, 8 et 9.

10.3. Exemple 2
On peut également se servir de l'instruction switch pour déterminer le nombre de jours
que contient un mois donné en tenant compte de l'année pour le mois de février.

printf ("Donnez l\'année (1600 à 2000) : \n");


scanf ("%d", &iAnnee);
printf ("Donnez le mois (1 à 12) : \n");
scanf ("%d", &iMois);
switch (iMois)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
printf("\nle %dème mois comporte toujours 31 jours", iMois);
break;
case 4:
case 6:
case 9:
case 11:
printf("\nle %dème mois comporte toujours 30 jours!", iMois);
break;
case 2:
if ((iAnnee%4) == 0 &&
((iAnnee%1000) == 0 || (iAnnee%100) != 0)))
printf ("\nLe mois de février %d comporte 29 jours\n!",
iAnnee);
else
printf ("\nLe mois de février %d comporte 28 jours\n!",
iAnnee);
break;
default:
printf ("\nNuméro de mois invalide !\n");
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 43


Notes provisoires Logique et technique de programmation

Page 44 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 7 :

La répétitive : "TANT QUE ... FAIRE ... FIN TANT QUE"

La structure répétitive permet de répéter un traitement un certain nombre de fois : tant qu’une
condition donnée est vraie. Dès qu’elle est fausse, on arrête d’effectuer le traitement.

1. EN PSEUDO-CODE :

TANT QUE (condition)


FAIRE
Traitement

FIN TANT QUE

2. ORGANIGRAMME :

non
Condition

oui
Traitement X

3. EN C :
Cette instruction en C s’exprime :

while (condition)
{
...
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 45


Notes provisoires Logique et technique de programmation

4. EXEMPLE 1 :
Nous devons écrire un programme qui permet d’afficher les nombres 1, 2, 3 et 4.

4.1. En pseudo-code :
i  1
TANT QUE (i < 5)
FAIRE afficher i
i  i + 1
FIN TANT QUE

4.2. Organigramme :

i1

non
i<5

oui
afficher i
ii+1

4.3. En C :
main ()
{
int i ;

i = 1 ;

while (i < 5)
{
printf ("%d", i);
i = i + 1;
}
}

On s’arrête quand la condition est fausse, c’est-à-dire quand i est égal à 5 !

Attention : Deux instructions importantes à ne surtout pas oublier pour le bon


! fonctionnement du programme :
 il faut initialiser la variable sur laquelle porte la condition !! En effet, si on ne
lui donne pas explicitement une valeur, on ne peut faire aucune supposition
quant à son contenu.

Page 46 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

 il faut absolument modifier le contenu d’au moins une des variables sur
lesquelles porte la condition, à l’intérieur du bloc correspondant à la boucle. En
effet, si on entre une première fois dans la boucle, c’est que la condition est
vraie. Si on ne modifie pas la valeur de la variable sur laquelle porte la
condition, la condition continuera a être vraie, et on aura une boucle infinie : le
programme va rester indéfiniment dans la boucle ! La manière de modifier le
contenu de la variable doit faire en sorte que tôt ou tard, on finira par rendre
la condition fausse.

Attention à ne jamais mettre de ‘ ;’ après la condition, tout comme dans les alternatives.
Sinon, l’ordinateur interprète le code en disant que le corps de la boucle ne contient qu’une
! seule instruction vide.
Ce scénario, si la condition est vraie, débouchera sur une boucle infinie.

5. EXEMPLE 2:
Saisir un entier compris entre 0 et 20 et calculer son carré.

5.1. En pseudo-code :
lire a
TANT QUE (a < 0 OU a > 20)
FAIRE lire a
FIN TANT QUE
afficher a * a

5.2. En C :
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

main ()
{
int a, b ;

printf ("entrez la valeur de a (entre 0 et 20)\n");


fflush (stdin) ;
scanf ("%d", &a) ;

while (a < 0 || a > 20)


{
printf ("\nEntrez une valeur entre 0 et 20 ! \n");
fflush (stdin) ;
scanf ("%d", &a) ;
}

b = (int)pow(a, 2) ; /* ou b = a * a */
printf ("\nLe carré de %d vaut %d\n", a, b) ;
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 47


Notes provisoires Logique et technique de programmation

6. EXEMPLE 3:
Saisir deux entiers a et b. Calculer la valeur de "a/b". Si b vaut 0 on donnera un
message d’erreur approprié. On recommencera l’opération autant de fois que l’utilisateur le
désire.

6.1. En pseudo-code :
c  1
TANT QUE (c == 1)
FAIRE lire a, b
SI (b != 0)
ALORS r = a / b
afficher r
SINON afficher un message d’erreur
FIN SI
lire c
FIN TANT QUE

6.2. En C :
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void main ()
{
int a, b, c ;
float r ;

system ("cls") ;
c = 1 ;
while (c == 1)
{
fflush (stdin);
printf ("Entrez le numérateur : \n");
scanf ("%d", &a);
printf ("\nEntrez le dénominateur : \n");
scanf ("%d", &b);
if (b != 0)
{
r = (float)a / b;
printf ("\n%d / %d = %f", a, b, r);
}
else
printf ("La division par 0 n’est pas permise");
printf ("\nEntrez 1 pour recommencer …") ;
fflush (stdin) ;
scanf ("%d", &c);
}
}

Page 48 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

7. EXEMPLE 4 :
Calculer la valeur de a + b, où a et b sont donnés par l’utilisateur, autant de fois que
l’utilisateur désire le faire. On arrête lorsque a et b sont égaux à 0.

7.1. En pseudo-code :
lire a et b
TANT QUE (a != 0 OU b != 0)
FAIRE r = a + b ;
afficher a, b, r
lire a et b
FIN TANT QUE

7.2. En C :
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void main ()
{
int a, b, c ;

system ("cls");
printf ("Entrez le premier nombre : \n");
fflush (stdin) ;
scanf ("%d", &a) ;
printf ("\nEntrez le deuxième nombre : \n");
fflush (stdin) ;
scanf ("%d", &b) ;
while (a != 0 || b != 0)
{
c = a + b;
printf ("\n%d + %d = %d", a, b, c);
printf ("\nEntrez deux nouvelles valeurs (0 et 0 pour
arrêter \n") ;
fflush (stdin) ;
scanf ("%d", &a) ;
fflush (stdin);
scanf ("%d", &b);
}
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 49


Notes provisoires Logique et technique de programmation

8. EXEMPLE 5 :
Saisir un entier a.
Calculer et afficher r = a * x avec x qui varie de 4 à 5 par pas de 0.1.
Par exemple, si a vaut 2 :

x r

4.0 8.0
4.1 8.2
4.2 8.4
4.3 8.6
4.4 8.8
4.5 9.0
4.6 9.2
4.7 9.4
4.8 9.6
4.9 9.8
5.0 10.0

8.1. En pseudo-code :
lire a
x = 4.0
TANT QUE (x < 5.1)
FAIRE r = a * x ;
afficher r
x = x + 0.1
FIN TANT QUE

8.2. En C :
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void main ()
{
int a;
float r, x;

system ("cls");
printf ("Entrez le multiplicateur : \n");
scanf ("%d", &a) ;
x = 4.0 ;
while (x < 5.1)
{
r = a * x;
printf ("\n%5.1f | %5.1f", x, r);
x = x + 0.1;
}
}

Page 50 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

On aurait pu essayer en utilisant la condition (x != 5.1). Au point de vue logique, il n’y a


aucune différence. Cela risque cependant de poser problème à cause des erreurs d’arrondi au
niveau du programme. En effet, les valeurs successives de x ne seront pas forcément 4.0, 4.1,
4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9 et 5.0 mais pourraient être semblables à ceci : 4.0, 4.1, 4.2,
4.3, 4.4, 4.5, 4.599999, 4.699999, 4.799999, 4.899999, 4.999999, 5.099999, 5.199999 et
ces deux dernières valeurs sont différentes de 5.1, ce qui fait que cette boucle ne
s’arrêterait jamais !!

Avec les variables de type float, il est impératif de se servir d’une condition contenant
une comparaison avec un des signes <, >, <= ou >= . Il faut éviter à tout prix == ou !=
! Par contre, avec les entiers, tous les signes peuvent bien entendu être utilisés.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 51


Notes provisoires Logique et technique de programmation

Page 52 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 8 :

La répétitive : "FAIRE ... TANT QUE ..."

1. EN PSEUDO CODE

FAIRE Traitement

TANT QUE (condition)

2. ORGANIGRAMME :

Traitement X

non
Condition

oui

3. EN C :
Cette instruction en C s’exprime :

do
{
...
} while (condition);

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 53


Notes provisoires Logique et technique de programmation

4. EXEMPLE :
Reprenons l’exemple que nous avons vu dans le cadre de la boucle "TANT QUE
(condition) FAIRE … FIN TANT QUE"

lire a
TANT QUE (a < 0 OU a > 20)
FAIRE lire a
FIN TANT QUE
afficher (a * a)

On voit que l’instruction "lire a" apparaît deux fois dans l'algorithme. La boucle "FAIRE
… TANT QUE (condition)" va nous permettre de simplifier cet algorithme :

FAIRE lire a
TANT QUE (a < 0 OU a > 20)
afficher (a * a)

C'est un exemple typique d’utilisation de la boucle "FAIRE … TANT QUE (condition)"

Page 54 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 9 :

Le type CARACTERE : char

1. CARACTERES ASCII NORMALISES


La table ASCII normalisée contient 128 caractères.
Valeur décimale : de 0 à 127
Valeur hexadécimale : de 00 à 7F
Valeur binaire : de 0000 0000 à 0111 1111

Par exemple : 'a' 97 déc 61 hex


'A' 65 déc 41 hex

2. CONSTANTE ET VARIABLE DE TYPE CARACTERE – CHAR


La constante de type char 'A', caractère précédé et suivi de ', occupe 1 octet en
mémoire et est signée. Cet octet reçoit la valeur ASCII du caractère.

La variable de type char occupe 1 octet en mémoire. Elle est déclarée de la manière
suivante :
char x; /* par défaut, char est signé */
signed char x;
unsigned char x;

char x;
4 1
x = 'A'; x représentation hexadécimale
0100 0001
x représentation binaire

valeur décimale : 65

2.1. Remarque
98 est une constante entière, 4 octets, exprimée en décimal
00 00 00 62

0x62 est une constante entière, 4 octets, exprimée en hexadécimal


00 00 00 62

'\x62' est une constante char, 1 octet, exprimée en hexadécimal


62

'b' est une constante char, 1 octet, exprimée par son caractère.
62

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 55


Notes provisoires Logique et technique de programmation

2.2. Exemples

char y;
62 62
y = 'b'; ou y = '\x62';
y constante

printf("%c %d %x", y, y, y);

cette portion de code affiche à l'écran :


b 98 62

char y;

y = 0x62; ou y = 98; 62 00 00 00 62
y constante

printf("%c %d %x", y, y, y);

cette portion de code affiche à l'écran :


b 98 62

Dans cet exemple, on assigne la valeur d'une constante de type int à une variable de
type char (char = int). Il n'y a cependant pas de warning à la compilation car il n'y a pas
de perte de digits significatifs.

int y;

y = 'b'; ou y = '\x62';
00 00 00 62 62
y constante (signée)
répétition du bit de signe 0 dans y

printf("%c %d %x", y, y, y);

cette portion de code affiche à l'écran :


b 98 62

int y;

y = 0x62; ou y = 98;
00 00 00 62 00 00 00 62
y constante (signée)

printf("%c %d %x", y, y, y);

cette portion de code affiche à l'écran :


b 98 62

Page 56 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

3. CARACTERES ASCII NON NORMALISES

3.1. Premier exemple


char c = '\x87';
printf ("%c %d %x", c, c, c);

On obtient à l'affichage :
ç -121 ffffff87

ffffff87 étant la représentation hexadécimale de l'entier –121.


La représentation d'un entier se fait par le complément à 2. Ce complément, effectué
en hexadécimal est plus simple et fournit, bien entendu, le même résultat (complément à 16).

12110 = 7916
0 00 00 00 00
-121 - 00 00 00 79
_______ _____________________

-121 10 ff ff ff 8716

Par contre si on écrit

char c = 0x87;
printf ("%c %d %x", c, c, c);

Dans ce cas, on obtient un warning à la compilation : "truncation from const int to char".

c 87  00 00 00 87

c est signé, sa valeur maximale est 127. Or, 8716 = 13510  Warning

Si on exécute le programme malgré cet avertissement, on obtient les mêmes valeurs :


ç -121 ffffff87

Une solution consiste à utiliser le type caractère non signé :

unsigned char c = 0x87;


printf ("%c %d %x", c, c, c);

On obtient à l'affichage :
ç 135 87

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 57


Notes provisoires Logique et technique de programmation

3.2. Deuxième exemple


Soit le programme suivant :

char c = 'ç';
printf ("%c %d %x", c, c, c);

On obtient à l'affichage :
þ –25 ffffffe7 00 00
- ff e7
_________

00 19 hex = 25déc

Autre possibilité :
unsigned char c = 'ç';
printf ("%c %d %x", c, c, c);

On obtient à l'affichage :
þ 231 e7 e7hex = 231déc

Pourquoi n'obtient-on pas le 'ç' ???


L'encodage des caractères ASCII non standardisés sous DOS n'est pas le même que
leur encodage sous windows. Or, quand on édite le programme et qu'on le compile, on est sous
windows, quand on l'exécute, on est dans une fenêtre DOS émulée par Windows. Le code du
'ç' dans le code source du programme n'est pas le même que son code dans la fenêtre DOS qui
exécute le programme.

Donc si on effectue l'instruction :


printf ("123ç");
On obtiendra :
123þ

4. FORMATS D'AFFICHAGE ET DE LECTURE

affichage lecture

int printf ("%d", …); scanf ("%d", &…);

float printf ("%f", …); scanf ("%f", &…);

printf ("%c", …); scanf ("%c", &…);


char
ou putchar (…); ou getchar(…);

Page 58 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

5. LECTURE D'UN CARACTERE : GETCHAR()

getchar ()

Le prototype de cette fonction est inclus dans stdio.h.


getchar requiert donc : #include <stdio.h>

getchar est compatible ANSI Win95 WinNT

Prototype de la fonction :
int getchar (void);

Cette fonction ne demande aucun paramètre.

Cette fonction retourne un entier; celui-ci représente le caractère lu ou –1 en cas


d'erreur de lecture (ou EOF qui vaut –1).

5.1. Comment fonctionne la fonction getchar() ?


La fonction explore le buffer d'entrée, stdin et en extrait le premier caractère (1
octet).
Cet octet est étendu à 4 octets par des 0 quelle que soit l'option du C (signed ou
unsigned char).

Rappel à propos du buffer d'entrée : 2 cas peuvent se présenter : soit stdin est vide,
soit il ne l'est pas.

Si stdin est vide : getchar attend un entrée au clavier terminée par un " " (enter)
Si stdin n'est pas vide : getchar extrait le premier caractère du buffer stdin.

5.2. Exemple 1
stdin est vide

int i, k;
i = getchar() b => stdin : 62 0A

getchar() extrait le caractère 'b' (62)


getchar l'étend à 4 octets : 00 00 00 62
getchar retourne cette valeur dans la variable i
le premier caractère de stdin est maintenant : (0A)

si on écrit maintenant : k = getchar();


k reçoit la valeur 00 00 00 0A et à ce moment seulement, le buffer est vide !

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 59


Notes provisoires Logique et technique de programmation

5.3. Exemple 2
char ca, cb, cc, cd, ce; /* stdin est vide */

ca = getchar(); saisie de abc => ca 'a'


cb = getchar(); => cb 'b'

cc = getchar(); => cc 'c'


cd = getchar(); => cd

ce = getchar(); /* stdin est vide, le programme attend une


nouvelle entrée de l'utilisateur */

5.4. Exemple 3
char c;
c = getchar();
printf ("%c %d %x", c, c, c);

Quand on compile, pas de warning, quand on exécute,


si on entre 'A', on obtient :
A 65 41
si on entre 'ç', on obtient :
ç -121 ffffff87 (la variable c est signée)

unsigned char c;
c = getchar();
printf ("%c %d %x", c, c, c);

Quand on compile, pas de warning, quand on exécute,


si on entre 'A', on obtient :
A 65 41
si on entre 'ç', on obtient :
ç 135 87 (la variable c est non signée)

Ici, on obtient bien le caractère 'ç' car il donné sous DOS à la fonction getchar !!

5.5. Récapitulatif
 getchar() retourne un entier qui contient :
soit le caractère saisi dans stdin, étendu à 4 octets (par des 0)
soit la valeur –1 en cas d'erreur de lecture ou s'il lit le caractère de fin
de fichier
 getchar() a pris le 1er caractère de stdin et le pointeur du buffer se place au
caractère suivant

Page 60 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

5.6. Conclusion
Si on utilise l'instruction getchar() dans un programme dans le but de lire un
caractère et de l'utiliser dans un traitement, il faut impérativement que le buffer
"stdin" soit vide.
Il faut faire attention au fait que getchar() n'est pas la seule instruction à
utiliser stdin : scanf() l'utilise également.

Pour vider le buffer, on utilise l'instruction "fflush(stdin);".

Exemple :

main()
{
int i;
char ca;

scanf ("%d", &i);


/* si on entre 123 i prend la valeur 123 */
fflush(stdin);
/* le caractère est enlevé du buffer d'entrée */
ca = getchar();
/* lecture de la valeur du caractère */
...
}

6. LECTURE D'UN CARACTERE : GETCH()

getch ()

Cette autre fonction permet également de saisir (ou lire) un caractère au clavier.

Son prototype se trouve dans le fichier d'entête "conio.h" :


getch requiert donc : #include <conio.h>

getch est compatible ANSI Win95 WinNT

Prototype de la fonction :
int getch(void);

Cette fonction ne reçoit aucun paramètre, elle retourne un entier qui contient :
 dans le byte de poids faible : le code ASCII du caractère
 dans les 3 autres bytes : 0

getch n'utilise pas stdin !

Attention, contrairement à ce qui se passe lorsque l'on utilise "getchar()", le caractère


tapé au clavier n'est pas affiché à l'écran : il n'y a pas d'"écho".

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 61


Notes provisoires Logique et technique de programmation

6.1. Exemple :
#include <stdio.h>
#include <conio.h>

main ()
{
int ia;
char ca;

ia = getch(); /* on tape ç */
printf ("%c", ia); /* affichage de ç */
printf ("%d", ia); /* affichage de 135 */
printf ("%x", ia); /* affichage de 87 */
ca = getch(); /* on tape ç */
printf ("%c", ca); /* affichage de ç */
printf ("%d", ca); /* affichage de -121 */
printf ("%x", ca); /* affichage de FF FF FF 87 */
ca = (char) getch(); /* on tape ç */
printf ("%c", ca); /* affichage de ç */
printf ("%d", ca); /* affichage de -121 */
printf ("%x", ca); /* affichage de FF FF FF 87 */
getch();
}

6.2. Conclusion
L'instruction getch() est intéressante car elle saisit un caractère immédiatement sans
que l'utilisateur ne doive appuyer sur
! Mais sa portabilité (voir chapitre suivant) est restreinte car elle ne fait pas partie de la
norme ANSI.

Page 62 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

7. AFFICHAGE D'UN CARACTERE : PUTCHAR()

int putchar (int c)


Cette fonction permet d'afficher un caractère à l'écran. Elle ne provoque pas de saut
de ligne après le caractère affiché !
Son prototype se trouve dans le fichier d'entête "stdio.h".
putchar requiert donc : #include <stdio.h>

putchar est compatible ANSI Win95 WinNT

Cette fonction reçoit comme paramètre le caractère à afficher (byte de poids faible de
l'entier).
Elle retourne un entier qui contient :
soit le code ASCII du caractère affiché à l'écran
soit –1 (EOF) pour indiquer une erreur ou une condition de fin de fichier.

7.1. Exemple :
#include <stdio.h>
#include <conio.h>

main ()
{
char ca, cb;

ca = getch(); /* on tape ç */
cb = putchar(ca); /* on affiche ç */
printf ("\n%c", cb); /* on affiche ç */
}

8. AFFICHAGE D'UN CARACTERE : PUTCH()

int putch (int c)


Cette autre fonction permet d'afficher un caractère à l'écran.
Son prototype se trouve dans le fichier d'entête "conio.h".

putch est compatible ANSI Win95 WinNT

Cette fonction reçoit comme paramètre le caractère à afficher.


Elle retourne un entier qui contient :
soit le code ASCII du caractère affiché à l'écran
soit -1 (EOF) pour indiquer une erreur ou une condition de fin de fichier.

Remarque : cette fonction putch écrit le caractère directement sur la console, sans
passer par le buffer (de la même façon que getch).

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 63


Notes provisoires Logique et technique de programmation

Si on refait l'exercice du paragraphe précédent en remplaçant les getchar par getch,


on obtiendra exactement les mêmes résultats.

Page 64 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 10 :

Compatibilité et portabilité

Il existe plusieurs compilateurs de langage C différents. On utilise actuellement au


laboratoire, le comilateur VISUAL C++ 6.0 de Microsoft.
On peut également citer le QC (Quick C), le ansi C, le C sous Unix ou le Turbo C de
Borland.

On dit souvent qu'un programme écrit en langage C peut être compilé par n'importe quel
compilateur C : ce n'est pas tout à fait exact !!

Les différents compilateurs ne sont pas entièrement compatibles, c'est-à-dire qu'ils


vont chacun reconnaître certaines instructions et ne pas reconnaître les autres : donc donner
des erreurs à la compilation.

La portabilité d'un programme (d'une instruction) indique les différents compilateurs


qui vont l'accepter.

Comment connaître la portabilité d'un instruction ?


Chercher dans un livre de référence !
Au laboratoire, on dispose du livre "Guide PSI du programmeur". Celui-ci reprend la
liste des instructions du C, donne une explication quant au fonctionnement de celles-ci et
indique leur portabilité.

Par exemple : les instructions de stdio.h sont portables sur tous les compilateurs C,
tandis que celles de conio.h ne le sont pas !
Exemple : clrscr(); => printf ("\x27]1J"); en code ansi

On ne peut jamais dire à l'avance si un programme devra être porté sur un autre
système ou pas. C'est pour cette raison qu'il faut essayer d'utiliser, lorsque c'est possible,
des instructions qui offrent la plus grande portabilité.
Mais les instructions spécifiques à un compilateur offrent parfois des facilités
intéressantes => il faut faire un choix !!

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 65


Notes provisoires Logique et technique de programmation

Page 66 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 11 :

Les tableaux

1. DEFINITIONS
Un tableau est
 un ensemble fini de variables de même type, donc
 un ensemble fini de zones mémoire de même taille

En mémoire, ces zones sont contigues.

On appellera vecteur un tableau à une dimension.

En C, pour déclarer un vecteur d'entiers par exemple :

int v[10];

Cette instruction réserve 10 * 4 octets contigus en mémoire, dont le contenu est


indéterminé.

Le nom symbolique de ces 10 * 4 octets est v :

v:

0 1 2 3 4 5 6 7 8 9

Le contenu de ces 10 cases au moment de la déclaration est indéterminé, tout comme le


contenu d'une simple variable comme nous avons vu jusqu'à présent.
L'identificateur de l'élément d'indice x est v[x].

! En C, on commence à compter à partir de 0 !!

Le premier élément du vecteur v est donc donné par v[0]


Le cinquième élément du vecteur v est v[4], etc.

Exemples :
scanf ("%d", &v[4]);
printf ("%d", v[4]);

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 67


Notes provisoires Logique et technique de programmation

2. EXEMPLES :

2.1. Premier exemple d'utilisation : comment fonctionnent les


indices

main()
{
int i, va[5], vb[6];

i = 3;
scanf ("%d", &va[i]); /* l'utilisateur donne 12 */
i = i – 3;
scanf ("%d", &vb[i]); /* l'utilisateur donne 14 */
...
/*
Il n'existe, par défaut, aucun lien entre i, va et vb.
Les seuls liens qui peuvent exister sont ceux créés
par le programmeur !!!
*/
...
va[i + 1] = 17; /* i = 0 => i + 1 = 1 */
i = 1;
vb[i] = vb[i-1] + va[i+2]; /* vb[0] + va[3] */
...
}

i 301 va 17 12 vb 14 26
0 1 2 3 4 0 1 2 3 4 5

2.2. Exemple 2 : initialisation et affichage d'un vecteur complet

main ()
{
int i, n;
int va[50];

scanf ("%d", &n); /*


* 1 <= n <= 50 : n représente le nombre
* d'éléments du vecteur va qui vont
* réellement être utilisés.
*/
/*
* Pour saisir une valeur et la placer dans va[i], il faut
* écrire "scanf("%d", &va[i]);"
* => pour mettre une valeur dans les n premiers éléments
* du vecteurs va, il faut utiliser une boucle
*/

i = 0;
while (i < n)
{
scanf ("%d", &va[i]);

Page 68 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

i++;
}
...

/*
* Pour afficher le contenu des n cases actives de va, il
* suffit de remplacer scanf par printf dans la boucle.
* Il ne faut pas oublier non plus de remettre la variable
* compteur à 0 !!!
*/

i = 0;
while (i < n)
{
printf ("%d", va[i]);
i++;
}
...
}

2.3. Exemple 3
Soit va, un vecteur d'entiers
Lire n : nombre d'éléments utilisés dans va
Lire les n éléments de va
Afficher les n éléments de va
Calculer la somme de n éléments de va

Lire, afficher et calculer : 3 boucles différentes !?!

! Pourquoi ne pas mélanger ces 3 actions dans la même boucle ?


Parce que ces 3 actions seront, plus tard, isolées dans une fonction chacune. Il est donc
préférable de déjà prendre l'habitude de séparer les instructions liées à des actions
différentes

Pseudo-code :

res = 0;
i = 0;

lire n;
TANT QUE (i < n)
FAIRE
lire va[i];
i++;
FIN TANT QUE
i = 0;
TANT QUE (i < n)
FAIRE
afficher va[i];
i++;
FIN TANT QUE
i = 0;
TANT QUE (i < n)
FAIRE
res = res + va[i];

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 69


Notes provisoires Logique et technique de programmation

i++;
FIN TANT QUE
afficher res;

3. LA BOUCLE "FOR …"


Dans les exemples que l'on a vu jusqu'à présent, on faisait typiquement :
Lire n
lire n éléments du vecteur va
Effectuer un traitement sur les éléments du vecteur va
afficher les n éléments de va.

C'est-à-dire :
Initialisation
int va[40];
int n, i;
Condition
scanf ("%d", &n); /* 5 ↵ */
Opération de fin de boucle
i = 0; n=5
while (i < n)
{
traitement; …
i = i + 1; 0 1 2 3 4 5 6 7 8 9 39
}
avec 1 <= n <= 40

Autre méthode : lorsque l'on travaille avec des vecteurs, il est plus facile d'utiliser
l'instruction for. La logique de cette instruction est exactement la même que celle de
l'instruction "while", elles seront donc traduites de la même manière en pseudo-code, par
contre, la syntaxe est, elle, différente :

for (i = 0; i < n; i = i+1) Initialisation


{
traitement
Condition
}

Opération de fin de boucle

On voit bien dans cet exemple, que l'on retrouve les mêmes éléments dans les deux
types de boucles : initialisation au départ, condition pour savoir si on doit encore effectuer le
traitement de la boucle, incrémentation de la variable i en fin de boucle !

Page 70 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Ordinogramme de la boucle for :

Initialisation

non
condition

oui

Traitement

Opération(s) de fin
de boucle

exemple :

i=0

non
i<n

oui

lire va[i]

i ++

Le raisonnement est le même pour la boucle "for" que pour la boucle "while" (TANT QUE
…). Sa syntaxe la rend seulement plus facile à écrire et à lire dans le cas des tableaux car
toute l'information concernant la boucle est placée sur la même ligne.

4. INCREMENTATION ET DECREMENTATION
Quand on écrit "i = i + 1;" on augmente la valeur contenue dans la variable 'i' d'une unité
=> on l'incrémente d'une unité.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 71


Notes provisoires Logique et technique de programmation

De même, quand on écrit "i = i – 1;", on décrémente la valeur de 'i'.


Il existe en C une instruction plus courte qui permet d'obtenir le même effet :
i++ : post-incrémentation (après)
++i : pré-incrémentation (avant)

Exemples :
a = ++b;
1) la valeur de b est incrémentée
2) a reçoit la nouvelle valeur de b
Si b = 4 => a = 5 et b = 5 après cette instruction !

a = b++;
1) a reçoit la valeur de b
2) la valeur de b est incrémentée
si b = 4 => a = 4 et b = 5 après cette instruction !

j = ++i + 5;
1) i = i + 1
2) j = i + 5
si i = 3 = > j = 9 et i = 4 après cette instruction !

j = i++ + 5;
1) j = i + 5
2) i = i + 1
si i = 3 => j = 8 et i = 4 après cette instruction !

De même, il existe la décrémentation :


i-- : post-décrémentation (après)
--i : pré-décrémentation (avant)

On peut également se servir de la post ou pré-incrémentation dans les boucles :


Exemple :

main ()
{
int va[40];
int i, n;

fflush (stdin);
scanf ("%d", &n);
for (i = 0; i < n; i++)
{
scanf ("%d", &va[i]);
}
...
}

Page 72 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

5. ILLUSTRATION WHILE, FOR, INCRÉMENTATION …

Soit va un vecteur d'entiers


Lire n
Lire les n éléments de va
Afficher les n éléments de va
Permuter les n éléments de va
Afficher les n éléments de va

Exemple :
n=5 va : 4 8 3 0 7
=> 7 0 3 8 4

n=4 va : 1 2 3 4
=> 4 3 2 1

Pseudo-code :

Lire n;

i = 0;
TANT QUE (i < n)
FAIRE
lire va[i];
i++;
FIN TANT QUE

k = 0;
i = n – 1;
TANT QUE (k < n/2)
FAIRE
ech = va[k];
va[k] = va[i];
va[i] = ech;
k++;
i--;
FIN TANT QUE

i = 0;
TANT QUE (i < n)
FAIRE
afficher va[i];
i++;
FIN TANT QUE

Ce qui donne en C :

main ()
{
int i, n, np, inter;
int v[40];

scanf ("%d", &n);

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 73


Notes provisoires Logique et technique de programmation

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


{
scanf ("%d", &v[i]);
}

np = n/2;

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


{
inter = va[i];
va[i] = va[n – 1 – i];
va [n – 1 – i] = inter;
}

...
}

autre solution pour la dernière boucle de cette exemple :

for (i = 0, j = n-1; i < np; i++, j--)


{
inter = va[i];
va[i] = va[j];
va [j] = inter;
}

On peut voir dans cette exemple que l'on peut avoir plusieurs instructions dans la
première et la troisième partie de l'instruction for. Dans ce cas, ces instructions sont
séparées par des ','.

Remarque : autre méthode pour permuter 2 entiers a et b :


a = a – b;
b = b + a;
a = b – a;

Page 74 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

6. LES TABLEAUX A N DIMENSIONS


Un vecteur est un tableau à une dimension :

Par exemple :

v[10] => v
0 1 2 3 4 5 6 7 8 9

Pour accéder aux différentes cases de v : v[1], v[6], …

En C, on peut également avoir des tableaux qui comportent plus d'une dimension.
Pour un tableau à deux dimensions, on peut également utiliser le terme de "matrice".

Exemple : tab[4][5]

nom du tableau nombre de colonnes

nombre de lignes

tab :
0 1 2 3 4
0
1 7
2
3

Tab[1][2] = 7

Indice de colonne
Indice de ligne

6.1. Disposition des données en mémoire


Int tab[4][5];

ligne 0 ligne 1 ligne 2


tab …
[0][0] [0][1] [0][2] [0][3] [0][4] [1][0] [1][1] [1][2] [1][3] [1][4] [2][0] [2][1] [2][2] [2][3] [2][4] …

Une matrice est réservée en mémoire ligne par ligne.

6.2. Exemple
int tab[4][10];

 Cette déclaration réserve 4 * 10 * 4 octets contigus => 160 octets contigus


en mémoire

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 75


Notes provisoires Logique et technique de programmation

 Le nom symbolique permettant d'accéder à ces 160 octets est tab


 Le contenu des 40 "cases" ou éléments de tab au moment de la déclaration
est inconnu
 L'identificateur de l'élément de la ligne X et de la colonne Y est tab[X][Y]

6.3. Exemple 2 : initialisation et affichage d’un tableau à deux


dimensions
Initialiser et afficher l’entièreté d’un tableau à 2 dimensions :

lire n ; /* nombre de lignes utiles */


lire m ; /* nombre de colonnes utiles */

i <- 0 ;
TANT QUE i < n FAIRE
j <- 0
TANT QUE j < m FAIRE
lire t[i][j]
j <- j + 1
FIN TANT QUE
i <- i + 1
FIN TANT QUE

i <- 0 ;
TANT QUE i < n FAIRE
j <- 0
TANT QUE j < m FAIRE
afficher t[i][j]
j <- j + 1
FIN TANT QUE
i <- i + 1
FIN TANT QUE

6.4. Tableau à 3 dimensions


Pour déclarer un tableau à 3 dimensions, on utilisera une instruction du type :

int tab[5][4][10];

On peut également utiliser des tableaux à plus de trois dimensions.


Le nombre de dimensions n'est pas directement limité, mais sera limité par le place
maximale que pourront occuper les variables déclarées dans le programme. Sous DOS, cette
taille est souvent limitée à 64kb, pour toutes les variables du programme.

Page 76 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 12 :

Adresses et pointeurs

1. LES ADRESSES
On a vu que le nom d'une variable est un nom symbolique donné à une zone de la
mémoire.
Par exemple, si on écrit :
int a; /* 4 octets */

a = 2; a 2

a n'est qu'un nom que le programmeur a décidé de donner à une zone de la mémoire.
L'ordinateur, quant à lui, accède à cette zone grâce à son adresse.

a 2
x1B00

En fait, il s'agit de l'adresse du premier octet de la variable. Chaque octet de la


mémoire est repéré par une adresse :
2

x1AFC x1B00 x1B04

Comment connaître l'adresse du premier octet d'une variable ?


=> opérateur : &

exemple : &a renvoie l'adresse du premier octet de la variable a

rappel : scanf ("%d", &a);


&a indique à scanf à quelle adresse il doit placer la valeur entrée au clavier !

2. LES POINTEURS
Rappel : lorsque l'on écrit
float r; /* 4 octets */
on déclare que l'on va utiliser une variable de type float
?
r: contenu indéterminé !

La variable r peut recevoir une valeur de type float. Exemple : 25.387

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 77


Notes provisoires Logique et technique de programmation

Un pointeur est un type de variable qui occupe 4 octets et qui peut recevoir comme
valeur une adresse.

Remarques importantes :
 la taille d'une variable de type pointeur est toujours la même (4 octets), que
ce pointeur contienne l'adresse d'un short, d'un int, d'un float ou d'un
double.
 sur d'autres systèmes (OS et HW), la taille d'un pointeur peut être
différente de 4 octets, MAIS sur ce système, tous les pointeurs auront la
même taille.
exemple :
float *pv;
/* on déclare une variable de type pointeur de float */
int *pa;
/ * on déclare une variable de type pointeur de int */

3. EXEMPLE
14.0
11.0
main () 4.0 7.0
{
v w
float v, w ;
float *pv, *pa; pv pa
pv = &v; /* pv reçoit l’adresse de v */
/* on dit également que pv pointe sur v */

/*
* Comment modifier la valeur contenue dans la variable v ? :
* soit v = … soit *pv = … C’est la même chose !!!
* *pv permet d’accéder à la variable dont l'adresse est
* contenue dans pv ou à la variable pointée par pv
*/

*pv = 4.0 ;
pa = &w ;
scanf ("%f", &w) ; /* ou scanf("%f", pa) ; */
/* l’utilisateur entre 7.0 ↵ */
v = *pv + *pa ; /* v = 4.0 + 7.0 */
pv = pa; /* pv reçoit l’adresse contenue dans pa
* => pv contient maintenant l’adresse de la variable w
*/
v = *pv + *pa ; /* v = 7.0 + 7.0 = 14.0 */
}

4. RÉSUMÉ

short i; i => 2 octets


short *pi;
pi = &i; pi => 4 octets

Page 78 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

=> pour accéder au contenu de i, on peut utiliser soit i, soit *pi

type associé à i => short ............................................................................................ short


pi => adresse d’un short, donc pointeur sur un short ............ short *
*pi => short ........................................................................................... short
&i => adresse d’un short, donc pointeur sur un short ............ short *

5. EXEMPLES

5.1. Exemple 1 :

char c ; c ‘A’
char *pc ; ? ‘B’

pc ? ? ? ?
c = 'A' ; Adresse pointée est indéterminée au départ !!
*pc = 'B' ;
/*
* Attention, cette instruction va écrire n’importe où en
* mémoire avec le risque d’endommager d’autres données, voire
* le programme lui-même !
*/

Attention, comme pour toute variable, il ne faut certainement pas oublier


d’initialiser le pointeur à la bonne adresse à l’aide d’une instruction du type pc = &c,
! avant de s’en servir !!!

5.2. Exemple 2 :

char *pa, *pb, ca, cb ;

pa = &ca ; ca ‘A’ cb ‘B’


pb = &cb ; adr 200 adr 252

*pa = 'A' ;
*pb = 'B' ; 200 252
pa pb
if (*pa == *pb)
/*
* On compare le contenu des addresses pointées,
* donc ici, on compare 'A' avec 'B' !!
*/
{
...
}
...
if (pa == pb)
/*
* ici, on compare les addresses elles-mêmes !!
* donc ici, on compare 200 avec 252 !!

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 79


Notes provisoires Logique et technique de programmation

*/
{
...
}

5.3. Exemple 3 :

char *pc, ca;


float *pf, fb; ca fb
adr 200 adr 252
pc = &ca;
pf = &fb;

pc 200
pf 252
if (pc == pf)
...
/*
* comparaison de 2 adresses qui n’ont pas le même
* type
* => on doit forcer la comparaison en faisant un
* casting !!!
*/

if ((float *)pc == pf)


...

6. LE TYPE DES POINTEURS !


Tous les pointeurs occupent 4 octets, alors pourquoi doit-on préciser le type de la
variable pointée ?

int i ;
int *pi ; 4
75
float f; i 3
float *pf; adr 100

i = 3; 104
f = 5.0;
pi 100
i++;
pi = &i;
pi = pi + 1; /* ou pi++ */
/*
* pi est incrémenté d’une unité, c’est-à-dire de 4
* puisque pi est un pointeur d’entiers
* et qu’un entier occuper 4 octets !
*/ f 5.0
*pi = 75 ;
pf = &f ;
pf ++ ; pf
/*
* pf est incrémenté de 4 puisque pf est un pointeur de
* float !
*/

Page 80 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Quel est l’intérêt d’incrémenter un pointeur ??

6.1. Exemple 1 :

float va [10] ;
float *pfa ; va 37.25 20.11
Adr 100 0 1 2 3 4 5 6 7 8 9
pfa = &va[2] ;
*pfa = 37.25 ; 108 112
pfa
pfa++ ;
*pfa = 20.11 ;

6.2. Exemple 2 :

v 20
int v[10];
Adr 200 0 1 2 3 4 5 6 7 8 9
int *pv;

pv = &v[0] ; 0204
*pv = 20 ; 0200
pv = pv + 1; pv

C’est la même chose qu’en assembleur ! :

MOV BX,offset v
MOV [BX],20 => adressage indirect
ADD BX,4 => car un integer prend 4 octets

7. EXEMPLES

7.1. Premier exemple


Reprenons l'exemple donné précédemment dans lequel on cherche à inverser l'ordre des
éléments du vecteur va.
Rappel du programme écrit :

main ()
{
int i, n, np, inter;
int v[40];

scanf ("%d", &n);


for (i = 0; i < n; i++)
{
scanf ("%d", &v[i]);
}

np = n/2;

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 81


Notes provisoires Logique et technique de programmation

for (i = 0, j = n-1; i < np; i++, j--)


{
inter = va[i];
va[i] = va[j];
va [j] = inter;
}
...
}

On va maintenant réécrire ce programme en se servant de pointeurs pour accéder aux


différents éléments de va :

main ()
{
int i, np, *pa, *pb, inter;
int v[40];

scanf ("%d", &n);


np = n/2;

pa = &v[0];
pb = &v[n-1];

while (pa <= pb)


{
scanf ("%d", pa);
pa++;
}

i = 0;
pa = &v[0];
while (i < np)
{
inter = *pa;
*pa = *pb;
*pb = inter;
pa++;
pb--;
i++;
}
}

Autre possibilité pour la dernière boucle de ce programme :

while (pa < pb)


/*
* On n'a plus besoin de la
* variable np
*/
{
inter = *pa;
*pa = *pb;
*pb = inter;
pa++;
pb--;
}

Page 82 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Encore une autre possibilité :

for (pa = &v[0], pb = &v[n-1];


pa < pb;
pa++, pb--)
{
inter = *pa;
*pa = *pb;
*pb = inter;
}

7.2. Deuxième exemple


Soit le tableau ta de n lignes et m colonnes
Calculer la somme des éléments de chaque ligne
Calculer la somme des éléments de chaque colonne

Pseudo-code utilisant les indices du tableau :

/*
* somme des éléments de chaque ligne
* en utilisant boucle POUR
*/
POUR (i variant de 0 à n-1 par pas de 1)FAIRE
total = 0
POUR (j variant de 0 à m-1 par pas de 1) FAIRE
total = total + ta[i][j]
FIN POUR
afficher total
FIN POUR

/*
* somme des éléments de chaque colonne
* en utilisant boucle TANT QUE …
*/
j = 0
TANT QUE (j < m) FAIRE
total = 0
i = 0
TANT QUE (i < n) FAIRE
total = total + ta[i][j]
i = i + 1
FIN TANT QUE
afficher total
j = j + 1
FIN TANT QUE

Un tableau tel que int ta[5][9] est implanté en mémoire ligne par ligne :

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 83


Notes provisoires Logique et technique de programmation

ligne 0 ligne 1 ligne 2 ligne 3 ligne 4

ta[0][0] ta[1][0] ta[2][0] ...

pa = &ta[0][0]; /* pa contient l'adresse de ta[0][0] */


pa = pa + 9; /* pa contient l'adresse de ta[1][0] */
pa = pa + 9; /* pa contient l'adresse de ta[2][0] */

int ta[5][9] ;

0 A/D E
1 B
2
3
4 C
0 1 2 3 4 5 6 7 8

A : p = adresse de ta[0][0]
B :p = p + 9 (m = 9)
C : … p = adresse de ta[4][0]
D :p = p – 5 * 9 5 = n; 9 = m
=> retour à la première case de la colonne 0
E :p = p + 1 => passage à la première case de la colonne suivante !

Pseudo-code utilisant les pointeurs :

/*
* somme des éléments de chaque ligne
* en utilisant boucle POUR
*/
p = adresse de ta[0][0]
POUR (i variant de 0 à n-1 par pas de 1)FAIRE
total = 0
POUR (j variant de 0 à m-1 par pas de 1) FAIRE
total = total + *p
p++
FIN POUR
afficher total
FIN POUR

/*
* somme des éléments de chaque colonne
* en utilisant boucle TANT QUE …
*/
j = 0
TANT QUE (j < m) FAIRE
p = adresse de ta[0][j]
total = 0
i = 0
TANT QUE (i < n) FAIRE

Page 84 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

total = total + *p
p = p + m
i = i + 1
FIN TANT QUE
afficher total
j = j + 1
FIN TANT QUE

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 85


Notes provisoires Logique et technique de programmation

Page 86 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 13 :

Les fonctions

1. UN PREMIER EXEMPLE

void message1 (void) ;


void message2 (void) ; prototype des fonctions message1 et
message2.
main () Chaque prototype donne :
{ - le nom de la fonction
printf ("1\n") ;
message1() ; - le type des paramètres qu’elle reçoit
printf ("2\n") ; (ici void)
message2() ; - le type de la valeur retournée
printf ("3\n") ; (ici void)
}

void message1()
{
printf ("Hello\n") ;
}

void message2()
{
printf ("Salut \n") ;
}

Lors de l’exécution de ce programme, on obtiendra à l’écran :

1
Hello
2
Salut
3

2. LE PROTOTYPE DE LA FONCTION
Le prototype de la fonction est écrit au début du fichier, avant la fonction main().

Le prototype de la fonction est utilisé par le compilateur pour vérifier le bon usage
de la fonction, c’est-à-dire si les appels à cette fonction sont corrects : bon passage de
paramètre(s) et bonne utilisation de la valeur de retour, s’il y en a !

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 87


Notes provisoires Logique et technique de programmation

3. COMMENTAIRE D’EN-TETE

/***********************************************************/
/* INPUT : description des paramètres d’entrée */
/* PROCESS : description de la tâche effectuée par la fct */
/* OUTPUT : description de la valeur retournée */
/***********************************************************/

4. FONCTION QUI REÇOIT UN PARAMETRE

Exemple :

void erreur (int) ;


void bravo (void);

main()
{
int n ;

printf ("4 + 3 = ?\n") ;


scanf ("%d", &n) ;
if (n != 7)
erreur (7) ;
else
bravo();

printf ("\n4 * 3 = ?\n");


scanf ("%d", &n);
if (n != 12)
erreur(12) ;
else
bravo();
}

/**************************************************/
/* INPUT : entier i : bon résultat du calcul */
/* à afficher */
/* PROCESS : affiche le bon résultat */
/* OUTPUT : / */
/**************************************************/
void erreur (int i)
{
printf ("\nla réponse correcte est : %d \n", i) ;
}

/**************************************************/
/* INPUT : / */
/* PROCESS : affiche un message de félicitation */
/* OUTPUT : / */
/**************************************************/
void bravo(void)
{
printf ("\nBravo, votre réponse est correcte\n");
}

Page 88 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Dans les appels de la fonction “erreur()”, les valeurs 7 et 12 sont les paramètres
effectifs, tandis que i, dans la fonction est un paramètre fictif.

Fonctionnement :
affichage de 4+3=?
si l’utilisateur entre 15
affichage de la réponse correcte est 7
affichage de 4*3=?
si l’utilisateur entre 12
affichage de Bravo, votre réponse est correcte

Main, erreur et bravo sont trois fonctions. Main et erreur possèdent chacune des
variables locales qui ne sont connues que au sein de celles-ci :

main erreur
n i

5. PASSAGE DE PARAMETRE(S)
Exemple :

void affiche(int) ;

main ()
{
int i ;
Ce qui apparaît à l’écran
i = 5 ;
affiche (2) ; val = 2
affiche (i) ; val = 5
affiche (i * 3 + 4) ; val = 19
affiche (2.3) ; val = 2
affiche (i / 2) ; val = 2
}

void affiche (int n)


{
printf ("val = %d\n", n);
}

Dans cet exemple, à chaque appel de la fonction "affiche", la fonction main lui passe un
et un seul paramètre.
Peu importe s’il s’agit :
- d’une constante
- d’une variable
- d’une expression
La conversion est même effectuée implicitement si nécessaire (2.3  2).

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 89


Notes provisoires Logique et technique de programmation

5.1. Exemple 1

void afficheProduit(int, float) ;

main ()
{
float x=2.7;
Ce qui apparaît à l’écran
afficheProduit (5, x) ;
printf ("fin\n") ; 5 * 2.7 = 13.5
} fin

/********************************************/
/* input : int i, float n (normalement, le */
/* commentaire doit être plus parlant) */
/* process : calcule et affiche le produit */
/* de deux nombres : un entier et */
/* un réel */
/* output : / */
/********************************************/
void afficheProduit (int i, float n)
{
printf ("%d * %f = %f\n", i, n, i*n);
}

La fonction reçoit 2 paramètres : un entier et un réel et ne retourne aucune valeur. 5


et x sont les arguments effectifs.

5.2. Exemple 2

void Fct(int) ;

main ()
{
int i=4; i 4

Fct (i + 3) ;
Ce qui apparaît à l’écran
printf ("i = %d\n", i);
}
i = 4
void Fct (int j)
{
int i;
10
i = j + 1; j 7 i 8
j = j + 3;
}

Attention, dans cet exemple, la variable i dont le contenu est modifié dans Fct, (i = j
+ 1;), est une variable locale à la fonction Fct(), elle ne correspond pas à la variable i de la
fonction main : il s’agit d’un autre emplacement mémoire.

Page 90 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

5.3. Exemple 3 (contient une erreur)

void Fct(int) ;

main ()
{
int i, j; i j

i = 4;
j = 3;
f(i);
}

void Fct (int n)


{
int k;

k = i; n k
}

erreur de syntaxe : undefined i !!!

La variable i de la fonction main n’est pas connue dans les autres fonctions

6. VALEUR DE RETOUR
Dans l'exemple suivant, la fonction retourne une valeur :

int triple(int) ;

main ()
{ 4 12
int i = 4, j; i j

j = triple (i);
printf ("j = %d\n", j);
}

int triple (int n)


{
int k;
4 12
k = n * 3; n k
return (k);
}

L'évaluation de triple(i) fournit un entier qui est assigné à la variable j :


1° : évaluation de l'expression => fournit le résultat
2° : assignation : la variable reçoit le résultat

return : - retourne une valeur


- correspond à la fin logique de la fonction : on quitte cette fonction

Dans une fonction, on peut trouver 0, 1 ou plusieurs "return".

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 91


Notes provisoires Logique et technique de programmation

6.1. Exercice
Saisir 2 entiers a et b
Calculer soit r = a + b si a > b A réaliser avec une
soit r = a + 3 * b si a <= b fonction
afficher a, b, r

int calcul(int, int) ;

main ()
{
int a, b;

scanf ("%d", &a); /* l'utilisateur entre 4 */


scanf ("%d", &b); /* l'utilisateur entre 6 */
printf ("%d %d %d", a, b, calcul(a,b)) ;
} Ce qui apparaît à l’écran

4 6 22
/********************************************/
/* input : int x, int y : les deux valeurs */
/* utilisées dans le calcul */
/* process : effectue une opération en */
/* fonction des valeurs de x et y */
/* output : le résultat de l'opération */
/* effectuée */
/********************************************/
int calcul (int x, int y)
{
int w;

if (x > y)
w = x + y;
else
w = x + 3 * y;

return (w);
}

OU

int calcul (int x, int y)


{
if (x > y)
return (x + y);
else
return (x + 3 * y);
}

Conclusions :
 une fonction a un et un seul point d'entrée
 une fonction a un ou plusieurs points de sortie

Dans le cas où on définit plusieurs points de sortie, on ne respecte pas le


programmation structurée. Même si le compilateur accepte cette syntaxe, il est
préférable de ne pas l'utiliser.

Page 92 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

6.2. Deuxième exemple


Casting dans le passage des paramètres et la valeur de retour.

float moitie(int);

main ()
{
int i = 4;
float x;

x = moitie (i + 3);

printf ("La moitié de %d vaut : %f\n", i, x) ;


}

/********************************************/
/* input : int n : nombre dont on doit */
/* calculer la moitié */
/* process : calcule la moitié du nombre */
/* donné */
/* output : le résultat de la division */
/********************************************/
float moitie (int n)
{
return (n / 2); main : i 4 x 3.0
}
moitie : n 7 => n/2 = 3

/*
* Dans cette première solution, le résultat donné
* sera toujours un entier : casting
* => meilleures solutions ci-dessous :
*/

float moitie (int n) main : i 4 x 3.5


{
return ((float) n / 2); moitie : n 7 => n/2 = 7.0 / 2 = 3.5
}

OU

float moitie (int n) main : i 4 x 3.5


{ 7
moitie : n => n/2 = 7 / 2.0 = 3.5
return (n / 2.0);
}

Attention, "warnings" possibles en fonction du compilateur utilisé.

7. LES VARIABLES GLOBALES

7.1. Exemple :

void fct (int); 3


4 5
int i = 4; i

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 93


Notes provisoires Logique et technique de programmation

main ()
{
int j; Ce qui apparaît à l’écran
i = 5;
3
fct (3);

printf("%d\n", i);
}

void fct (int n)


{ 3
i = n; n
}

7.2. Définitions
Une variable locale est définie dans une fonction et connue uniquement dans
cette fonction (stack segment).
Une variable globale est définie hors de toute fonction et est connue de toutes
les fonctions (data segment).

Dans l'exemple précédent, quand on manipule la variable i dans la fonction fct ou


dans la fonction main, on manipule la variable globale i.

7.3. Exemple
void fct (int);

int i = 4; i 4

main ()
{
int i; i 5

i = 5;
fct (i + 1); Ce qui apparaît à l’écran

printf("i = %d\n", i); i = 6


} i = 5
6
void fct (int i) i
{
printf("i = %d\n", i);
}

La variable globale i n'est accessible dans une fonction que si cette fonction ne
! contient pas de variable locale i .

Il faut éviter d'utiliser les variables globales :


 on risque la confusion entre les variables locales et globales qui portent le
! même nom,
 on risque d'utiliser les variables globales à mauvais escient

Page 94 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Il est de loin préférable d'utiliser au maximum les variables locales et donner aux
fonctions toutes les données dont elles ont besoin au moyen des paramètres. Elles
peuvent également utiliser la valeur de retour pour communiquer un résultat à la
fonction appelante.

8. UNE FONCTION PEUT APPELER UNE AUTRE FONCTION


Le code contenu dans une fonction peut contenir exactement le même type
d'instructions que celui que l'on peut écrire dans la fonction main().
En particulier, une fonction peut donc contenir des appels à d'autres fonctions.

8.1. Exemple :

int pgcd (int, int);

main ()
{
int a, b, r;

scanf ("%d", &a);


scanf ("%d", &b);

r = pgcd (a, b);


printf("le PGCD de %d et %d est %d\n", a, b, r);
}

/********************************************/
/* input : x et y deux entiers positifs */
/* process : calcule le PGCD des 2 nombres */
/* output : le PGCD */
/********************************************/

int pgcd (int x, int y)


{
while (x != y)
{
if (max(x,y) == x)
x = x – y;
else
y = y – x;
}
/* quand on sort de la boucle, x == y == pgcd */
return (x);
}

/********************************************/
/* input : x et y deux entiers positifs */
/* process : détermine le max des 2 nb */
/* output : le maximum */
/********************************************/

int max (int x, int y)


{
int imax;

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 95


Notes provisoires Logique et technique de programmation

if (x > y)
imax = x;
else
imax = y;

return imax;
}

Dans cet exemple, la fonction pgcd fait appel à la fonction max pour déterminer quel est
le plus grand des deux nombres.
Remarquons également la manière dont la valeur de retour de la fonction max est
utilisée dans une condition de la fonction pgcd.

9. LES FONCTIONS ET LES POINTEURS

9.1. Rappel à propos des pointeurs

int i; i => 4 octets


int *pi;
pi = &i; pi => 4 octets

=> pour accéder au contenu de i, on peut utiliser soit i, soit *pi

type associé à i => int


pi => adresse d’un entier, donc pointeur sur un int, int *
*pi => int
&i => adresse d’un entier, donc pointeur sur un int, int *

=> i = 20; OU *pi = 20; ces deux instructions sont équivalentes

Page 96 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

9.2. Exemple d'utilisation des pointeurs dans les fonctions

int triple(int *);


2 6
i j
main () adr 1B00 adr 2100
{
int i, j;

i = 2;
j = triple (&i);

printf("le triple de %d vaut %d\n", i, j);


}

int triple (int *p)


{ 1B00 2
int i; p i
adr 1200 adr 1300
i = *p;
return (3 * i);
}
Ce qui apparaît à l’écran

Le triple de 2 vaut 6

On peut voir sur cet exemple que cela fonctionne également correctement quand
on utilise des pointeurs comme paramètres.

Mais où est l'intérêt de passer l'adresse d'une variable à une fonction ?

9.3. Exemple

void fct (int *);


2 5
i j
main () adr 1B00 adr 2100
{
int i = 2, j;

fct (&j);

printf("i = %d\nj = %d\n", i, j);


}
Ce qui apparaît à l’écran
void fct (int *p)
{ 2100 i = 2
*p = 5; p
adr 1200
j = 5
}

Lorsque l'on passe l'adresse d'une variable à une fonction, on peut modifier la
valeur de cette variable

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 97


Notes provisoires Logique et technique de programmation

Cela fonctionne bien, mais on aurait pu tout aussi bien retourner une valeur au
moyen d'un "return".

Mais, si l'on désire modifier la valeur de plusieurs variables au sein de la


fonction, on est obligé d'utiliser les pointeurs !
! Une fonction ne peut retourner qu'une seule valeur !

9.4. Nouvel exemple

void permut (int *, int *);


3 2
i 2 j 3
main () adr A100 adr A120
{
int i = 2, j = 3;

printf("i = %d\tj = %d\n", i, j);

permut (&i, &j);

printf("i = %d\tj = %d\n", i, j);


}

void permut (int *a, int *b)


{ A100 A102
int inter; a b
adr 1200 adr 12CC
inter = *a;
*a = *b; 2
Ce qui apparaît à l’écran
*b = inter; inter
}
i = 2 j = 3
i = 3 j = 2

9.5. Exemple avec un vecteur

void LireVect (int, int *);


void AfficherVect (int, int *);

main ()
{
int v[50], n;

scanf ("%d", &n);

/* lire les n éléments du vecteur */


LireVect (n, &v[0]);

/* afficher les n éléments du vecteur */


AfficherVect (n, &v[0]);
}

Page 98 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

/************************************************/
/* input : nbr : nombre d'éléments ds le vect */
/* pt : adresse du vecteur */
/* process : lit nbr éléments et les place dans */
/* le vecteur dont l'adresse est donnée */
/* output : / */
/************************************************/
void LireVect (int nbr, int *pt)
{
int i;

for (i = 0 ; i < nbr ; i++, pt++)


{
scanf ("%d", pt);
}
}

/************************************************/
/* input : nbr : nombre d'éléments ds le vect */
/* pt : adresse du vecteur */
/* process : affiche les nbr éléments du vecteur*/
/* output : / */
/************************************************/
void AfficherVect (int nbr, int *pt)
{
int i;

for (i = 0 ; i < nbr ; i++, pt++)


{
printf ("%d ", *pt);
}
}

On n'a pas besoin de passer tout le vecteur !


On passe uniquement l'adresse du premier élément du vecteur et le nombre
d'éléments utilisés. A partir de ces deux informations, on peut accéder à tous les
éléments du vecteur.

10. RAPPEL CONCERNANT LES POINTEURS


Les pointeurs et l'incrémentation/décrémentation.

int v[5] = {10, 20, 30, 40, 50}

v 10 20 30 40 50
0 1 2 3 4

int x, *p;

p = &v[0]; idem : le nom du vecteur ou d'un tableau représente


p = v; l'adresse de son premier élément

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 99


Notes provisoires Logique et technique de programmation

1er exemple x = (*p)++ post-incrémentation =>


après l'assignation
x  *p ; (*p)++
11
x 10 v[0]
10
11
2ème exemple x = *p++; 1. assignation : x = *p => x
2. incrémentation de p => p pointe sur
v[1]

3ème exemple x = * ++p; 1. incrémentation de p => p pointe sur


l'élément suivant : sur v[2]
2. assignation : x = *p => x 30

4ème exemple x = *(p++); 1. assignation : x = *p => x 30


2. incrémentation de p => p pointe sur
l'élément suivant : sur v[3]

Remarque : les exemples sont exécutés séquentiellement.


L'opérateur * est prioritaire sur l'opérateur +

10.1. Exemple

main ()
{ v 10 20 30 40 50
int v[5];
int x, *pa; 0 1 2 3 4

pa = &v[0]; /* pa pointe vers le 1er élément de v */


x = *pa; /* x reçoit la valeur de v[0] */
pa++ ; /* pa pointe sur v[1] */
x = *pa; /* x reçoit 20 */

x = *pa + 1; /* x reçoit 21 */
x = *(pa + 1); /* pa pointe sur v[1] */
/* pa + 1 pointe sur v[2] MAIS pa n'est
pas modifié !!! */
/* x reçoit 30 (contenu de v[2]) */

/*
* Ces deux instructions sont différentes des
* deux suivantes !!!
*/

x = *(pa++); /* x reçoit le contenu de v[1] */


x = *pa++; /* et pa pointe sur v[2] */
}

Page 100 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

10.2. Exercice
Rechercher l'élément le plus grand (le maximum) du vecteur.
Principe :

ind_max = 0;
for (i = 1 ; i < n ; i++)
{
if (v[i] > v[ind_max])
ind_max = i;
}

On pourrait écrire ce code tel quel dans la fonction main, mais on demande
d'écrire une fonction qui recherche l'élément maximum d'un vecteur et retourne un
pointeur sur cet élément.
Cette fonction ne reçoit que l'adresse du début du vecteur et le nombre
d'éléments utilisés.

void LireVect (int, int *);


int * ad_max (int, int *);

main ()
{
int v[10], n, *p;

scanf ("%d", &n);

/* lire les n éléments du vecteur */


LireVect (n, &v[0]);

p = ad_max (n, &v[0]);


printf ("p = %p\n", p);
}

/************************************************/
/* input : nbr : nombre d'éléments ds le vect */
/* pt : adresse du vecteur */
/* process : lit nbr éléments et les place dans */
/* le vecteur dont l'adresse est donnée */
/* output : / */
/************************************************/
void LireVect (int nbr, int *pt)
{
int i;

for (i = 0 ; i < nbr ; i++, pt++)


{
scanf ("%d", pt);
}
}

/************************************************/
/* input : nbr : nombre d'éléments ds le vect */
/* pt : adresse du vecteur */
/* process : recherche l'élément max du vecteur */
/* output : adresse de l'élément max */
/************************************************/
int *ad_max (int nbr, int *pt)

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 101


Notes provisoires Logique et technique de programmation

{
int *pmax, i;

pmax = pt++;
/*
* pmax reçoit l'adresse contenue dans pt
* => pmax pointe vers v[0]
* pt pointe vers l'élément suivant du vecteur
*/

for (i = 1; i < nbr; i++, pt++)


{
if (*pt > *pmax)
pmax = pt;
}

return (pmax);
}

Si on avait voulu retourner l'indice du maximum du vecteur, la fonction aurait été


semblable à ceci :

/************************************************/
/* input : nbr : nombre d'éléments ds le vect */
/* pt : adresse du début du vecteur */
/* process : recherche l'élément max du vecteur */
/* output : indice de l'élément max */
/************************************************/
int ind_max (int nbr, int *pt)
{
int imax=0, i;

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


{
if (*(pt + i) > *(pt + imax))
imax = i;
}

return (imax);
}

11. MANIPULATION DES TABLEAUX DANS LES FONCTIONS


Pour pouvoir manipuler un tableau dans une fonction, on doit connaître l'adresse du
tableau, le nombre de lignes et le nombre de colonnes utiles. On dispose ainsi de toute
l'information nécessaire, et ce n'est pas vraiment plus difficile que de manipuler des vecteurs
dans les fonctions

11.1. Exemple :
Ecrire une fonction qui compte le nombre de 0 dans un tableau et qui retourne le
nombre obtenu.

1. Si on considère que toutes les cases du tableau sont occupées :

Page 102 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

int CompteZero (int *, int, int);


void LireTab (int *, int, int);
void AfficherTab (int *, int, int);

main ()
{
int tab[4][5], x;

LireTab (&tab[0][0], 4, 5);


x = CompteZero (&tab[0][0], 4, 5);
AfficherTab (&tab[0][0], 4, 5);
}

/************************************************/
/* input : pt : adresse du tableau */
/* l : nombre de lignes utiles du tableau */
/* c : nombre de colonnes utiles du tableau*/
/* process : compte le nombre de '0' dans le */
/* tableau */
/* output : retourne le nombre trouvé */
/************************************************/
int CompteZero (int *pt, int l, int c)
{
int i, j, cptr = 0;

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


{
for (j = 0; j < c; j++, pt++)
{
if (*pt == 0)
cptr ++;
}
}
return (cptr);
}

/************************************************/
/* input : pt : adresse du tableau */
/* l : nombre de lignes utiles du tableau */
/* c : nombre de colonnes utiles du tableau*/
/* process : initialise le contenu du tableau */
/* output : / */
/************************************************/
void LireTab (int *pt, int l, int c)
{
int i, j;

printf ("entrez les valeurs du tableau : ");


for (i = 0; i < l; i++)
{
for (j = 0; j < c; j++, pt++)
{
printf ("\ntab[%d][%d] = ", i, j);
scanf ("%d", pt);
}
}
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 103


Notes provisoires Logique et technique de programmation

/************************************************/
/* input : pt : adresse du tableau */
/* l : nombre de lignes utiles du tableau */
/* c : nombre de colonnes utiles du tableau*/
/* process : affiche le contenu du tableau */
/* output : / */
/************************************************/
void AfficheTab (int *pt, int l, int c)
{
int i, j;

printf ("Voici le contenu du tableau : ");


for (i = 0; i < l; i++)
{
for (j = 0; j < c; j++, pt++)
{
printf ("\t%d", *pt);
}
printf ("\n");
}
}

2. Si l'utilisateur est libre de choisir le nombre de lignes et le nombre de


colonnes :
on a donc besoin de connaître :
 le nombre réel de colonnes
 le nombre de colonnes utilisées
 le nombre de lignes utilisées

On peut choisir de définir le nombre réel de lignes ainsi que le nombre réel
de colonnes au moyen d'une instruction "#define" au début du programme.
Exemple :
#define MAXCOL 50
#define MAXLIG 50

MAXCOL et MAXLIG sont donc des constantes nommées globales.


Dans ce cas, le premier paramètre cité ci-dessus n'a pas besoin d'être
passé à la fonction : celle-ci peut utiliser les constantes qui sont connues de
tout le programme.
Cependant, si on désire définir les fonctions dans un autre fichier C que l'on
pourra compiler séparément en tant que fichier objet (extension ".obj")
pour le "linker" ensuite avec le programme principal, ces constantes ne
seront pas connues du deuxième fichier source dans lequel on veut définir
nos fonctions. Dans ce cas, il sera alors indispensable de passer ces valeurs
comme arguments de la fonction. C'est pourquoi, dans les exemples
suivants, on se donnera la peine de passer ces valeurs comme paramètres.

Page 104 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

int CompteZero (int *, int, int, int);


void LireTab (int *, int, int, int, int);
void AfficherTab (int *, int, int, int, int);

#define MAXCOL 50
#define MAXLIG 50

main ()
{
int tab[MAXLIG][MAXCOL], x;

LireTab (&tab[0][0], MAXCOL, 4, 5);


x = CompteZero (&tab[0][0], MAXCOL, 4, 5);
AfficherTab (&tab[0][0], MAXCOL, 4, 5);
}

/************************************************/
/* input : pt : adresse du tableau */
/* maxcol : nombre réel de colonnes */
/* dans le tableau */
/* l : nombre de lignes utiles du tableau */
/* c : nombre de colonnes utiles du tableau*/
/* process : compte le nombre de '0' dans le */
/* tableau */
/* output : retourne le nombre trouvé */
/************************************************/
int CompteZero (int *pt, int maxcol,
int l, int c)
{
int i, j, cptr = 0, *ptmp;

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


{
ptmp = pt;
for (j = 0; j < c; j++, ptmp++)
{
if (*ptmp == 0)
cptr ++;
}
pt = pt + maxcol;
}
return (cptr);
}

/************************************************/
/* input : pt : adresse du tableau */
/* maxcol : nombre réel de colonnes */
/* dans le tableau */
/* l : nombre de lignes utiles du tableau */
/* c : nombre de colonnes utiles du tableau*/
/* process : initialise le contenu du tableau */
/* output : / */
/************************************************/
void LireTab (int *pt, int maxcol, int l, int c)
{
int i, j, *ptmp;

printf ("entrez les valeurs du tableau : ");


for (i = 0; i < l; i++)
{
ptmp = pt;

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 105


Notes provisoires Logique et technique de programmation

for (j = 0; j < c; j++, ptmp++)


{
printf ("\ntab[%d][%d] = ", i, j);
scanf ("%d", ptmp);
}
pt = pt + maxcol;
}
}

/************************************************/
/* input : pt : adresse du tableau */
/* maxcol : nombre réel de colonnes */
/* dans le tableau */
/* l : nombre de lignes utiles du tableau */
/* c : nombre de colonnes utiles du tableau*/
/* process : affiche le contenu du tableau */
/* output : / */
/************************************************/
void AfficherTab(int *pt, int maxcol,
int l, int c)
{
int i, j, *ptmp;

printf ("Voici le contenu du tableau : ");


for (i = 0; i < l; i++)
{
ptmp = pt;
for (j = 0; j < c; j++, ptmp++)
{
printf ("\t%d", *ptmp);
}
pt = pt + maxcol;
printf ("\n");
}
}

11.2. Une matrice peut être considérée comme un vecteur de


vecteurs.
Un vecteur est représenté par 2 choses indissociables : le nom du vecteur (son
adresse) et le nombre d'éléments utiles du vecteur.

Exemple : int v[7]

n 7 v 4 8 12 15 20 18 19
0 1 2 3 4 5 6

Qu'est-ce que supprimer un élément d'un vecteur ?


Si on veut éliminer 15, on doit obtenir le résultat suivant :

6
n 7 v 4 8 12 20 18 19 1199
0 1 2 3 4 5 6

Page 106 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Un tableau à 2 dimensions peut également être considéré comme un vecteur de


vecteurs :
int v[5]; un vecteur contenant 5 entiers
int tab[4][3]; un tableau d'entiers de 4 lignes et 3 colonnes
ou un vecteur contenant 4 vecteurs de 3 entiers !!!

Exemples d'initialisation :
int v[5] = {1, 2, 3, 4, 5};
int tab[4][3] = {
{4, 9, 3},
{5, 3},
{1, 4, 6}
}

ligne [0] ligne [1] ligne [2] ligne [3]


4 9 3 5 3 0 1 4 6 0 0 0
0 1 2 0 1 2 0 1 2 0 1 2

Elément [1] de la ligne [2] soit tab[2][1]

Les cases du tableau pour lesquelles l'utilisateur n'a pas donné de valeur seront
initialisées à '0'.
! MAIS si on n'initialise aucune case du tableau, celles-ci contiendront toutes une
valeur indéterminée !!!

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 107


Notes provisoires Logique et technique de programmation

Page 108 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 14 :

Tableaux de caractères

1. UN PREMIER EXEMPLE

char VCh[10];
/*
* Réservation d'un tableau de 10 octets :
* un caractère occupe un octet
*/

VCh[0] = 'W';
VCh[1] = 'I'; VCh W I N N I E ? ? ? ?
VCh[2] = 'N'; 0 1 2 3 4 5 6 7 8 9
VCh[3] = 'N';
VCh[4] = 'I';
VCh[5] = 'E';

Ce tableau est un tableau comme on en a vu jusqu'à présent, les cases peuvent contenir
chacune un caractère. Le tableau est donc constitué de 10 cases d'un octet chacune.

2. AFFICHER ET LIRE LE CONTENU D'UN VECTEUR DE CARATERES


Nous allons écrire une fonction qui permet d'afficher le contenu d'un tableau de
caractères :

void AfficherVCar (char *, int);

/************************************************/
/* input : pc : adresse du tableau */
/* nbElem : nombre de caractères */
/* contenus dans le vecteur */
/* process : affiche le contenu du tableau */
/* output : / */
/************************************************/
void AfficherVCar (char *pc, int nbElem)
{
int i;

for (i = 0; i < nbElem; i++, pc++)


{
printf ("%c", *pc);
}
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 109


Notes provisoires Logique et technique de programmation

Dans l'exemple suivant, on va voir comment remplir un vecteur de caractères :

int LireVCar (char *, int);

/************************************************/
/* input : pc : adresse du tableau */
/* nbElem : nombre de caractères */
/* maximum permis dans le vecteur */
/* process : saisit le contenu du tableau */
/* output : nombre de caractères saisis */
/************************************************/
int LireVCar (char *pc, int nbElem)
{
int i;

i = 0;
fflush(stdin);
do
{
*pc = getchar();
i++;
pc++;
} while (*(pc-1) != '\n' && i < nbElem);

return(i);
}

3. EXERCICE 1
Ecrire un programme qui indique le nombre de voyelles dans un vecteur de caractères.

int LireVCar (char *, int);


int EstVoyelle (char);
int NbrVoyelles (int, char *);

#define MAXELEM 50

main ()
{
char v[MAXELEM];
int n;

n = LireVCar (&v[0], MAXELEM);


printf ("Nombre de voyelles = %d\n",
NbrVoyelles (MAXELEM, &v[0]));
}

/************************************************/
/* input : pc : adresse du tableau */
/* nbElem : nombre de caractères */
/* maximum permis dans le vecteur */
/* process : saisit le contenu du tableau */
/* output : nombre de caractères saisis */
/************************************************/
int LireVCar (char *pc, int nbElem)
{
int i=0;

fflush(stdin);

Page 110 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

do
{
*pc = getchar();
i++;
pc++;
} while (*(pc-1) != '\n' && i < nbElem);

return(i);
}

/************************************************/
/* input : pc : adresse du tableau */
/* nbElem : nombre de caractères */
/* dans le vecteur */
/* process : compte le nombre de voyelles dans */
/* ce vecteur */
/* output : nombre de voyelles trouvées */
/************************************************/
int NbrVoyelles (int nbElem, char *pc)
{
int k, cpt = 0;

for (k = 0; k < nbElem; k++, pc++)


{
if (EstVoyelle(*pc) == 1)
cpt++;
}
return (cpt);
}

/************************************************/
/* input : c : caractère voyelle ou consonne ? */
/* process : détermine si le caractère est une */
/* voyelle */
/* output : 1 si le caractère est une voyelle */
/* 0 sinon */
/************************************************/
int EstVoyelle (char c)
{
int i = 0;

c = toupper(c); /* => #include <ctype.h> */

switch (c)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
case 'Y': i = 1;
}
return (i);
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 111


Notes provisoires Logique et technique de programmation

4. BOUCLE ET GETCHAR()
Lorsqu'on a abordé l'instruction scanf au début du cours, on a dit qu'il s'agissait d'une
instruction dangereuse car elle comporte deux inconvénients majeurs :
1. Si on entre <ENTER> avant d'avoir tapé une valeur, l'instruction ne donne
pas de message d'erreur. Elle se contente de ne pas modifier le contenu de
la variable.

2. Si le buffer (zone tampon entre les périphériques et l'unité centrale) n'est


pas vide, scanf va lire le contenu de celui-ci, avant que l'utilisateur n'ait
entré quoi que ce soit au clavier.

Voyons comment on peut se passer entièrement de scanf(), même pour saisir les
entiers !

Rappel :

char a, b, c, d;
stdin '1' '2' '5' ↵
a = getchar(); ↵
125↵ a '1' 0 1 2 3
b = getchar(); b '2'
c = getchar(); c '5'
d = getchar(); d ↵

Si on désire saisir un entier, on peut utiliser l'instruction "getchar()" et une boucle !


!
4.1. Exemple 1 : lire un entier au moyen de "getchar()"
Lire un entier positif et inférieur à 10000 au moyen d'une boucle et de la fonction
getchar().

char c;
int i = 0;

c = getchar ();

while ((c >= '0' && c <= '9') && (i <= 999))
{
i = i * 10 + (c – 48);
/* c – 48 : code ASCII => chiffre */
c = getchar();
}

=> ↵
127↵ : i => i 1
=> i 12
=> i 127
=> 12A ↵ : i => i 1
=> i 12
=> sortie de la boucle

Si la valeur saisie n'est pas dans les limites 0 à 9999, la valeur de i qui en résulte ne
correspondra pas à la valeur saisie.

Page 112 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Notes :
 ↵' = '0A' en hexadécimal
'↵
 48 en décimal = 30 en hexadécimal; or, en code ASCII, les nombres sont
de 30 à 39 en hexadécimal => en faisant, par exemple 38 – 30 en
hexadécimal, il reste 8, qui est le chiffre dont on a besoin

Comment peut-on détecter une erreur ?

c = getchar ();

while ((i >= 0 && i <= 999) && (c != '\n'))


{
if (c >= '0' && c <= '9')
{
i = i * 10 + (c – 48);
c = getchar();
}
else
{
i = -1; /* i contient une nouvelle valeur
une valeur fausse */
}
}

OU

else
{
printf ("erreur, ");
printf ("i doit être un entier positif < 10000");
fflush (stdin);
i = 0;
c = getchar();
}

Une telle boucle est évidemment plus longue à écrire que l'instruction scanf, mais
elle offre une plus grande sécurité. Elle permet également de contrôler les
dépassements de valeur suivant le type de données attendu.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 113


Notes provisoires Logique et technique de programmation

Page 114 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 15 :

Les chaînes de caractères

1. DEFINITION

Une chaîne de caractères est simplement un vecteur de caractères


terminé par la valeur '\0' ou 00 hex.

1.1. Exemples

char ch1[10]={"Donald"};

ch1 'D' 'o' 'n' 'a' 'l' 'd' '\0' ? ? ?


0 1 2 3 4 5 6 7 8 9

char ch2[]={"Donald"};

ch2 'D' 'o' 'n' 'a' 'l' 'd' '\0'


0 1 2 3 4 5 6

char ch3[6]={"Donald"};

! ch1 'D'
0
'o'
1
'n'
2
'a'
3
'l'
4
'd'
5
'\0'

ATTENTION, ce dernier exemple va poser problème : on utilise le byte qui suit l'espace
mémoire que l'on a réservé pour la chaîne de caractères => on risque d'effacer une
information nécessaire au bon fonctionnement du programme !
 On doit donc impérativement réserver une case du vecteur pour y
placer le '\0'
 Le '\0' sert à détecter la fin de la chaîne de caractères
 On appelle le '\0' le caractère de fin de chaîne

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 115


Notes provisoires Logique et technique de programmation

2. INITIALISATION : CHAINE / VECTEUR DE CARACTERES

char nom1[10]={"Donald"};

nom1 'D' 'o' 'n' 'a' 'l' 'd' '\0' ? ? ?


0 1 2 3 4 5 6 7 8 9

il s'agit ici d'une chaîne de caractères

char nom2[10]={'D', 'o', 'n', 'a', 'l', 'd'};

nom2 'D' 'o' 'n' 'a' 'l' 'd' ? ? ? ?


0 1 2 3 4 5 6 7 8 9

il s'agit ici d'un vecteur de caractères

3. INSTRUCTIONS ET FONCTIONS RELATIVES A LA GESTION DES


CHAINES DE CARACTERES

3.1. Lire une chaîne.

[Link]. Scanf (pas recommandé)

char nom[30];

scanf ("%s", &nom[0]);


ou
scanf ("%s", nom);

Cette instruction permet de lire une chaîne de caractères à partir de stdin.

Exemple : Donald ↵

stdin 'D' 'o' 'n' 'a' 'l' 'd' '\n' ? ? ?


0 1 2 3 4 5 6 7 8 9

L'instruction scanf avec "%s" traduit le caractère '\n' en '\0', c'est-à-dire en


caractère de fin de chaîne.

Inconvénient de cette instruction : elle s'arrête de remplir le vecteur dès


qu'elle rencontre un espace blanc.
Scanf n'est donc pas l'instruction à conseiller pour lire une chaîne de
caractères !

Page 116 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

[Link]. Gets

Prototype :
dans stdio.h

char * gets (char *);

Le paramètre est l'adresse du début de la chaîne de caractères (donc le début


de l'espace mémoire dans lequel on veut conserver la chaîne que l'on va lire)
La valeur de retour est l'adresse du début de la chaîne de caractère (donc la
même adresse).

Attention, l'instruction permet de lire au maximum 127 caractères !


Attention, aucun garde-fou; la saisie d'une suite de caractères peut provoquer
un dépassement de capacité de la zone réceptrice.

3.2. Afficher une chaîne

[Link]. printf

char nom[30];

printf ("%s", nom);


ou
printf ("%s", &nom[0]);

Dans ce cas, on ne donne pas à printf un caractère ou une valeur comme


deuxième paramètre, mais bien une adresse : c'est la seule manière de procéder qui
permette à l'instruction d'accéder à l'entièreté du vecteur et non pas seulement au
premier caractère.

[Link]. puts

char nom[30];

puts (nom);
ou
puts (&nom[0]);

Puts affiche une chaîne sur le stdout suivie d'un caractère de fin de ligne (donc
d'un passage à la ligne).
Si l'instruction s'est déroulée sans problème, la fonction retourne le dernier
caractère affiché, sinon, elle retourne EOF.

puts (nom);

est équivalent à :

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 117


Notes provisoires Logique et technique de programmation

printf ("%s\n", nom);

3.3. Exercice
Ecrire une fonction qui reçoit l'adresse du début d'un vecteur de caractères et qui
saisit au clavier une chaîne à placer dans ce vecteur.
Il s'agit donc d'écrire une fonction qui effectue le même travail que "gets", sans
utiliser ni "gets", ni "scanf".

[Link]. Version simple

/************************************************/
/* input : pc, adresse d'un vecteur de max */
/* elements */
/* process : la fonction saisit une suite de */
/* caractères qu'elle assigne au vecteur */
/* d'adresse pc. */
/* output : // */
/************************************************/

void LireChaine (char *pc, int max)


{
char c;
int i=0;

fflush (stdin);
c = getchar();
while ((i < max-1) && (c != '\n'))
{
*pc = c;
pc++;
i++;
c = getchar();
}

*pc = '\0'; /* caractère de fin de chaîne */


}

Le paramètre max permet de contrôler que l'on ne sort pas de l'espace réservé !
Cette méthode est la plus sûre pour saisir une chaîne de caractères; elle est
d'ailleurs recommandée par B. W. Kernighan et D. M. Ritchie (principaux créateurs du
langage C).

Page 118 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

[Link]. Version qui retourne le nombre de caractères lus

/************************************************/
/* input : pc, adresse d'un vecteur de max */
/* elements */
/* process : la fonction saisit une suite de */
/* caractères qu'elle assigne au vecteur */
/* d'adresse pc. */
/* output : renvoie le nombre de caractères de */
/* la chaîne */
/************************************************/

int LireChaine (char *pc, int max)


{
char c;
int i=0;

fflush (stdin);
c = getchar();
while ((i < max-1) && (c != '\n'))
{
*pc = c;
pc++;
i++;
c = getchar();
}

*pc = '\0'; /* caractère de fin de chaîne */

return (i);
}

3.4. Copier une chaîne

[Link]. Préliminaire : affectation d'une constante à une chaîne de


caractères :

char nom[10];

nom = "Donald" ;

 provoque une erreur à la compilation !!!


(pourquoi ??? nom est une constante qui représente l'adresse (char *) du
vecteur nom; donc l'instruction tenterait de modifier une constante !!)

Pour pouvoir affecter une valeur à une chaîne de caractères, il faut procéder
comme suit :

char nom[10];

nom[0] = 'D';
nom[1] = 'o';
nom[2] = 'n';
nom[3] = 'a';

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 119


Notes provisoires Logique et technique de programmation

nom[4] = 'l';
nom[5] = 'd';
nom[6] = '\0';

[Link]. Exercice
Ecrire une fonction qui copie le contenu d'une chaîne de caractères dans une
autre chaîne de caractères.

Proposition de solution :

char * CopierChaineCaract (char *, char *)

/*******************************************************/
/* input : pta : adresse de la chaîne de destination */
/* ptb : adresse de la chaîne source */
/* process : copie le contenu de la chaîne source */
/* dans la chaîne de destination */
/* output : adresse de la chaîne de destination */
/*******************************************************/
char * CopierChaineCaract (char * pta, char * ptb)
{
char *p;

p = pta;

while (*ptb != '\0')


{
*pta = *ptb;
pta++;
ptb++;
}

*pta = *ptb; /* caractère de fin de chaîne */

return (p);
}

[Link]. strcpy
Il existe une fonction, dont le prototype est donné dans le fichier "string.h", qui
effectue la même tâche que celle de l'exercice précédent : il s'agit de strcpy.

Prototype :

dans string.h

char * strcpy (char *destination, char *source);

Les paramètres sont l'adresse du début de la chaîne de caractères de


destination ainsi que l'adresse de la chaîne de caractères source.
La fonction strcpy n'a pas besoin de connaître la longueur de la chaîne de
caractères puisque sa fin est signalée par la valeur '\0'.
Cette fonction retourne l'adresse du vecteur de destination.

Page 120 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

[Link]. strncpy
Il existe également une fonction semblable strncpy :

Prototype :

dans string.h

char * strncpy (char *destination, char *source,


int nbChar);

Les deux premiers paramètres et la valeur de retour sont semblables aux


données correspondantes de la fonction strcpy.
Il existe cependant 2 différences importantes :
 un troisième paramètre donne le nombre de caractères à copier
 le caractère '\0' n'est pas ajouté à la fin de la chaîne de destination : le
programmeur doit prévoir de l'ajouter lui-même !

3.5. Déterminer la longueur d'une chaîne

[Link]. Exercice
Ecrire une fonction qui détermine la longueur d'une chaîne de caractères.

Proposition de solution :

int LongueurChaineCaract (char *);

/************************************************/
/* input : pc : adresse de la chaîne de caract */
/* process : détermine la longueur de la chaîne */
/* output : longueur de la chaîne reçue en param*/
/************************************************/
int LongueurChaineCaract (char *pc)
{
int lg=0;

while (*pc != '\0')


{
pc++;
lg++;
}

return (lg);
}

[Link]. strlen
Il existe une fonction, dont le prototype est donné dans le fichier "string.h", qui
effectue la même tâche que celle de l'exercice précédent : il s'agit de strlen

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 121


Notes provisoires Logique et technique de programmation

Prototype :

dans string.h

size_t strlen (char *chaine);

Le paramètre est l'adresse de la chaîne de caractères dont on doit déterminer


la longueur.
Cette fonction retourne la longueur de la chaîne de caractères, c'est-à-dire le
nombre de caractères qu'elle contient, sans compter le caractère de fin de chaîne

3.6. Comparer le contenu de deux chaînes de caractères

[Link]. Exercice
L'exercice consiste à comparer le contenu d'une chaîne de caractères avec le
contenu d'une autre chaîne de caractères.

Proposition de solution :

int ComparerChainesCaract (char *,char *);

/************************************************/
/* input : pa : adresse de la première chaine */
/* pb : adresse de la deuxième chaine */
/* process : compare le contenu des 2 chaines */
/* output : 1 si 1er chaine est > 2eme */
/* 0 si 2 chaines sont identiques */
/* -1 si 2eme chaine est > 1er */
/************************************************/
int ComparerChainesCaract (char *pa, char *pb)
{
int comp=0;

while (comp == 0 && *pa != '\0' && *pb != '\0')


{
if (*pa != *pb)
{
if (*pa > *pb)
comp = 1;
else
comp = -1;
}
pa++;
pb++;
}

if (comp == 0)
{
if (*pa == '\0')
{
if (*pb != '\0')
comp = -1;
}
else
comp = 1;

Page 122 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

return (comp);
}

[Link]. strcmp
Il existe une fonction, dont le prototype est donné dans le fichier "string.h", qui
effectue la même tâche que celle de l'exercice précédent : il s'agit de strcmp

Prototype :

dans string.h

int strcmp (char *s1, char *s2);

Les paramètres sont l'adresse des deux chaînes de caractères à comparer.


Cette fonction retourne :
 -1 si s1 est alphabétiquement avant s2
 0 si les deux chaînes sont identiques
 +1 si s1 est alphabétiquement après s2
du moins avec le compilateur VC++ 6.0. D'autres compilateurs (par exemple TC
de Borland), pour cette fonction, retournent une valeur négative, nulle ou positive.

3.7. Autres fonctions intéressantes

[Link]. strcat
Prototype :

dans string.h

char * strcat (char *s1, char *s2);

Cette fonction juxtapose les deux chaînes de caractères reçues, le résultat


étant mis dans celle donnée comme premier paramètre.
Les paramètres sont l'adresse des deux chaînes de caractères à juxtaposer.
Cette fonction retourne l'adresse de la chaîne comprenant les 2 chaînes de
caractères de départ.

[Link]. strchr
Prototype :

dans string.h

char * strchr (char *s1, int c);

Cette fonction recherche le caractère donné comme deuxième paramètre dans


la chaîne donnée par le premier paramètre.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 123


Notes provisoires Logique et technique de programmation

Le premier paramètre est l'adresse de la chaîne de caractères dans laquelle on


veut rechercher le caractère donné comme deuxième paramètre.
Cette fonction retourne l'adresse de la première occurrence du caractère dans
la chaîne, ou NULL si ce caractère n'a pas été trouvé.

[Link]. strstr
Prototype :

dans string.h

char * strstr (char *s1, char *s2);

Cette fonction recherche la chaîne de caractères donnée comme deuxième


paramètre dans la chaîne donnée par le premier paramètre. (Même principe que
strchr, mais cette fois-ci, les deux paramètres sont des chaînes de caractères).
Le premier paramètre est l'adresse de la chaîne de caractères dans laquelle on
veut rechercher la chaîne donnée comme deuxième paramètre.
Cette fonction retourne l'adresse de la première occurrence de la deuxième
chaîne de caractères dans la première, ou NULL si cette sous-chaîne n'a pas été
trouvée.

4. VECTEURS DE CHAINES DE CARACTERES OU TABLEAUX DE


CARACTERES.

Exemples :

char tabNoms[3][10] = {"Donald", "Mickey", "Max"};

tabNoms 0 'D' 'o' 'n' 'a' 'l' 'd' '\0' ? ? ?


1 'M' 'i' 'c' 'k' 'e' 'y' '\0' ? ? ?
2 'M' 'a' 'x' '\0' ? ? ? ? ? ?
0 1 2 3 4 5 6 7 8 9

autres instructions possibles :

strcpy (&tab[1][0], "Grincheux");

ou

strcpy (tab[1], "Prof");

Page 124 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 16 :

Tableaux de pointeurs

1. RAPPEL

main ()
{ a b
int a, b; X0156 X015A
int *pa, *pb;
int v[2]; X0156 X015A
pa pb
pa = &a;
pb = &b; 10 20
v
v[0] = 10; 0 1
v[1] = 20;
...
}

 a et b sont des variables de type entier (integer) et peuvent contenir


chacune une valeur entière
 v est un vecteur qui contient 2 entiers. Chacun de ses éléments peut donc
contenir un entier.
 pa et pb sont des variables de type pointeur d'entier. Chacune de ces
variables peut contenir l'adresse d'un entier.

2. UN VECTEUR DE POINTEURS

main ()
{ a 5 8
b
int a, b; X0156 X015A
int * vp[2];

vp[0] = &a; X0156 X015A


vp[1] = &b; vp
*vp[0] = 5; 0 1
*vp[1] = *vp[0] + 3;
...
}

 vp est un vecteur de pointeurs d'entiers


 vp contient 2 éléments
 chacun des éléments de vp peut contenir l'adresse d'un entier

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 125


Notes provisoires Logique et technique de programmation

3. EXERCICE 1
Ecrire une fonction qui permet de connaître l'adresse du maximum et du minimum d'un
vecteur.
Remarque : il est impossible pour une fonction d'avoir 2 valeurs de retour, il faut donc
utiliser un vecteur pouvant contenir 2 adresses d'entier (une adresse pour le maximum et une
pour le minimum). Nous utiliserons donc un vecteur de pointeurs d'entiers (vp).

void ChercheMaxMin (int *, int, int **);

main ()
{
int v[10];
int * vp[2];

LireVect(&v[0], 10);

ChercheMaxMin(&v[0], 10, &vp[0]);


...
}

void ChercheMaxMin (int *pv, int nbElem, int **ppv)


{
int i, max, min; pointeur
de pointeur
max = *pv;
min = *pv; d'entier
*ppv = pv;
/*
* le contenu de l'adresse pointée par ppv
* recoit la valeur de pv (donc une adresse)
*/
*(ppv + 1) = pv;

pv++;
for (i = 1; i < nbElem; i++, pv++)
{
if (*pv > max)
{
max = *pv;
*ppv = pv;
}
else
{
if (*pv < min)
{
min = *pv;
*(ppv + 1) = pv;
}
}
}
}

Page 126 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 17 :

Les structures

1. DEFINITIONS

Un tableau est un ensemble fini d'élements de même type (éléments


contigus en mémoire).

Une structure est un ensemble fini d'éléments de même type ou de


types différents (éléments contigus en mémoire).

Les types connus du C sont entre autre : int, char, float, double, etc.

Une structure est composée d'un mélange de types connus du C, mais le C ne


connaît pas par défaut la composition d'une structure que l'on crée.
Pour pouvoir utiliser une structure dans toutes les fonctions d'un programme, il
faut que sa composition soit connue dans toutes ses fonctions

La déclaration d'un TYPE de structure se fait donc de manière globale, en dehors


de toute fonction
Mais la déclaration d'une VARIABLE de type structure se fait toujours de la même
manière qu'une autre variable d'un type quelconque, et donc le plus souvent en local.

2. DECLARATIONS

2.1. Déclaration d'un type de structure

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

Les composants d'une structure sont appelés les champs. Dans l'exemple ci-
dessus, la structure "Date" contient 3 champs dont les noms sont "jour", "mois" et
"annee".

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 127


Notes provisoires Logique et technique de programmation

2.2. Déclaration d'un variable ayant pour type la structure que


nous venons de définir.

main ()
{
struct Date examen;

...
}

examen est le nom de la variable de type struct Date.

en mémoire :

examen
jour mois annee
1B00 1B04 1B08

3. ACCES AUX CHAMPS D'UNE STRUCTURE

L'opérateur '.' permet d'accéder aux différents champs d'une


structure.

3.1. Exemple simple

struct Date {
int jour;
int mois;
int annee; examen 15 6 2003
};

main ()
{
struct Date examen;

[Link] = 15;
[Link] = 6;
[Link] = 2003;

printf ("Date de l'examen : %d/%d/%d",


[Link], [Link], [Link]);
}

Page 128 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

3.2. Exemple avec des champs de types différents

struct Fiche {
char nom[20];
int age;
int cotisation;
char genre;
};

main ()
{
struct Fiche membre;

gets ([Link]);
scanf ("%d", &[Link]);
[Link] = 25;
[Link] = getchar();
...
} nom age cotisation genre

membre
X1B00 X1B14 X1B18 X1B1C

4. UNE STRUCTURE COMME PARAMETRE A UNE FONCTION


Il est bien évidemment possible de passer une structure comme paramètre à une
fonction. Voici un exemple :

struct Fiche {
char nom[20];
int age;
int cotisation;
char genre;
};
void afficherStruct (struct Fiche);
main ()
{
struct Fiche membre;

gets ([Link]);
scanf ("%d", &[Link]);
[Link] = 25;
[Link] = getchar();

afficherStruct(membre);
...
}

void afficherStruct (struct Fiche memb)


{
puts ([Link]);
printf ("%d\n", [Link]);
printf ("%d\n", [Link]);
printf ("%c\n", [Link]);
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 129


Notes provisoires Logique et technique de programmation

On peut également choisir de donner l'adresse d'une variable de type structuré à une
fonction. Dans ce cas,

L'opérateur '->' permet d'accéder aux différents champs d'une


structure.

Voici un exemple :

struct Fiche {
char nom[20];
int age;
int cotisation;
char genre;
};

void InitStruct (struct Fiche *);

main ()
{
struct Fiche membre;

InitStruct(&membre);
...
}

void InitStruct (struct Fiche *pmemb)


{
gets (pmemb->nom);
scanf ("%d", &(pmemb->age));
scanf ("%d", &(pmemb->cotisation));
fflush (stdin);
pmemb->genre = getchar();
}

5. UN VECTEUR OU TABLEAU DE STRUCTURES


Déclarer un type de structures revient à "inventer" un nouveau type de variable dont on
a besoin.
On peut dès lors également créer un vecteur ou un tableau de structures.

Page 130 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Exemple :

struct Fiche { cotisation


char nom[20]; nom age genre
int age; vec 0
int cotisation; 1
char genre;
};
2
3
main () 4
{ 5
struct Fiche vec[10];
struct Fiche *pf;
6
7
pf = &vec[0]; 8
pf++; 9
/*
* => pf va se déplacer du nombre d'octets occupés
* par une variable de son type
* donc pf contiendra l'adresse de vec[1] !!
*/

...
}

6. UNE STRUCTURE CONTENANT UNE STRUCTURE


Les champs d'une structure peuvent être de n'importe quel type de données connu. Il
peuvent donc être d'un type structuré défini préalablement.

Exemple :

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

struct Fiche {
char nom[20];
struct Date naissance;
int age;
int cotisation;
char genre;
};

7. TYPEDEF
On a vu dans les exemples donnés jusqu'à présent que chaque fois que l'on désire
déclarer une variable d'un type structuré, on doit répéter le mot clé "struct". Afin d'éviter
cela, il suffit d'utiliser le mot clé "typedef" au moment de la déclaration de la structure, on
peut alors se contenter du nom du type défini lors de la déclaration ultérieure de variables.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 131


Notes provisoires Logique et technique de programmation

Syntaxe :

typedef <définition de la structure> <identificateur de la structure> ;

OU

struct <nom de la structure> {


...
};
typedef <nom de la structure> <identificateur de la structure> ;

Exemple :

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

typedef struct {
char nom[20];
Date naissance;
int age;
int cotisation;
char genre;
} Fiche;

main()
{
Fiche membre, listeMembre[10];
Fiche *pf;

...
}

8. STRUCTURE ET ALIGNEMENT DES CHAMPS

struct SPers {
char nom[10];
unsigned short poids;
};

sizeof(struct sPers) ≡ 12 (10 + 2)

Page 132 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

struct SPers {
char nom[9];
unsigned short poids;
};

sizeof(struct sPers) ≡ 12
(9 + 1 + 2)
Un byte pour forcer
l'alignement à une adresse
paire du champ "poids"

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 133


Notes provisoires Logique et technique de programmation

Page 134 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 18 :

Les fichiers sur disque

1. DEFINITION
Un fichier est un ensemble de données qui se trouvent sur un disque.

2. OUVERTURE D'UN FICHIER SUR DISQUE


Pour pouvoir accéder à un fichier en lecture ou en écriture, il faut avant tout l'ouvrir.

2.1. Principe généraux


Ouvrir un fichier consiste à recopier une partie de ce fichier en mémoire RAM,
dans un buffer.

HD
buffer

On parle alors de fichier "bufferisé"

La taille par défaut du buffer est de 512 octets. Lors de l'ouverture d'un
fichier, on recopie donc au maximum 512 octets de ce fichier dans la mémoire RAM. La
taille du buffer peut être modifiée à l'aide de l'instruction setbuf().
L'ouverture d'un fichier est réalisée à l'aide de la fonction fopen().
Pour accéder au contenu du fichier, il faut en réalité accéder au contenu du
buffer. Pour cela, il faut se servir d'un pointeur que l'on va déplacer dans le buffer
(l'évolution de ce pointeur se fait par l'utilisation de certaines fonctions "disque").

2.2. La fonction fopen


Le prototype de cette fonction se trouve dans le fichier d'entête <stdio.h> qu'il
faudra donc penser à inclure dans tous les programmes C effectuant des accès à des
fichiers de données.

FILE * fopen (char * , char * );

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 135


Notes provisoires Logique et technique de programmation

Cette fonction "fopen" reçoit deux paramètres de type chaîne de caractères et


retourne une adresse récupérée par fp, adresse logique de type "FILE".

FILE * fp;

fp = fopen("a:[Link]", "rb");
/* fp = adresse logique du fichier */

La valeur retournée par la fonction fopen est de type "FILE *"; c'est-à-dire une
adresse vers le type prédéfini FILE. Ce type FILE est un type structuré. Les
différents champs de ce type FILE sont utilisés par les fonctions "disque" pour assurer
la gestion du fichier.
Dans l'exemple ci-dessus, le fichier "a:[Link]" est ouvert en mode binaire b et
en lecture seule r.
La valeur retournée est stockée dans le pointeur fp.

! Ne pas confondre le pointeur fp et le pointeur qui évolue dans le buffer !

Si un problème survient en cours de l'ouverture du fichier, la fonction retourne 0


ou NULL.
Les problèmes qui peuvent être rencontrés sont du type : tentative d'ouverture
en lecture d'un fichier inexistant, tentative d'ouverture d'un fichier dans un répertoire
ou sur un disque inexistant ou non prêt.

Le premier paramètre est une chaîne de caractères qui donne le chemin d'accès
ainsi que le nom du fichier que l'on désire ouvrir.
Exemples : a:[Link]
c:\ETUDIANT\[Link]

Le second paramètre est de même type : chaîne de caractères, et permet de


préciser le mode d'ouverture que l'on désire appliquer. Il contiendra une des valeurs
données ci-dessous :
r : ouverture en lecture uniquement
w : ouverture en écriture
a : ouverture en ajout (écriture à la fin du fichier)
r+
w+ : permettent l'accès en lecture / écriture
a+

Les différentes lettres données ci-dessus seront suivies soit de 'b' pour binaire
soit de 't' pour texte. Ces lettres 'b' et 't' permettent de préciser la manière dont les
caractères doivent être interprétés. Voir ci-dessous pour plus de détails.

2.3. Modes d'ouverture


On peut accéder aux fichiers de 2 manières différentes : soit en mode binaire,
soit en mode texte.
En mode binaire, tous les octets représentent une information, tous les octets
jouent le même rôle.

Page 136 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

En mode texte, certains octets ou certaines combinaisons d'octets ont une


signification particulière. Par exemple, la paire d'octets "0D 0A" représente la fin
d'une ligne.
Lors de l'ouverture du fichier, le programmeur doit préciser le mode qu'il choisit
en ajoutant un 'b' ou un 't' à la fin de la chaîne de caractères représentant le deuxième
paramètre de la fonction "fopen".

Mode binaire ou mode texte ?


On ouvrira un fichier en mode texte si celui-ci ne contient que du texte et rien
d'autre.
Si le contenu du fichier n'est pas uniquement composé de texte pur, alors il faut
ouvrir le fichier en binaire. Par exemple, si on désire enregistrer les fiches
d'inscription des membres d'un club, on pourra donner pour chaque personne, son nom,
son prénom (2 champs de type texte), son âge, le montant de sa cotisation (2 champs de
type nombre). On choisira donc le mode binaire !

Attention, si on ouvre un fichier binaire en mode texte, certaines valeurs seront


interprétées comme des caractères de mise en page (ex.: "0D 0A" => CR LF)

2.4. Méthodes d'accès

[Link]. r : ouverture en lecture seule


 Si le fichier n'existe pas, fopen retourne NULL
 Si le fichier existe, le fichier est ouvert
 Juste après l'ouverture, le pointeur donne l'adresse du début du premier
enregistrement du fichier
 fread est possible, fwrite pas (ouverture en lecture seule !)

[Link]. w : ouverture en écriture seule


 Si le fichier n'existe pas, un nouveau fichier vide est créé
 Si le fichier existe, alors le fichier actuel est détruit et un nouveau
fichier est créé => ce mode est à utiliser pour la création d'un nouveau
fichier.
 Juste après l'ouverture, le fichier est vide et le pointeur contient
l'adresse du début du fichier (buffer).
 fwrite est utilisé pour constituer ce fichier (pour y enregistrer des
données).

[Link]. a : (append) ouverture en ajout en fin de fichier


 Si le fichier n'existe pas, un nouveau fichier vide est créé
 Si le fichier existe, il est ouvert
 Juste après l'ouverture, le pointeur contient l'adresse de la fin du
fichier => APRES le dernier enregistrement
 fwrite peut être utilisé pour ajouter des nouvelles données à la fin du
fichier (et uniquement là !)

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 137


Notes provisoires Logique et technique de programmation

[Link]. r+ :
 Si le fichier n'existe pas, la fonction fopen retourne NULL
 Si le fichier existe, il est ouvert
 On peut accéder au fichier en lecture ET écriture
 Après l'ouverture, le pointeur contient l'adresse du début du fichier

[Link]. w+ :
 Si le fichier n'existe pas, un nouveau fichier vide est créé
 Si le fichier existe, il est détruit et un nouveau fichier vide est créé
 On peut accéder au fichier en lecture ET écriture
 Après l'ouverture, le pointeur contient l'adresse du début du fichier

[Link]. a+ :
 Si le fichier n'existe pas, un nouveau fichier vide est créé
 Si le fichier existe, il est ouvert
 On peut y accéder en lecture ET écriture (l'écriture se faisant
uniquement en fin de fichier)
 Après l'ouverture, le pointeur donne l'adresse du début du fichier (si on
y accède en lecture) ou celle de la fin du fichier (si on y accède en
écriture).

[Link]. Remarque
En mode '+', les créateurs du turbo C conseillent de toujours positionner le
! pointeur de fichier avant chaque accès (lecture ou écriture).

Page 138 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

[Link]. Résumé :

! r w a r+ w+ a+

destruction et destruction et
le fichier création d'un création d'un
ouverture ouverture ouverture ouverture
existe nouveau nouveau
fichier fichier

le fichier retourne retourne


création création création création
n'existe pas NULL NULL

à la position à la position à la position à la position


lecture impossible impossible
du pointeur du pointeur du pointeur du pointeur

à la position à la fin du à la position à la position en fin de


écriture impossible
du pointeur fichier du pointeur du pointeur fichier

position du
lecture : au
pointeur de début du au début du
début du à la fin du au début du début
fichier juste fichier (qui est fichier
fichier fichier fichier écriture : à la
après vide !) (qui est vide)
fin
l'ouverture

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 139


Notes provisoires Logique et technique de programmation

2.5. Exemple

#include <stdio.h>

main()
{
FILE *fp;

fp = fopen ("a:\\[Link]", "r+b");


if (fp == (FILE *) NULL)
printf ("Erreur d\'ouverture du fichier !!\n");
...
}

Pour information, la structure prédéfinie FILE correspond à la définition


suivante :

typedef struct {
short level; /* fill/empty level of buffer */
unsigned flags; /* File status flags */
char fd; /* File descriptor */
unsigned char hold; /* Ungetc char if no buffer */
short bsize; /* Buffer size */
unsigned char *buffer; /* Data transfer buffer */
unsigned char *curp; /* Current active pointer */
unsigned istemp; /* Temporary file indicator */
short token; /* Used for validity checking */
} FILE; /* This is the FILE object */

2.6. En cas d'erreur


Lorsque la fonction fopen retourne la valeur "NULL" pour signifier qu'une erreur
s'est produite au cours de la tentative d'ouverture du fichier, on peut obtenir un
supplément d'information grâce à la valeur de la variable "errno" définie dans le fichier
d'entête "errno.h". Dans l'exemple ci-dessous, on peut voir trois manières différentes
d'obtenir et d'utiliser cette information :

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

main()
{
FILE *fp;

fp = fopen ("a:\\[Link]", "rb");


if (fp == NULL)
{
/*
* l'instruction perror affiche le message donne comme
* argument suivi de ':' ainsi que du message d'erreur
* correspondant a la valeur actuelle de errno, c'est-à-

Page 140 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

* dire le code de la derniere erreur rencontree lors de


* l'acces à un fichier
*/
perror("Erreur rencontree lors de l'ouverture du fichier");

/*
* Affiche simple du code d'erreur
*/
printf ("\n%d", errno);

/*
* strerror retourne le message d'erreur correspondant au
* code d'erreur actuel
*/
printf ("\n\n%s\n", strerror(errno));
}

On trouve les différentes valeurs possibles de "errno" ainsi que leur signification
dans le fichier d'entête errno.h

3. LECTURE D'UN FICHIER SUR DISQUE


La fonction suivante, dont le prototype se trouve dans le fichier d'entête "stdio.h" va
nous permettre de lire des informations préalablement enregistrées dans un fichier sur
disque :

size_t fread (void *ptr, size_t size, size_t n, FILE *fp);

Cette fonction retourne le nombre d'enregistrements lus.


! ATTENTION, la valeur retournée ne correspond pas au nombre d'octets lus !!

Paramètres d'entrée :
 void *ptr : adresse de la RAM où il faut écrire les données lues
 size_t size : taille d'un enregistrement
 size_t n : nombre d'enregistrements à lire
 FILE *fp : adresse logique du fichier.

La lecture s'effectue à partir de l'adresse contenue dans le pointeur de fichier


(buffer).
Après chaque lecture, le pointeur est automatiquement déplacé du nombre d'octets lus.

Exemple : on souhaite lire les 5 premiers enregistrements d'un fichier contenant des
chaînes de 20 caractères.

#include <stdio.h>

main()
{
FILE *fp;
char tabChar[50][20];
int nbElem;

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 141


Notes provisoires Logique et technique de programmation

fp = fopen ("a:\\[Link]", "rb");


if (fp == (FILE *) NULL)
printf ("Erreur d\'ouverture du fichier !!\n");
else
nbElem = fread(&tabChar[0][0], 20, 5, fp);
...
}

4. ECRITURE D'INFORMATION DANS UN FICHIER SUR DISQUE


La fonction donnée ci-dessous, dont le prototype se trouve dans le fichier d'entête
"stdio.h", va nous permettre d'écrire des informations dans un fichier sur disque :

size_t fwrite (const void *ptr, size_t size, size_t n, FILE *fp);

Cette fonction retourne le nombre d'enregistrements écrits dans le fichier.


! ATTENTION, la valeur retournée ne correspond pas forcément au nombre d'octets
écrits !!

Paramètres d'entrée :
 void *ptr : adresse de la RAM où on trouve les données à écrire
 size_t size : taille d'un enregistrement
 size_t n : nombre d'enregistrements à écrire
 FILE *fp : adresse logique du fichier.

L'écriture se fait à partir de l'adresse contenue dans le pointeur de fichier.


Après chaque écriture, le pointeur est automatiquement déplacé du nombre d'octets
écrits.

5. FERMETURE D'UN FICHIER


Voici maintenant la fonction qui va nous permettre de fermer un fichier préalablement
ouvert. Son prototype se trouve également dans le fichier d'entête "stdio.h" :

int fclose (FILE *fp);

Cette fonction retourne 0 si le fichier a pu être fermé sans problème. Sinon, 'EOF' est
retourné.

"fclose" a donc pour effet de fermer un fichier qui a été ouvert.


Il faut toujours fermer tous les fichiers avant de quitter un programme, sinon :
 on pourrait avoir un message d'erreur
 La fonction fwrite écrit dans le buffer; lorsque celui-ci est rempli, son
contenu est enregistré sur disque. Si le buffer n'est pas rempli, c'est la

Page 142 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

fonction fclose qui provoquera l'enregistrement des données qui subsistent


dans le buffer. Si on ne ferme pas le fichier de manière explicite, on
risque de perdre les nouvelles données que l'on désirait écrire dans le
fichier.

6. LES FICHIERS STRUCTURES


Dans un fichier structuré, tous les enregistrements ont la même taille.

Imaginons par exemple un fichier dans lequel on va enregistrer les fiches des chiens
participants à un concours.
Chaque fiche est en réalité une structure composée des champs suivants :
 une chaîne de 30 caractères : nom du chien
 une chaîne de 30 caractères : nom du propriétaire
 une chaîne de 40 caractères : l'adresse du propriétaire du chien
 3 entiers permettant de donner la date de naissance du chien : jour, mois,
année
 un entier pour le numéro de pédigrée
 un entier pour le classement en fin de concours.

Quelle que soit la taille du nom du chien et du nom du propriétaire, de celle de


l'adresse, … la fiche (ou structure) contenant l'information concernant un chien particulier
occupera toujours la même taille, le même nombre d'octets.

Dans le fichier :

enregistrement 1 enregistrement 2 enregistrement 3 enregistrement 4

nom du nom du adresse jour mois an- pédi- classe-


chien propriétaire nee grée ment

date naissance

L'avantage d'un fichier structuré est que tous ses enregistrements ont la même taille.
On peut donc facilement calculer le nombre d'octets qui nous séparent de l'enregistrement
qui nous intéresse.

Dans notre exemple, un enregistrement occupe 120 octets. Si le pointeur contient


l'adresse du premier enregistrement, alors, pour le quatrième, il faut se déplacer de 3 * 120 =
360 octets.

Un autre avantage est que si on connaît la taille du fichier et la taille d'un


enregistrement, on peut facilement calculer le nombre d'enregistrements présents dans le
fichier :
nombre d'enregistrements = taille du fichier / taille d'un enregistrement
L'inconvénient est que l'on perd de la place sur le disque : il faut rarement 30
caractères pour le nom d'un chien ou pour le nom d'une personne !

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 143


Notes provisoires Logique et technique de programmation

7. DEPLACEMENT DANS UN FICHIER


La fonction suivante, dont le prototype se trouve dans le fichier d'entête "stdio.h", va
nous permettre de déplacer le pointeur de fichier :

int fseek (FILE *fp, long offset, int whence);

Cette fonction retourne '0' si elle s'est bien déroulée, et une valeur différente de '0'
en cas de problème.

Paramètres d'entrée :
 FILE *fp : adresse logique du fihcier dans lequel on veut modifier la
position du pointeur
 long offset : nombre d'octets dont le pointeur doit se déplacer par rapport
à un point de référence. Cet offset peut être positif : on se rapproche de
la fin du fichier ou négatif on revient vers le début du fichier.
 int whence : point de référence du déplacement :
SEEK_SET = 0 : début du fichier
SEEK_CUR = 1 : position actuelle du pointeur de fichier
SEEK_END = 2 : fin du fichier

"fseek" nous permettra donc de nous déplacer directement vers l'enregistrement qui
nous intéresse, sans devoir lire tous les enregistrements précédents.

Exemple :
#include <stdio.h>

main()
{
FILE *fp;
char tabChar[50][20];
int nbElem;

fp = fopen ("a:\\[Link]", "rb");


if (fp == (FILE *) NULL)
printf ("Erreur d\'ouverture du fichier !!\n");
else
{
fseek (fp, 3*20, SEEK_SET);
nbElem = fread(&tabChar[3][0], 20, 5, fp);
}
...
}

ATTENTION : si on déplace le pointeur de fichier au-delà des données contenues dans

! le buffer, le contenu actuel du buffer est enregistré sur le disque et le buffer est rempli
avec d'autres données.

Le deuxième argument de la fonction fseek est de type long. Si ce deuxième


argument est une expression dans laquelle figure l'opérateur sizeof (qui retourne un
unsigned int), une conversion implicite vers du long sera effectuée et pour peu que l'on

Page 144 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

souhaite un déplacement négatif, le résultat pourrait, d'un compilateur à l'autre être


inattendu.
Il faut dès lors être très attentif et peut-être construire ce déplacement dans une
variable "long depl" et placer cette variable comme 2ième argument de fseek.

8. OPERATIONS SUPPLEMENTAIRES DANS LE CADRE DES FICHIERS

8.1. ftell

long ftell(FILE *fp);

 prototype dans "stdio.h"


 arguments :
FILE *fp : adresse logique du fichier
 valeur de retour :
la position actuelle du pointeur, c'est-à-dire le nombre d'octets qui
séparent le début du fichier de la position donnée par le pointeur de
fichier.
-1 en cas d'erreur
 Tous les déplacements et toutes les tailles dans un fichier sont exprimées
en LONG

8.2. sizeof

unsigned int sizeof(<type> ou <expression>);

 sizeof n'est pas une instruction mais un OPERATEUR


 prototype dans "stdio.h"
 opérateur appliqué à une structure ou un type de données
 résultat obtenu : taille de la structure ou du type donné

Exemple : comment connaître le nombre d'enregistrements présents dans un


fichier structuré :

#include <stdio.h>

typedef struct {
...
} FICHE;

main()
{
FILE *fp;
int nbEnreg;

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 145


Notes provisoires Logique et technique de programmation

fp = fopen ("a:\\[Link]", "rb");


if (fp == (FILE *) NULL)
printf ("Erreur d\'ouverture du fichier !!\n");
else
{
fseek (fp, 0, SEEK_END);
nbEnreg = ftell(fp) / sizeof(FICHE);
}
...
}

8.3. feof

int feof (FILE *fp);

 prototype dans "stdio.h"


 argument :
FILE *fp : adresse logique du fichier
 valeur de retour :
≠ 0 si le pointeur se trouve à la fin du fichier
0 si le pointeur ne se trouve pas à la fin de fichier

8.4. rewind

void rewind (FILE *fp);

 prototype dans "stdio.h"


 place le pointeur de fichier au début du fichier, cela équivaut donc à fseek
(fp, 0, SEEK_SET);

8.5. perror

void perror (const char *s);

 prototype dans "stdio.h"


 argument : adresse d'une chaîne de caractères à afficher
 affiche un message d'erreur en fonction de la valeur de errno et le string
reçu en argument
 errno est défini dans errno.h, stdlib.h, stddef.h; errno prend une valeur en
fonction de l'erreur rencontrée lors d'un accès à un fichier

Page 146 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

8.6. rename

int rename (const char *oldname, const char *newname);

 prototype dans "stdio.h"


 change le nom du fichier en fonction des paramètres donnés. Le fichier
doit être fermé avant de pouvoir le renommer ! Le fichier doit rester sur
le même disque.
 arguments :
const char *oldname : nom actuel du fichier
const char *newname : nom que l'on veut attribuer au fichier
 valeur de retour :
0 si le nom du fichier a pu être modifié.
-1 en cas d'erreur

8.7. remove

remove (const char *filename);

 macro définie dans "stdio.h"


 supprime l fichier dont elle reçoit le nom.
 arguments :
const char *filename : nom du fichier à effacer

Remarque : il s'agit d'une macro, non d'une fonction. Cela signifie qu'au cours de
la compilation, le compilateur va recopier le code de la macro à l'emplacement de son
appel => accroissement du nombre de lignes de code. Cela est donc différent d'un appel
à une fonction.
Cette macro fait appel à la fonction suivante : unlink().

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 147


Notes provisoires Logique et technique de programmation

Page 148 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 19 :

L'allocation dynamique en mémoire


et les structures de données dynamiques

1. L'ALLOCATION DYNAMIQUE

La réservation de la mémoire n'est réalisée qu'au moment de


l'exécution de la fonction de réservation de mémoire (malloc).

1.1. Introduction
Rappel :

main ()
{
int v[10];

...
}

Au début de l'exécution de ce programme, 40 octets sont réservés en mémoire


centrale.
Inconvénients :
 il est impossible de placer plus de 10 entiers dans ce vecteur
 ce vecteur occupe toujours 40 octets même si on n'y place que 2 valeurs

Pire encore :

main ()
{
char tab[50][20];

...
}

Le tableau occupe toujours la même place, quel que soit le nombre de lignes utilisées et
quelle que soit la longueur de chaque nom !

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 149


Notes provisoires Logique et technique de programmation

Rappel :

int va[10]; /* variable globale */

main ()
{
int vb[10]; /* variable locale à la fct main */
int *pi; /* variable locale à la fct main */
...
}

CS

DS
va
HEAP

SS
vb, pi

1.2. Réservation d'une zone mémoire

void * malloc (size_t num_byte);

 prototype dans "malloc.h" ou "stdlib.h"


 cette fonction recherche une zone de num_byte octets libres dans le heap,
la réserve et en retourne l'adresse.
 arguments :
size_t num_byte : un entier non signé qui correspond au nombre
d'octets à réserver
 valeur de retour :
l'adresse de la zone de mémoire réservée.
NULL si aucune zone suffisamment grande n'est disponible

Exemple :

main ()
{
int i, *pi;
char *pc;

scanf ("%d", &i);


pi = (int *) malloc(i);
if (pi == NULL)
...
scanf ("%d", &i);
pc = (char *) malloc (i);

Page 150 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

if (pc == NULL)
...
...
}

1.3. Libération d'une zone mémoire

void free (void * mem_adress);

 prototype dans "alloc.h" ou "stdlib.h"


 cette fonction libère une zone mémoire qui a été préalablement allouée au
moyen d'une instruction malloc.
 arguments :
void * mem_adress : un pointeur vers le début de l'espace mémoire
à libérer

Exemple :

main ()
{
char *pc;

pc = (char *) malloc (10);


if (pc == NULL)
...
...
free (pc);
pc = NULL;
...
}

ATTENTION, il est très important d'affecter la constante NULL à un pointeur


lorsqu'il ne contient plus l'adresse d'un espace mémoire utilisé. Il faut couper les ponts
! afin d'éviter des problèmes. En effet, cet espace peut être repris lors d'une autre
allocation dynamique => risque de détruire des données.

1.4. Exemple
Prenons le cas d'un vecteur de chaînes de caractères ou vecteurs de noms ou
encore tableau de caractères.

Avec l'allocation dynamique, on peut obtenir la solution suivante : un tableau de


pointeurs de caractères.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 151


Notes provisoires Logique et technique de programmation

SS HEAP

0 J U L E S \0

1 R O S E \0

2
E M I L E \0

3 A N T O I N E \0
4

Dans cette solution, chaque chaîne de caractères n'occupe que l'espace


strictement nécessaire.

Exemple de code pour initialiser les chaînes de caractères :

char *x;
char *t[5];
char buf[20];

/* saisie clavier */
for (i = 0 ; i < 5 ; i++)
{
gets(buf);
t[i] = (char *) malloc (strlen(buf) + 1);
strcpy (t[i], &buf[0]);
}

Exemple de code pour trier les chaînes de caractères par ordre alphabétique :

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


{
for (j = i+1 ; j < 5 ; j++)
{
if (strcmp (t[i], t[j]) > 0)
{
x = t[i];
t[i] = t[j];
t[j] = x;
}
}
}

Exemple de code pour afficher les chaînes de caractères :

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


printf "%s\n", t[i]);

Page 152 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Dans cet exemple, on ne réserve que les octets qui sont réellement utilisés par les
chaînes de caractères. Mais le nombre de chaînes maximum est toujours fixé à la
compilation (taille du tableau de pointeurs).

Solution : les Structures de Données Dynamiques.

2. LES STRUCTURES DE DONNEES DYNAMIQUES

2.1. Les structures de données simples


Une Structure de Données Dynamique (SDD) est un chaînage d'éléments. En C, chaque
élément est une variable de type structuré; au moins un champ de cette structure est un
pointeur vers une structure de même type.
Ce pointeur permet de savoir où se trouve l'élément suivant de la SDD.

Exemple :

struct FICHE {
char nom[20];
int age;
struct FICHE *psvt;
};

Cette instruction ne réserve aucun espace en mémoire, elle définit simplement une
structure de données qui pourra être utilisée par la suite.

Exemple d'utilisation :

struct _FICHE {
char nom[20];
int age;
struct _FICHE *psvt;
};

typedef struct _FICHE FICHE;

main ()
{
int i = 0;
FICHE *pdebut = NULL, *ptemp = NULL;
char cont = 'O';

while (cont =='O' || cont = 'o')


{
ptemp = (FICHE *) malloc (sizeof(FICHE));
gets (ptemp->nom);
scanf ("%d", &(ptemp->age));
ptemp->psvt = pdebut;
pdebut = ptemp;
/* le nouvel élément est inséré en début de SDD */
i++;
fflush (stdin);
cont = getchar();

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 153


Notes provisoires Logique et technique de programmation

}
printf ("Vous avez enregistré %d élément(s)", i);
ptemp = pdebut;
n = i;
for (i = 0; i < n; i++)
/*
* ou mieux car on n'a pas besoin de connaître le nombre
* d'éléments dans la SDD :
* while (ptemp != NULL)
*/
{
puts (ptemp->nom);
printf ("age : %d\n", ptemp->age);
ptemp = ptemp->psvt;
}
}

Remarques concernant cet exemple :


 il est toujours préférable d'initialiser les pointeurs à la valeur NULL afin
d'éviter d'avoir des problèmes (par exemple affichage au moyen d'une
boucle "while(ptemp != NULL)", si on n'a mis aucun enregistrement dans la
SDD mais que l'on a fait l'initialisation de ptemp à NULL au départ, cette
boucle se déroulera correctement dans tous les cas !
 pdebut est le seul élément qui nous permet de savoir où commence la SDD !
Si on déplace pdebut dans la SDD sans avoir sauvegardé son contenu
(adresse de premier élément de la SDD) alors il n'y a aucun moyen de
retrouver le début de la SDD !!!
 On peut également trier les éléments de notre SDD, par exemple par ordre
alphabétique : il faut réorganiser l'ordre de parcours, donc modifier la
valeur des pointeurs.
 On ne sait se déplacer dans ce type de SDD que dans un sens. Il s'agit
d'une Liste Linéaire Unidirectionnelle ou LLU.
 Attention, toutes les SDD ne sont pas des LLU. Par exemple, la structure
suivante permet de construire une Liste Linéaire Bidirectionnelle (LLB) :

struct FICHE {
struct FICHE *pprec;
char nom [20];
int age;
struct FICHE *psvt;
};

2.2. Le concept de pile : LIFO

LIFO : Last In First Out

En informatique, le concept de pile peut être réalisé soit dans un tableau, soit à
l'aide d'un chaînage (SDD).

Page 154 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Dans une pile, on ne sait accéder qu'au dernier élément qui a été placé dans celle-
ci. On peut comparer ce principe à une pile de livres, on ne sait prendre que le livre qui
se trouve au sommet, si on veut accéder à un autre, on doit d'abord enlever ceux qui se
trouvent au-dessus de lui :

empiler ou push : ajouter un élément au-dessus de la pile

dépiler ou pop : retirer le dernier élément empilé

On peut implémenter une pile soit au moyen d'un tableau, soit au moyen d'un chaînage,
donc d'une structure de données dynamique.

Dans un tableau :

0 A i 3
1 B
2 C
3 D
4
5

A l'aide d'un chaînage :

struct PILE {
char nom[20];
int age;
struct PILE *psvt;
};
struct PILE ppile;
236

175

125 Lisa 4 Léa 8 Lionel 2 NULL


ppile 125 236 175

ppile contient toujours l'adresse du sommet de la pile.

Nous allons écrire la fonction qui permet d'ajouter un élément au sommet de la pile en
considérant que celle-ci est implémentée au moyen d'une SDD

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 155


Notes provisoires Logique et technique de programmation

/*******************************************************
* input : adresse du pointeur du début de pile
* adresse d'une structure contenant l'info à
* copier dans l'element à ajouter au sommet
* de la pile
* process : ajoute un nouvel élément au sommet de la
* pile et copie dans cet élément les valeurs
* reçues dans la structure donnee en 2° param
* output : 1 si l'allocation de mémoire OK
* 0 si KO
******************************************************/
int empiler (struct PILE **Ad_Pile, struct PILE *t)
{
struct PILE *ptemp;
int contr=0;

ptemp = (struct PILE *) malloc (sizeof (struct PILE));


if (ptemp != NULL)
{
strcpy (ptemp->nom, t->nom);
ptemp->age = t->age;
ptemp->psvt = *Ad_Pile;
*Ad_Pile = ptemp;
contr = 1;
}

return (contr);
}

autre possibilité :

/*******************************************************
* input : adresse du début de pile
* adresse d'une structure contenant l'info à
* copier dans l'élément à ajouter au sommet
* de la pile
* process : ajoute un nouvel élément au sommet de la
* pile et copie dans cet élément les valeurs
* reçues dans la structure donnée en 2° param
* output : adresse du sommet de la pile
******************************************************/
struct PILE * empiler (struct PILE * PPile,
struct PILE *t)
{
struct PILE *ptemp;

ptemp = (struct PILE *) malloc (sizeof (struct PILE));


if (ptemp != NULL)
{
strcpy (ptemp->nom, t->nom);
ptemp->age = t->age;
ptemp->psvt = PPile;
}
else
ptemp = PPile;

return (ptemp);
}

Page 156 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Nous allons maintenant écrire la fonction qui permet d'enlever un élément du sommet de
la pile en considérant que celle-ci est implémentée au moyen d'une SDD

struct PILE *depiler (struct PILE **Ad_Pile)


{
struct PILE *ptemp;

ptemp = *Ad_Pile;
if (ptemp != NULL)
{
*Ad_Pile = ptemp->psvt;
}

return ptemp;
}

Cette fonction retourne donc l'adresse de l'élément qui a été enlevé du sommet de la
pile.

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 157


Notes provisoires Logique et technique de programmation

Page 158 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 20 :

Autres notions

1. PASSAGE DE PARAMETRES A UN PROGRAMME


Nous savons que la première fonction exécutée lorsqu'un programme est lancé est la
fonction main().
C'est le point de départ du programme.

Jusqu'à présent, nous avons toujours défini la fonction main par l'instruction "main()".
Ce qui implique qu'elle ne reçoit aucun paramètre.

Pour passer des paramètres à un programme, il faut, en réalité, passer ces paramètres à
la fonction main().

Il y a cependant deux difficultés :


 on ne connaît pas le nombre de paramètres qui seront passés
 on ne connaît pas le type de ces paramètres (int, char, float, …)

Le passage de paramètres à la fonction main() se fait d'une autre manière que pour une
autre fonction.

La fonction main() reçoit :


 soit aucun paramètre
 soit 2 paramètres

Dans le cas où elle reçoit 2 paramètres, le premier est un entier qui indique le nombre
de termes séparés par un espace présents sur la ligne de commande lors de l'exécution du
programme (y compris le nom du programme). Le deuxième paramètre est l'adresse d'un
tableau de pointeurs de chaînes de caractères. Ces chaînes de caractères étant les termes
présents sur la ligne de commande.

c:>[Link] param1 param2<enter>

main (int argc, char **argv)


{
...
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 159


Notes provisoires Logique et technique de programmation

argc 3

argv "param1\0"

argv + 1 "param2\0"

argv + 2 "[Link]\0"

NULL

On utilise souvent comme noms de paramètres argc pour "argument count" et argv pour
"argument vector". En effet, le premier paramètre nous permet de savoir combien de
paramètres se trouvent sur la ligne de commande tandis que le second nous permet d'accéder
à la liste des paramètres.
On peut également voir dans le schéma ci-dessus que, par convention, l'élément
argv(argc) contient la valeur NULL.

1.1. Premier exemple


Le programme suivant nous permet d'afficher la liste des paramètres reçu par le
programme en les séparant par un espace :

#include <stdio.h>
#include <conio.h>

void main (int argc, char **argv)


{
int i;

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


{
printf ("%s\n", argv[i]);
}
printf ("\n");
}

Exemple d'exécution de ce programme :

Ligne de commande :

C:\etudiant>[Link] 4 SALUT CESAR ↵

Résultat affiché à l'écran :

[Link]
4
SALUT
CESAR
C:\etudiant>

Page 160 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

1.2. Deuxième exemple


Dans cet exemple, l'idée de base est de saisir un certain nombre de lignes de
caractères. On cherche ensuite dans ces lignes si un modèle (sous-chaîne de caractères) y
apparaît. Si c'est le cas et suivant la valeur d'un autre paramètre, on décidera d'afficher les
lignes qui ne contiennent pas le modèle ou au contraire celles qui le contiennent.
Les flags possibles sont :
 "-x" si on veut afficher les lignes qui ne contiennent pas le modèle (on ne
donne pas ce flag dans la ligne de commande si on veut afficher les lignes
qui contiennent le modèle).
 "-n" si on veut afficher le numéro de ligne
Ces flags, s'ils sont tous les deux présents peuvent être combinés de la manière
suivante : -xn (ou –nx).
L'ordre d'apparition de ces deux arguments ne doit pas avoir d'influence sur le résultat
obtenu.

#include <stdio.h>
#include <string.h>
#include <conio.h>

#define MAXLINE 1000

int lireligne (char ligne[], int max);

int main (int argc, char *argv[])


{
char ligne[MAXLINE];
int numligne = 0;
int c, sauf = 0, numero = 0, trouvees = 0;

while (--argc > 0 && (*++argv) [0] == '-')


{
while (c = *++argv[0])
{
switch (c)
{
case 'x' :
sauf = 1;
break;
case 'n' :
numero = 1;
break;
default :
printf ("trouver : option interdite %c\n", c);
argc = 0;
trouvees = -1;
break;
}
}
}

if (argc != 1)
printf ("Usage : trouver -x -n modele \n");
else
while (lireligne(ligne, MAXLINE) > 0)
{
numligne++;
if ((strstr(ligne , *argv) != NULL) != sauf)

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 161


Notes provisoires Logique et technique de programmation

{
if (numero)
printf ("%d : ", numligne);
printf ("%s\n", ligne);
trouvees++;
}
}

return trouvees;
}

int lireligne (char s[], int lim)


{
int c, i;

i = 0;
while (--lim > 0 && (c=getchar())!=EOF && c!='\n')
s[i++] = c;

s[i]= '\0';

return i;
}

2. FONCTIONS AYANT UN NOMBRE VARIABLE DE PARAMETRES


Il est possible, en C, d'écrire des fonctions pouvant recevoir un nombre variable de
paramètres. C'est-à-dire qu'il est possible de les appeler en ne leur passant pas le même
nombre de paramètres à chaque appel.
Certaines fonctions du langage C ont cette particularité : par exemple printf !

Pour définir ces fonctions, on écrit entre les parenthèses les paramètres qui seront
toujours passés, (comme pour toutes les fonctions, pour printf, un seul paramètre obligatoire :
la chaîne de caractères), et on termine la liste par ", …)".

Les paramètres obligatoires seront reçus comme tous les paramètres que l'on a utilisés
jusqu'à présent.

Les autres paramètres seront placés sur une pile. Le programmeur doit aller les
rechercher lui-même, ce qui peut poser quelques difficultés si on ne connaît ni leur nombre, ni
leur type.

Exemple :

void fct (int a, ...);

main ()
{
int x, y, z;

x = 5;
y = 10;
z = 15;
fct(x, y, z);

Page 162 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

...
}

void fct (int a, ...);


{
printf ("%d", a);
printf (" %d %d", *(&a + 1), *(&a + 2));
}

Remarque, c'est exemple est très simple car :


 tous les paramètres sont du même type (dans le cas contraire, le calcul des
adresses est plus compliqué)
 la fonction récupère le bon nombre de paramètres (il peut être très utile
d'utiliser un des paramètres obligatoires à cet effet).

2.1. Premier exemple


Dans ce premier exemple, le programmeur gère lui-même le nombre et le type
d'arguments reçus en nombre variable. On peut constater que chacune des fonctions reçoit
comme premier paramètre non optionnel le nombre total de paramètres lors de cet appel de
fonction.

#include <stdio.h>
#include <stdlib.h>

float FctBizarre (int a, ...);


void FctBizarre2 (int a, char c, ...);
void FctBizarre3 (int a, char tab[10], ...);

void main (void)


{
float res;

res = FctBizarre (5, 6, 8, 4, 2);


printf ("La moyenne de 6, 8, 4 et 2 vaut : %f", res);
printf ("\n\n");
res = FctBizarre (7, 16, 128, 14, 22, 10, 25, 39);
printf ("La moyenne de 16, 128, 14, 22, 10, 25 et 39 vaut : %f",
res);
printf ("\n\n\n\n");
FctBizarre2 (6, 'a', 'b', 'c', 'e', 'f');
printf ("\n\n");
FctBizarre2 (11, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
printf ("\n\n\n\n");
FctBizarre3 (6, "aaaaaaaaa", "bbbbbbbbb", "ccccccccc", "ddddddddd",
"efefefefefefef");
printf ("\n\n");
FctBizarre3 (11, "AAAAAAAAA", "BBBBB", "CC", "DDDDDDDDD",
"EEEEEEEEEEEEEEEEEEEE", "FFFFFFFFF", "GGGGGGGGG", "HHHHHHHHH",
"IIIIIIIII", "JJJJJJJJJ");
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 163


Notes provisoires Logique et technique de programmation

float FctBizarre (int a, ...)


{
int i;
float somme=0, moyenne=0;

if (a > 1)
{
for (i = 1; i < a ; i++)
{
somme += *(&a+i);
}
moyenne = somme/(a-1);
}
return moyenne;
}

void FctBizarre2 (int a, char c, ...)


{
int i;

printf ("Voici la liste des caracteres donnes : %c", c);


for (i = 1; i < a-1; i++)
{
printf (", %c", *(&c+i*4));
}
}

void FctBizarre3 (int a, char tab[10], ...)


{
int i;

printf ("Voici les chaines de caracteres entrees : \n\t%s\n", tab);


for (i = 1; i < a-1; i++)
{
printf ("\t%s\n", *(&tab+i));
}
}

2.2. Deuxième exemple


Cet exemple implémente un version simplifiée de la fonction printf. Le premier
paramètre, qui est un paramètre obligatoire, contient le format d'affichage. Ce format
d'affichage contient le texte ainsi qu'éventuellement un certain nombre de "%d", "%f" ou
"%s". La liste des autres paramètres doit bien évidemment, comme pour la fonction printf,
contenir le même nombre d'arguments. On pourra alors avoir des paramètres de type
différents
Le ficher d'en-tête <stdarg.h> contient un ensemble de définitions de macros qui
indiquent comment parcourir une liste d'arguments. L'implémentation de ce fichier d'en-tête
dépend de la machine utilisée, mais il présente une interface standard.
Le type va_list sert à déclarer une variable qui sera associée à chaque argument à tour
de rôle; dans minprintf, cette variable s'appele pa, pour "pointeur d'argument". La macro
va_start initialise pa de façon à ce qu'elle pointe sur le premier argment non nommé. Il faut
l'appeler une seule fois avant d'utiliser pa. Il doit y avoir au moins un argument nommé;
va_start se sert du dernier argument nommé pour l'initialisation.
Chaque appel de va_arg retourne un argument et fait pointer pa sur le suivant; pa_arg a
besoin d'un nom de type pour détermnier le type de la valeur de retour et la taille du pas pour

Page 164 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

pointer sur l'argument suivant. Enfin, va_end réalise le nettoyage nécessaire. Il faut
l'appeler avant que la fonction rende la main.

#include <stdarg.h>
#include <stdio.h>

void minprintf (char *fmt, ...);

void main(void)
{
int i = 5;
double euro = 40.3399;
char nom[]="Leonard";

printf ("Voici le resultat de l\'operation minprintf :\n");


minprintf ("La valeur de i est %d, la valeur de euro est %f, et le
nom : %s", i, euro, nom);
printf ("\n\n");
}

void minprintf (char *fmt, ...)


{
va_list pa;

char *p, *vals;


int vali;
double valf;

va_start (pa, fmt);

for (p = fmt; *p; p++)


{
if (*p != '%')
{
putchar (*p);
}
else
switch (*++p)
{
case 'd' :
vali = va_arg(pa, int);
printf ("%d", vali);
break;
case 'f' :
valf = va_arg(pa, double);
printf ("%f", valf);
break;
case 's' :
for (vals = va_arg(pa, char*); *vals; vals++)
putchar (*vals);
break;
default :
putchar (*p);
break;
}
}

va_end (pa);
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 165


Notes provisoires Logique et technique de programmation

3. UNION
Une union, tout comme une structure, permet de créer un nouveau type de variables.
La déclaration du TYPE d'une union ne prend pas de place en mémoire et doit être
globale afin d'être connue de toutes les fonctions susceptibles de l'utiliser. (Même principe
que pour les structures).
La déclaration d'une variable de type union réserve de la place en mémoire et doit se
faire en local (à nouveau même principe que pour les variables de type structuré).

Une variable de type union permet de stocker des valeurs de types différents.

Exemple :

union Deux_types {
int val_entiere;
char val_car;
};

main ()
{
union Deux_types a, b;

a.val_entiere = 10;
b.val_car = 'I';

printf ("%d", a.val_entiere); /* affiche 10 */


printf ("%c", b.val_car); /* affiche I */
printf ("%c", a.val_car); /* affiche ??
code ASCII du 1er octet de a */
printf ("%d", b.val_entiere); /* affiche ?? */
}

Les compilateurs qui respectent la norme ANSI réservent pour chaque variable de type
Union un nombre d'octets égal à la taille du champ le plus grand.
Cependant, certains compilateurs se contentent de réserver un nombre d'octets égal à
la taille du 1er champ. C'est pourquoi il est préférable de commencer la définition d'une union
par le champ le plus grand.

Comme tous les autres types de variables, une variable de type union a une adresse. Il
s'agit de l'adresse du 1er octet qui la compose. Il y a moyen de déclarer un pointeur d'un type
d'union et de lui affecter l'adresse d'une variable de ce type.

Exemple :

union Deux_types {
int val_entiere;
char val_car;
};

main ()
{
union Deux_types a, *pa;

pa = &a;

Page 166 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

...

pa->val_entiere = 20;

...
}

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 167


Notes provisoires Logique et technique de programmation

Page 168 V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier


Logique et technique de programmation Notes provisoires

Chapitre 21 :

Bibliographie

Maîtrise du Turbo C
Gérard Leblanc
Edition Eyrolles 1989

Langage C
Claude Delannoy
Editions Eyrolles

Le Langage C
2° Edition
B. W. Kernighan et D. M. Ritchie
Editions Dunod Paris 2000

Visual C++ 6
Ivor Horton
Edition Eyrolles 2000

Programmation en C
Cours et Problèmes
Byron S. Gottfried
Serie Schaum

V. Jacquet, G. Leblanc, A. Léonard, F. Missa, P. Quettier Page 169

Vous aimerez peut-être aussi