Vous êtes sur la page 1sur 63

ECOLE NATIONALE SUPERIEURE D’ELECTRICITE ET DE

MECANIQUE CASABLANCA

SUPPORT DE COURS

PROGRAMMATION EN C

Année Universitaire : 2011 / 2012


Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
INTRODUCTION
Historique
Dans les dernières années, aucun langage de programmation n'a pu se vanter d'une croissance en popularité comparable à
celle de C et de son jeune frère C++. L'étonnant dans ce fait est que le langage C n'est pas un nouveau-né dans le monde
informatique, mais qu'il trouve ses sources en 1972 dans les 'Bell Laboratories': Pour développer une version portable du
système d'exploitation UNIX, Dennis M. Ritchie a conçu ce langage de programmation structuré, mais très 'près' de la
machine.
K&R-C
En 1978, le duo Brian W. Kernighan / Dennis M. Ritchie a publié la définition classique du langage C (connue sous le nom
de standard K&R-C ) dans un livre intitulé 'The C Programming Language'.
ANSI-C
Le succès des années qui suivaient et le développement de compilateurs C par d'autres maisons ont rendu nécessaire la
définition d'un standard actualisé et plus précis. En 1983, le 'American National Standards Institute' (ANSI) chargeait une
commission de mettre au point 'une définition explicite et indépendante de la machine pour le langage C', qui devrait quand
même conserver l'esprit du langage. Le résultat était le standard ANSI-C. La seconde édition du livre 'The C Programming
Language', parue en 1988, respecte tout à fait le standard ANSI-C et elle est devenue par la suite, la 'bible' des
programmeurs en C.
C++
En 1983 un groupe de développeurs de AT&T sous la direction de Bjarne Stroustrup a créé le langage C++. Le but était de
développer un langage qui garderait les avantages de ANSI-C (portabilité, efficience) et qui permettrait en plus la
programmation orientée objet. Depuis 1990 il existe une ébauche pour un standard ANSI-C++. Entre-temps AT&T a
développé deux compilateurs C++ qui respectent les nouvelles déterminations de ANSI et qui sont considérés comme des
quasi-standards (AT&T-C++ Version 2.1 [1990] et AT&T-C++ Version 3.0 [1992]).

Avantages
Le grand succès du langage C s'explique par les avantages suivants; C est un langage:
(1) universel :
C n'est pas orienté vers un domaine d'applications spéciales, comme par exemple FORTRAN (applications scientifiques et
techniques) ou COBOL (applications commerciales ou traitant de grandes quantités de données).
(2) compact :
C est basé sur un noyau de fonctions et d'opérateurs limité, qui permet la formulation d'expressions simples, mais efficaces.
(3) moderne :
C est un langage structuré, déclaratif et récursif; il offre des structures de contrôle et de déclaration comparables à celles
des autres grands langages de ce temps (FORTRAN, ALGOL68, PASCAL).
(4) près de la machine :
comme C a été développé en premier lieu pour programmer le système d'exploitation UNIX, il offre des opérateurs qui sont
très proches de ceux du langage machine et des fonctions qui permettent un accès simple et direct aux fonctions internes de
l'ordinateur (p.ex: la gestion de la mémoire).
(5) rapide :
comme C permet d'utiliser des expressions et des opérateurs qui sont très proches du langage machine, il est possible de
développer des programmes efficients et rapides.
(6) indépendant de la machine :
bien que C soit un langage près de la machine, il peut être utilisé sur n'importe quel système en possession d'un compilateur
C. Au début C était surtout le langage des systèmes travaillant sous UNIX, aujourd'hui C est devenu le langage de
programmation standard dans le domaine des micro-ordinateurs.
(7) portable :
en respectant le standard ANSI-C, il est possible d'utiliser le même programme sur tout autre système (autre hardware,
autre système d'exploitation), simplement en le recompilant.
(8) extensible :
C ne se compose pas seulement des fonctions standard; le langage est animé par des bibliothèques de fonctions privées ou
livrées par de nombreuses maisons de développement.

Désavantages
Evidemment, rien n'est parfait. Jetons un petit coup d'oeil sur le revers de la médaille:
(1) efficience et compréhensibilité :
En C, nous avons la possibilité d'utiliser des expressions compactes et efficientes. D'autre part, nos programmes doivent
rester compréhensibles pour nous-mêmes et pour d'autres. Comme nous allons le constater sur les exemples suivants, ces
deux exigences peuvent se contredire réciproquement.

Page 2
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Exemple 1
Les deux lignes suivantes impriment les N premiers éléments d'un tableau A[], en insérant un espace entre les éléments et
en commençant une nouvelle ligne après chaque dixième chiffre:
for (i=0; i<n; i++)
printf("%6d%c", a[i], (i%10==9)?'\n':' ');
Cette notation est très pratique, mais plutôt intimidante pour un débutant. L'autre variante, plus près de la notation en
Pascal, est plus lisible, mais elle ne profite pas des avantages du langage C:
for (I=0; I<N; I=I+1)
{
printf("%6d", A[I]);
if ((I%10) == 9)
printf("\n");
else
printf(" ");
}
Exemple 2
La fonction copietab() copie les éléments d'une chaîne de caractères T[] dans une autre chaîne de caractères S[]. Voici
d'abord la version 'simili-Pascal' :
void copietab(char S[], char T[])
{
int I;
I=0;
while (T[I] != '\0')
{
S[I] = T[I];
I = I+1;
}
}
Cette définition de la fonction est valable en C, mais en pratique elle ne serait jamais programmée ainsi. En utilisant les
possibilités de C, un programmeur expérimenté préfère la solution suivante:
void copietab(char *S, char *T)
{
while (*S++ = *T++);
}
La deuxième formulation de cette fonction est élégante, compacte, efficace et la traduction en langage machine fournit un
code très rapide; mais bien que cette manière de résoudre les problèmes soit le cas normal en C, il n'est pas si évident de
suivre le raisonnement.
Conclusions
Bien entendu, dans les deux exemples ci-dessus, les formulations 'courtes' représentent le bon style dans C et sont de loin
préférables aux deux autres. Nous constatons donc que la programmation efficiente en C nécessite beaucoup d'expérience
et n'est pas facilement accessible à des débutants. sans commentaires ou explications, les programmes peuvent devenir
incompréhensibles, donc inutilisables.

(2) portabilité et bibliothèques de fonctions


Les limites de la portabilité : La portabilité est l'un des avantages les plus importants de C: en écrivant des programmes qui
respectent le standard ANSI-C, nous pouvons les utiliser sur n'importe quelle machine possédant un compilateur ANSI-C.
D'autre part, le répertoire des fonctions ANSI-C est assez limité. Si un programmeur désire faire appel à une fonction
spécifique de la machine (p.ex: utiliser une carte graphique spéciale), il est assisté par une foule de fonctions
'préfabriquées', mais il doit être conscient qu'il risque de perdre la portabilité. Ainsi, il devient évident que les avantages
d'un programme portable doivent être payés par la restriction des moyens de programmation.

(3) discipline de programmation


Les dangers de C : Nous voici arrivés à un point crucial: C est un langage près de la machine, donc dangereux et bien que C
soit un langage de programmation structuré, il ne nous force pas à adopter un certain style de programmation (comme p.ex.
Pascal). Dans un certain sens, tout est permis et la tentation de programmer du 'code spaghetti' est grande. (Même la
commande 'goto', si redoutée par les puristes ne manque pas en C). Le programmeur a donc beaucoup de libertés, mais
aussi des responsabilités: il doit veiller lui-même à adopter un style de programmation propre, solide et compréhensible.

Remarque
Au fil de l'introduction du langage C, ce manuel contiendra quelques recommandations au sujet de l'utilisation des
différents moyens de programmation. Il est impossible de donner des règles universelles à ce sujet, mais les deux conseils
suivants sont valables pour tous les langages de programmation

Page 3
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Chapitre 1
L'ENVIRONNEMENT ET LES BIBLIOTHEQUES

Pour le travail pratique en C, il faut utiliser un compilateur et un éditeur facile à utiliser. A titre d'exemple on décrit ici
l'utilisation de l'environnement Borland C++ (Version 3.1). Ce programme nous offre une surface de programmation
confortable et rapide. Tout autre compilateur qui permet la programmation selon le standard ANSI-C fait aussi bien
l'affaire.

1.1. Description de l'environnement Borland C++


Borland C++ est une implémentation complète du standard C++ AT&T (Version 2.1). Le compilateur Borland C++ est
capable de produire du code C 'pur' selon la définition Kernighan & Ritchie ou selon le standard ANSI-C. D'autre part,
Borland C++ nous offre toute une série de bibliothèques, qui (parfois aux dépens de la portabilité) nous permettent
d'exploiter les capacités du PC. Ce chapitre se limite à une brève description des menus de Borland C++.

1.1.1. Les menus


- FILE gestion des fichiers, retour au DOS
- EDIT commandes d'édition du texte
- SEARCH recherche d'un texte, d'une déclaration de fonction, de la position d'une erreur dans le
programme
- RUN exécution d'un programme en entier ou par parties
- COMPILE traduction et/ou enchaînement (link) des programmes
- DEBUG détection d'erreurs en inspectant les données, en insérant des points d'observation (watch) ou
en insérant des points d'arrêt (breakpoints)
- PROJECT gestion de projets
- OPTIONS changement et sauvetage des réglages par défaut:
* des menus
* des options du compilateur
* de l'environnement
- WINDOW visualisation et disposition des différentes fenêtres (Message, Output, Watch, User, Project)
sur l'écran
- HELP système d'aide

1.1.2. Les nouveautés


a) L'interface utilisateur:
L'environnement Borland C++ nous permet l'utilisation confortable de plusieurs fenêtres sur l'écran. L'interaction des
données des différentes fenêtres (Message, Output, Watch, User Screen) a rendu la recherche d'erreurs très efficace.
b) La gestion de projets multi-fichiers:
En pratique, les programmes sont souvent subdivisés en plusieurs sous-programmes ou modules, qui peuvent être répartis
dans plusieurs fichiers. L'environnement Borland C++ nous offre un gestionnaire 'Project Manager' qui détecte
automatiquement les fichiers qui doivent être recompilés et enchaînés après une modification. Dans nos applications, nous
allons uniquement utiliser des fonctions définies dans le même fichier que notre programme principal ou prédéfinies dans
une bibliothèque standard; ainsi, nous n'aurons pas besoin de l'option 'Project'.
c) Le système d'aide:
Borland C++ est accompagné d'un programme d'aide puissant qui est appelé directement à partir de l'éditeur. Le système
d'aide peut donner des informations ponctuelles sur un sujet sélectionné ou nous laisser choisir un sujet à partir d'une liste
alphabétique:
F1 Aide sur l'environnement Borland C++
Shift-F1 Index alphabétique des mots-clefs
Ctrl-F1 Aide sur le mot actif (à la position du curseur)
1.2. Sélectionner le compilateur ANSI-C
Comme nous utilisons l'environnement Borland C++ pour compiler des programmes correspondant au standard ANSI-C,
nous devons d'abord changer le mode de compilation par défaut:
- Choisir le menu 'compilateur' par: Alt-Options|Compiler
- Choisir le modèle ANSI-C par: Source|ANSI|OK

1.3. Les bibliothèques de fonctions prédéfinies


Utilisation des bibliothèques de fonctions
La pratique en C exige l'utilisation de bibliothèques de fonctions. Ces bibliothèques sont disponibles dans leur forme
précompilée (extension: .LIB). Pour pouvoir les utiliser, il faut inclure des fichiers en-tête (header files - extension .H) dans

Page 4
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
nos programmes. Ces fichiers contiennent des 'prototypes' des fonctions définies dans les bibliothèques et créent un lien
entre les fonctions précompilées et nos programmes.
#include
L'instruction #include insère les fichiers en-tête indiqués comme arguments dans le texte du programme au moment de la
compilation.
Identification des fichiers
Lors de la programmation en Borland C, nous travaillons donc avec différents types de fichiers qui sont identifiés par leurs
extensions:
*.C : fichiers source
*.OBJ : fichiers compilés (versions objet)
*.EXE : fichiers compilés et liés (versions exécutables)
*.LIB : bibliothèques de fonctions précompilées
*.H : fichiers en-tête (header files)
Exemple
Nous avons écrit un programme qui fait appel à des fonctions mathématiques et des fonctions graphiques prédéfinies. Pour
pouvoir utiliser ces fonctions, le programme a besoin des bibliothèques:
MATHS.LIB
GRAPHICS.LIB
Nous devons donc inclure les fichiers en-tête correspondants dans le code source de notre programme à l'aide des
instructions:
#include <math.h>
#include <graphics.h>

Après la compilation, les fonctions precompilées des bibliothèques seront ajoutées à notre programme pour former une
version exécutable du programme (voir schéma).
Remarque: La bibliothèque de fonctions graphics.h est spécifique aux fonctionnalités du PC et n'est pas incluse dans le
standard ANSI-C

Schéma: Bibliothèques de fonctions et compilation

Page 5
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Chapitre 2
NOTIONS DE BASE
Avant de pouvoir comprendre ou même écrire des programmes, il faut connaître la composition des programmes dans le
langage de programmation. Dans ce chapitre, nous allons discuter un petit programme en mettant en évidence les structures
fondamentales d'un programme en C.

1. Hello C !
Suivons la tradition et commençons la découverte de C par l'inévitable programme 'hello world'. Ce programme ne fait rien
d'autre qu'imprimer les mots suivants sur l'écran:

HELLO_WORLD en C
#include <stdio.h>
main()
/* Notre premier programme en C */
{
printf("hello, world\n");
return 0;
}
Dans la suite du chapitre, nous allons discuter les détails de cette implémentation.

2. Les composantes d'un programme en C


Programmes, fonctions et variables
Les programmes en C sont composés essentiellement de fonctions et de variables. Pour la pratique, il est donc
indispensable de se familiariser avec les caractéristiques fondamentales de ces éléments.

2.2.1. Les fonctions


En C, le programme principal et les sous-programmes sont définis comme fonctions. Il n'existe pas de structures spéciales
pour le programme principal ni les procédures (comme en Pascal ou en langage algorithmique). Le programme principal
étant aussi une 'fonction', nous devons nous intéresser dès le début à la définition et aux caractéristiques des fonctions en C.
Définition d'une fonction en C
<TypeRés> <NomFonct> (<TypePar1> <NomPar1>,
<TypePar2> <NomPar2>, ... )
{
<déclarations locales>
<instructions>
}
En C, une fonction est définie par:
* une ligne déclarative qui contient: <TypeRés> - le type du résultat de la fonction <NomFonct> - le nom de la fonction
<TypePar1> <NomPar1>, <TypePar2> <NomPar2>, ... et les types et les noms des paramètres de la fonction un bloc
d'instructions délimité par des accolades { }, contenant: <déclarations locales> - les déclarations des données locales (c.-à-
d.: des données qui sont uniquement connues à l'intérieur de la fonction) <instructions> - la liste des instructions qui définit
l'action qui doit être exécutée

Résultat d'une fonction


Par définition, toute fonction en C fournit un résultat dont le type doit être défini. Si aucun type n'est défini explicitement,
C suppose par défaut que le type du résultat est int (integer). Le retour du résultat se fait en général à la fin de la fonction
par l'instruction return. Le type d'une fonction qui ne fournit pas de résultat (comme les procédures en langage
algorithmique ou en Pascal), est déclaré comme void (vide).

Paramètres d'une fonction


La définition des paramètres (arguments) d'une fonction est placée entre parenthèses ( ) derrière le nom de la fonction. Si
une fonction n'a pas besoin de paramètres, les parenthèses restent vides ou contiennent le mot void. La fonction minimale
qui ne fait rien et qui ne fournit aucun résultat est alors:
void dummy() {}

Page 6
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Instructions
En C, toute instruction simple est terminée par un point-virgule; (même si elle se trouve en dernière position dans un bloc
d'instructions). Par exemple:
printf("hello, world\n");

2.2. La fonction main


La fonction main est la fonction principale des programmes en C: Elle se trouve obligatoirement dans tous les programmes.
L'exécution d'un programme entraîne automatiquement l'appel de la fonction main

Définition de la fonction main en C


|main()
|{
| <déclarations>
| <instructions>
| return 0;
|}
Résultat de main
- En principe tout programme devrait retourner une valeur comme code d'erreur à son environnement. Par conséquent, le
type résultat de main est toujours int. En général, le type de main n'est pas déclaré explicitement, puisque c'est le type par
défaut. Nous allons terminer nos programmes par l'instruction: return 0; qui indique à l'environnement que le programme
s'est terminé avec succès, sans anomalies ou erreurs fatales.
Paramètres de main
- Si la liste des paramètres de la fonction main est vide, il est d'usage de la déclarer par ().
- Si nous utilisons des fonctions prédéfinies (par exemple: printf), il faut faire précéder la définition de main par les
instructions #include correspondantes.

Remarque avancée:
Il est possible de faire passer des arguments de la ligne de commande à un programme. Dans ce cas, la liste des paramètres
doit contenir les déclarations correspondantes. Dans notre cours, nous n'allons pas utiliser des arguments de la ligne de
commande. Ainsi la liste des paramètres de la fonction main sera vide (void) dans tous nos exemples et nous pourrons
employer la déclaration suivante qui fait usage des valeurs par défaut:
main() { ... }
Exercice 2.1
Comparez la syntaxe de la définition d'une fonction en C avec celle des fonctions et des procédures dans Pascal. (Basez-
vous sur les observations mentionnées ci-dessus.)

2.3. Les variables


Les variables contiennent les valeurs qui sont utilisées pendant l'exécution du programme. Les noms des variables sont des
identificateurs quelconques (voir 2.2.4). Les différents types de variables simples et les opérateurs admissibles sont discutés
au chapitre 3.

2.4. Les identificateurs


Les noms des fonctions et des variables en C sont composés d'une suite de lettres et de chiffres. Le premier caractère doit
être une lettre. Le symbole '_' est aussi considéré comme une lettre.
* L'ensemble des symboles utilisables est donc: {0,1,2,...,9,A,B,...,Z,_,a,b,...,z}
* Le premier caractère doit être une lettre (ou le symbole '_') (voir Remarques)
* C distingue les majuscules et les minuscules, ainsi: 'Nom_de_variable' est différent de 'nom_de_variable'
* La longueur des identificateurs n'est pas limitée, mais C distingue 'seulement' les 31 premiers caractères. (voir
Remarques)

Remarques:
- Il est déconseillé d'utiliser le symbole '_' comme premier caractère pour un identificateur, car il est souvent employé pour
définir les variables globales de l'environnement C.
- Le standard dit que la validité de noms externes (p.ex. noms de fonctions ou var. globales) peut être limité à 6 caractères
(même sans tenir compte des majuscules et minuscules) par l'implémentation du compilateur, mais tous les compilateurs
modernes distinguent au moins 31 caractères de façon à ce que nous pouvons généraliser qu'en pratique les règles ci-dessus
s'appliquent à tous les identificateurs.
Exemples
Identificateurs corrects : Identificateurs incorrects
nom1 : 1nom
nom_2 : nom.2
_nom_3 : -nom-3
Nom_de_variable : Nom de variable
Page 7
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
deuxieme_choix : deuxième_choix
mot_francais : mot_français

Exercice 2.2
Lesquels des identificateurs suivants sont acceptés par C ?
fonction-1 _MOYENNE_du_MOIS_ 3e_jour limite_inf. lim_supérieure __A_ a 3

2.5. Les commentaires


Un commentaire commence toujours par les deux symboles '/*' et se termine par les symboles '*/'. Il est interdit d'utiliser
des commentaires imbriqués.
Exemples
/* Ceci est un commentaire correct */
/* Ceci est /* évidemment */ défendu */

3. Discussion de l'exemple 'Hello_World'


Reprenons le programme 'Hello_World' et retrouvons les particularités d'un programme en C.

HELLO_WORLD en C
#include <stdio.h>
main()
/* Notre premier programme en C */
{
printf("hello, world\n");
return 0;
}
Discussion
- La fonction main ne reçoit pas de données, donc la liste des paramètres est vide.
- La fonction main fournit un code d'erreur numérique à l'environnement, donc le type du résultat est int et n'a pas besoin
d'être déclaré explicitement.
- Le programme ne contient pas de variables, donc le bloc de déclarations est vide.
- La fonction main contient deux instructions:
* l'appel de la fonction printf avec l'argument "hello, world\n"; Effet: Afficher la chaîne de caractères "hello world\n".
* la commande return avec l'argument 0; Effet: Retourner la valeur 0 comme code d'erreur à l'environnement.
- L'argument de la fonction printf est une chaîne de caractères indiquée entre les guillemets. Une telle suite de caractères
est appelée chaîne de caractères constante (string constant).
- La suite de symboles '\n' à la fin de la chaîne de caractères "hello, world\n" est la notation C pour 'passage à la ligne'
(angl: new line). En C, il existe plusieurs couples de symboles qui contrôlent l'affichage ou l'impression de texte. Ces
séquences d'échappement sont toujours précédées par le caractère d'échappement '\'. (voir exercice 2.4).

printf et la bibliothèque <stdio>


La fonction printf fait partie de la bibliothèque de fonctions standard <stdio> qui gère les entrées et les sorties de
données.La première ligne du programme: #include <stdio.h> instruit le compilateur d'inclure le fichier en-tête
'STDIO.H' dans le texte du programme. Le fichier 'STDIO.H' contient les informations nécessaires pour pouvoir utiliser
les fonctions de la bibliothèque standard <stdio> (voir chapitre 1.3).

Exercice 2.3
Modifiez le programme 'hello world' de façon à obtenir le même résultat sur l'écran en utilisant plusieurs fois la fonction
printf.

Exercice 2.4
Expérimentez avec les séquences d'échappement que vous trouvez dans le tableau ci-dessous et complétez les colonnes
vides.
séq. d'échapp. description. anglaise description. française
\n new line passage à la ligne
\t
\b
\r
\"
\\
\0
\a

Page 8
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Exercice 2.5
Ci-dessous, vous trouvez un simple programme en C. Essayez de distinguer et de classifier autant que possible les éléments
qui composent ce programme (commentaires, variables, déclarations, instructions, etc.)
#include <stdio.h>
/* Ce programme calcule la somme de 4 nombres entiers
introduits au clavier.
*/
main()
{
int NOMBRE, SOMME, COMPTEUR;
/* Initialisation des variables */
SOMME = 0;
COMPTEUR = 0;
/* Lecture des données */
while (COMPTEUR < 4)
{
/* Lire la valeur du nombre suivant */
printf("Entrez un nombre entier :");
scanf("%i", &NOMBRE);
/* Ajouter le nombre au résultat */
SOMME += NOMBRE;
/* Incrémenter le compteur */
COMPTEUR++;
}
/* Impression du résultat */
printf("La somme est: %i \n", SOMME);
return 0;
}

Page 9
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
CHAPITRE 3
TYPES DE BASE, OPERATEURS ET EXPRESSIONS
Récapitulation du vocabulaire
Les variables et les constantes sont les données principales qui peuvent être manipulées par un
programme. Les déclarations introduisent les variables qui sont utilisées, fixent leur type et parfois
aussi leur valeur de départ. Les opérateurs contrôlent les actions que subissent les valeurs des données.
Pour produire de nouvelles valeurs, les variables et les constantes peuvent être combinées à l'aide des
opérateurs dans des expressions. Le type d'une donnée détermine l'ensemble des valeurs admissibles,
le nombre d'octets à réserver en mémoire et l'ensemble des opérateurs qui peuvent y être appliqués.
Motivation
La grande flexibilité de C nous permet d'utiliser des opérandes de différents types dans un même
calcul. Cet avantage peut se transformer dans un terrible piège si nous ne prévoyons pas correctement
les effets secondaires d'une telle opération (conversions de type automatiques, arrondissements, etc.).
Une étude minutieuse de ce chapitre peut donc aider à éviter des phénomènes parfois 'inexplicables'

3.1. Les types simples


Ensembles de nombres et leur représentation
En mathématiques, nous distinguons divers ensembles de nombres:
* l'ensemble des entiers naturels (N),
* l'ensemble des entiers relatifs (Z),
* l'ensemble des rationnels (Q),
* l'ensemble des réels (R).
En mathématiques, l'ordre de grandeur des nombres est illimité et les rationnels peuvent être exprimés sans perte de
précision. Un ordinateur ne peut traiter aisément que des nombres entiers d'une taille limitée. Il utilise le système binaire
pour calculer et sauvegarder ces nombres. Ce n'est que par des astuces de calcul et de représentation que l'ordinateur obtient
des valeurs correctement approchées des entiers très grands, des réels ou des rationnels à partie décimale infinie.
Les charges du programmeur
Même un programmeur utilisant C ne doit pas connaître tous les détails des méthodes de codage et de calcul, il doit quand
même être capable de:
- choisir un type numérique approprié à un problème donné (càd trouver un optimum de précision, de temps de calcul et
d'espace à réserver en mémoire) ;
- choisir un type approprié pour la représentation sur l'écran ;
- prévoir le type résultant d'une opération entre différents types numériques (càd connaître les transformations automatiques
de type que C accomplit lors des calculs (voir 3.7.1)) ;
- prévoir et optimiser la précision des résultats intermédiaires au cours d'un calcul complexe (càd changer si nécessaire
l'ordre des opérations ou forcer l'ordinateur à utiliser un type numérique mieux adapté (casting: voir 3.7.2)).

Exemple
Supposons que la mantisse du type choisi ne comprenne que 6 positions décimales (ce qui est très réaliste pour le type
float):

3.1.1. Les types entiers (int)


Avant de pouvoir utiliser une variable, nous devons nous intéresser à deux caractéristiques de son type numérique:
(1) le domaine des valeurs admissibles
(2) le nombre d'octets qui est réservé pour une variable
Le tableau suivant résume les caractéristiques des types numériques entiers de C :

définition description domaine min domaine max nombre d'octets

Page 10
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
char caractère -128 127 1
short Entier court -32768 32767 2
int entier -32768 32767 2
long Entier double -2147483648 +2147483647 4

- char : caractère
Une variable du type char peut contenir une valeur entre -128 et 127 et elle peut subir les mêmes opérations que les
variables du type short, int ou long.
- int : entier standard
Sur chaque machine, le type int est le type de base pour les calculs avec les entiers. Le codage des variables du type int est
donc dépendant de la machine. Sur les IBM-PC sous MS-DOS, une variable du type int est codée dans deux octets.
- short : entier court
Le type short est en général codé dans 2 octets. Comme une variable int occupe aussi 2 octets sur notre système, le type
short devient seulement nécessaire, si on veut utiliser le même programme sur d'autres machines, sur lesquelles le type
standard des entiers n'est pas forcément 2 octets.
- Les modificateurs signed/unsigned:
Si on ajoute le préfixe unsigned à la définition d'un type de variables entières, les domaines des variables sont déplacés
comme suit:

définition description domaine min domaine max nombre d'octets


Unsigned char caractère 0 255 1
Unsigned short Entier court 0 65535 2
Unsigned int Entier standard 0 65535 2
Unsigned long Entier double 0 4294967295 4

Remarques
1. Le calcul avec des entiers définis comme unsigned correspond à l'arithmétique modulo 2 n. Ainsi, en utilisant une
variable X du type unsigned short, nous obtenons le résultat suivant:
Affectation : X = 65500 + 100
Résultat : X = 64 /* [+216] */
2. Par défaut, les types entiers short, int, long sont munis d'un signe. Le type par défaut de char est dépendant du
compilateur et peut être signed ou unsigned. Ainsi, l'attribut signed a seulement un sens en liaison avec char et peut
forcer la machine à utiliser la représentation des caractères avec signe (qui n'est cependant pas très usuelle).
3. Les valeurs limites des differents types sont indiquées dans le fichier header <limits.h>.
4. En principe, on peut dire que
sizeof(short) <= sizeof(int) <= sizeof(long)
Ainsi sur certaine architecture on peut avoir: short = 2 octets, int = 2 octets, long = 4 octets et sur d'autre: short = 2 octets,
int = 4 octets, long = 4 octets

3.1.2. Les types rationnels (float)


En informatique, les rationnels sont souvent appelés des 'flottants'. Ce terme vient de 'en virgule
flottante' et trouve sa racine dans la notation traditionnelle des rationnels:
<+|-> <mantisse> * 10<exposant>
<+|-> est le signe positif ou négatif du nombre
<mantisse> est un décimal positif avec un seul chiffre devant la virgule.
<exposant> est un entier relatif

Exemples
3.14159*100 1.25003*10-12
4.3001*10321 -1.5*103

En C, nous avons le choix entre trois types de rationnels: float, double et long double. Dans le tableau ci-dessous, vous
trouverez leurs caractéristiques:
min et max : représentent les valeurs minimales et maximales positives. Les valeurs négatives peuvent varier dans les
mêmes domaines et Mantisse : indique le nombre de chiffres significatifs de la mantisse.

définition précision mantisse domaine min domaine max nombre d'octets


float simple 6 3.4 * 10-38 3.4 * 1038 4
double double 15 1.7 * 10-308 1.7 * 10308 8
Page 11
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
long double suppl. 19 3.4 * 10
-4932
1.1 * 10
4932
10
Remarque avancée
Les détails de l'implémentation sont indiqués dans le fichier header <float.h>.
Exercice 3.1
Quel(s) type(s) numérique(s) pouvez-vous utiliser pour les groupes de nombres suivants ? Dressez un tableau et marquez le
choix le plus économique:

(1) 1 12 4 0 -125
(2) 1 12 -4 0 250
(3) 1 12 4 0 250
(4) 1 12 -4 0.5 125
(5) -220 32000 0
(6) -3000005.000000001
(7) 410 50000 2
(8) 410 50000 -2
(9) 3.14159265 1015
(10) 2*107 10000001
(11) 2*10-7 10000001
(12) -1.05*1050 0.0001
(13) 305.122212 0 -12

3.2. La déclaration des variables simples


Maintenant que nous connaissons les principaux types de variables, il nous faut encore la syntaxe pour
leur déclaration:
Déclaration de variables en langage algorithmique
<Type> <NomVar1>,<NomVar2>,...,<NomVarN>
Déclaration de variables en C
<Type> <NomVar1>,<NomVar2>,...,<NomVarN>;

Prenons quelques déclarations du langage descriptif,


entier COMPTEUR,X,Y
réel HAUTEUR,LARGEUR,MASSE_ATOMIQUE
caractère TOUCHE
booléen T_PRESSEE
et traduisons-les en des déclarations du langage C :
int compteur,X,Y;
float hauteur,largeur;
double masse_atomique;
char touche;
int t_pressee;

Langage algorithmique --> C


En général, nous avons le choix entre plusieurs types et nous devons trouver celui qui correspond le mieux au domaine et
aux valeurs à traiter. Voici quelques règles générales qui concernent la traduction des déclarations de variables numériques
du langage algorithmique en C :
- La syntaxe des déclarations en C ressemble à celle du langage algorithmique. Remarquez quand même les points-virgules
à la fin des déclarations en C.
entier : Nous avons le choix entre tous les types entiers (inclusivement char) dans leurs formes signed ou unsigned. Si les
nombres deviennent trop grands pour unsigned long, il faut utiliser un type rationnel (p.ex: double)
réel : Nous pouvons choisir entre les trois types rationnels en observant non seulement la grandeur maximale de l'exposant,
mais plus encore le nombre de chiffres significatifs de la mantisse.
caractère : Toute variable du type char peut contenir un (seul) caractère. En C, il faut toujours être conscient que ce
'caractère' n'est autre chose qu'un nombre correspondant à un code (ici: code ASCII). Ce nombre peut être intégré dans
toute sorte d'opérations algébriques ou logiques ...
chaîne : En C il n'existe pas de type spécial pour chaînes de caractères. Les moyens de traiter les chaînes de caractères
seront décrits au chapitre 8.
booléen : En C il n'existe pas de type spécial pour variables booléennes. Tous les types de variables numériques peuvent
être utilisés pour exprimer des opérations logiques:
valeur logique faux <=> valeur numérique zéro
valeur logique vrai <=> toute valeur différente de zéro
Si l'utilisation d'une variable booléenne est indispensable, le plus naturel sera d'utiliser une variable du type int. Les
opérations logiques en C retournent toujours des résultats du type int:
0 pour faux
Page 12
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
1 pour vrai

Exercice 3.2
Traduisez les déclarations suivantes en C, sachant que vous travaillerez dans les ensembles de nombres indiqués.
Choisissez les types les plus économiques, sans perdre en précision.
(1) entier COMPTEUR {0 ,..., 300}
(2) entier X,Y {-120 ,..., 100}
(3) entier MESURE {-10 ,..., 104}
(4) réel SURFACE1 {0.5 ,..., 150075}
(5) réel SURFACE2 {-12 ,..., 1500750.5}
(6) entier N1 {0 ,..., 210}
(7) entier N2 {-47 ,..., 47}
(8) entier N3 {0 ,..., 326}
(9) entier N4 {-1280 ,..., 1285}
(10) booléen TROUVE {vrai, faux}

3.2.1. Les constantes numériques


En pratique, nous utilisons souvent des valeurs constantes pour calculer, pour initialiser des variables, pour les comparer
aux variables, etc. Dans ces cas, l'ordinateur doit attribuer un type numérique aux valeurs constantes. Pour prévoir le
résultat et le type exact des calculs, il est important pour le programmeur de connaître les règles selon lesquelles
l'ordinateur choisit les types pour les constantes.
- Les constantes entières
Type automatique
Lors de l'attribution d'un type à une constante entière, C choisit en général la solution la plus économique: Le type des
constantes entières * Si possible, les constantes entières obtiennent le type int.
* Si le nombre est trop grand pour int (p.ex: -40000 ou +40000) il aura automatiquement le type long.
* Si le nombre est trop grand pour long., il aura le type unsigned long..
* Si le nombre est trop grand pour unsigned long., la réaction du programme est imprévisible.
Type forcé
Si nous voulons forcer l'ordinateur à utiliser un type de notre choix, nous pouvons employer les suffixes suivants:
suffixe type Exemple
u ou U unsigned (int ou long) 550u
l ou L long 123456789L
ul ou UL unsigned long 12092UL

Exemples
12345 type int
52000 type long
-2 type int
0 type int
1u type unsigned int
52000u type unsigned long
22lu Erreur !

Base octale et hexadécimale


Il est possible de déclarer des constantes entières en utilisant la base octale ou hexadécimale:
* Si une constante entière commence par 0 (zéro), alors elle est interprétée en base octale.
* Si une constante entière commence par 0x ou 0X , alors elle est interprétée en base hexadécimale.
Exemples
base décimale base octale base hexadécimale représentation. binaire
100 0144 0X64 1100100
255 0377 0xff 11111111
65536 0200000 0X10000 10000000000000000
12 014 0XC 1100
4040 07710 0xFC8 111111001000

- Les constantes rationnelles


Les constantes rationnelles peuvent être indiquées:
* en notation décimale, càd à l'aide d'un point décimal. Exemples : 123.4 -0.001 1.0
* en notation exponentielle, càd à l'aide d'un exposant séparé du nombre décimal par les caractères 'e' ou 'E'. Exemples :
1234e-1 -1E-3 0.01E2
L'ordinateur reconnaît les constantes rationnelles au point décimal ou au séparateur de l'exposant ('e' ou 'E'). Par défaut, les
constantes rationnelles sont du type double.
Page 13
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Les types des constantes rationnelles Sans suffixe sont du type double.
* Le suffixe f ou F force l'utilisation du type float.
*Le suffixe l ou L force l'utilisation du type long double.

- Les caractères constants


Les constantes qui désignent un (seul) caractère sont toujours indiquées entre des apostrophes: par
exemple 'x'. La valeur d'un caractère constant est le code interne de ce caractère. Ce code (ici: le code
ASCII) est dépendant de la machine. Les caractères constants peuvent apparaître dans des opérations
arithmétiques ou logiques, mais en général ils sont utilisés pour être comparés à des variables.
Séquences d'échappement
Comme nous l'avons vu au chapitre 2, l'impression et l'affichage de texte peut être contrôlé à l'aide de
séquences d'échappement. Une séquence d'échappement est un couple de symboles dont le premier est
le signe d'échappement '\'. Au moment de la compilation, chaque séquence d'échappement est traduite
en un caractère de contrôle dans le code de la machine. Comme les séquences d'échappement sont
identiques sur toutes les machines, elles nous permettent d'écrire des programmes portables, càd des
programmes qui ont le même effet sur toutes les machines, indépendamment du code de caractères
utilisé.
Séquences d'échappement
\a sonnerie \\ trait oblique
\b curseur arrière \? point d'interrogation
\t tabulation \' apostrophe
\n nouvelle ligne \" guillemets
\r retour au début de ligne \f saut de page (imprimante)
\0 NUL \v tabulateur vertical

Le caractère NUL
La constante '\0' qui a la valeur numérique zéro (ne pas à confondre avec le caractère '0' !!) a une signification spéciale dans
le traitement et la mémorisation des chaînes de caractères: En C le caractère '\0' définit la fin d'une chaîne de caractères.

Exercice 3.3
Complétez le tableau suivant:

base décimale base octale base hexadécimale représentation. binaire


01770
8100
0XAAAA
1001001001
1100101011111110
10000
0234
Exercice 3.4
Pour les constantes correctement définies, trouvez les types et les valeurs numériques décimales:
12332 23.4 345lu 34.5L -1.0
0xeba 0123l '\n' 1.23ul -1.0e-1
0FE0 40000 40000u 70000u 1e1f
'0' o '\0' 0 'O'
67e0 \r 01001 0.0l 0XEUL

3.2.2. Initialisation des variables


Initialisation
En C, il est possible d'initialiser les variables lors de leur déclaration:
int MAX = 1023;
char TAB = '\t';
float X = 1.05e-4;
const
En utilisant l'attribut const, nous pouvons indiquer que la valeur d'une variable ne change pas au cours d'un programme:
const int MAX = 767;
const double e = 2.71828182845905;
const char NEWLINE = '\n';

Page 14
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
3.3. Les opérateurs standard
Affectation en C
<NomVariable> = <Expression>;

Exemples d'affectations
- L'affectation avec des valeurs constantes
LONG = 141 ; (const. entière)
PI = 3.1415926; (const. réelle)
NATION = 'L'; (caractère const.)
- L'affectation avec des valeurs de variables
LETTRE = COURRIER;
- L'affectation avec des valeurs d'expressions
AIRE = PI*pow(R,2);
MOYENNE = (A+B)/2;
UN=pow(sin(X),2)+pow(cos(X),2);
RES = 45+5*X;
PLUSGRAND = (X>Y);
CORRECT = ('a' == 'a');

Observations
Les opérateurs et les fonctions arithmétiques utilisées dans les expressions seront introduites dans la suite du chapitre.
Observons déjà que:
* il n'existe pas de fonction standard en C pour calculer le carré d'une valeur; on peut se référer à la fonction plus générale
pow(x,y) qui calcule xy).
* le test d'égalité en C se formule avec deux signes d'égalité == , l'affectation avec un seul = .

3.3.1. Les opérateurs connus


Avant de nous lancer dans les 'spécialités' du langage C, retrouvons d'abord les opérateurs correspondant à ceux que nous
connaissons déjà en langage descriptif et en Pascal.
Opérateurs arithmétiques
+ addition
- soustraction
* multiplication
/ division (entière et rationnelle!)
% modulo (reste d'une division entière)

Opérateurs logiques
&& et logique (and)
|| ou logique (or)
! négation logique (not)

Opérateurs de comparaison
== égal à
!= différent de
<, <=, >, >= plus petit que, ...

Opérations logiques
Les résultats des opérations de comparaison et des opérateurs logiques sont du type int:
- la valeur 1 correspond à la valeur booléenne vrai ;
- la valeur 0 correspond à la valeur booléenne faux
Les opérateurs logiques considèrent toute valeur différente de zéro comme vrai et zéro comme faux:

3.3.2. Les opérateurs particuliers de C


- Les opérateurs d'affectation
En pratique, nous retrouvons souvent des affectations comme: i=i+2
En C, nous utiliserons plutôt la formulation plus compacte: i += 2
L'opérateur += est un opérateur d'affectation. Pour la plupart des expressions de la forme:
expr1 = (expr1) op (expr2)
il existe une formulation équivalente qui utilise un opérateur d'affectation:
expr1 op= expr2

Opérateurs d'affectation
+= ajouter à
Page 15
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
-= diminuer de
*= multiplier par
/= diviser par
%= modulo
Avantages
La formulation avec un opérateur d'affectation est souvent plus près de la logique humaine: Un homme dirait <<Ajoute 2 à
I>> plutôt que <<Ajoute 2 à I et écris le résultat dans I>>. Les opérateurs d'affectation peuvent aider le compilateur à
générer un code plus efficient parce que expr1 n'est évaluée qu'une seule fois. Les opérateurs d'affectation deviennent plus
intéressant si expr1 est une expression complexe. Ceci peut être le cas si nous calculons avec des tableaux.
L'expression:
Element[n*i+j] = Element[n*i+j] * x[j]
peut être formulée de manière plus efficiente et plus claire:
Element[n*i+j] *= x[j]

- Opérateurs d'incrémentation et de décrémentation


Les affectations les plus fréquentes sont du type: I=I+1 et I=I-1
En C, nous disposons de deux opérateurs inhabituels pour ces affectations:
I++ ou ++I pour l'incrémentation (augmentation d'une unité)
I-- ou --I pour la décrémentation (diminution d'une unité)

Les opérateurs ++ et -- sont employés dans les cas suivants:


*Incrémenter/décrémenter une variable (p.ex: dans une boucle). Dans ce cas il n'y a pas de différence entre la notation
préfixe (++I --I) et la notation postfixe (I++ I--).
* Incrémenter/décrémenter une variable et en même temps affecter sa valeur à une autre variable. Dans ce cas, nous devons
choisir entre la notation préfixe et postfixe:
X = I++ passe d'abord la valeur de I à X et incrémente après I
X = I-- passe d'abord la valeur de I à X et décrémente après I
X = ++I incrémente d'abord I et passe la valeur incrémentée à X
X = --I décrémente d'abord I et passe la valeur décrémentée à X

Exemple
Supposons que la valeur de N est égal à 5:
Incrém. Postfixe: X = N++; Résultat: N=6 et X=5
Incrém. préfixe: X = ++N; Résultat: N=6 et X=6

3.4. Les expressions et les instructions


Expressions
La formation des expressions est définie par récurrence : Les constantes et les variables sont des expressions. Les
expressions peuvent être combinées entre elles par des opérateurs et former ainsi des expressions plus complexes. Les
expressions peuvent contenir des appels de fonctions et elles peuvent apparaître comme paramètres dans des appels de
fonctions.
Exemples
i =0
i++
X=pow(A,4)
printf(" Bonjour !\n")
a=(5*x+10*y)*2
(a+b)>=100
position!=limite

Instructions
Une expression comme I=0 ou I++ ou printf(...) devient une instruction, si elle est suivie d'un point-virgule.

Exemples
i=0;
i++;
X=pow(A,4);
printf(" Bonjour !\n");
a=(5*x+10*y)*2;

Évaluation et résultats
En C toutes les expressions sont évaluées et retournent une valeur comme résultat:
(3+2==5) ; retourne la valeur 1 (vrai)
Page 16
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
A=5+3 ; retourne la valeur 8

Comme les affectations sont aussi interprétées comme des expressions, il est possible de profiter de la valeur rendue par
l'affectation: ((A=sin(X)) == 0.5)

3.5. Les priorités des opérateurs


L'ordre de l'évaluation des différentes parties d'une expression correspond en principe à celle que nous
connaissons des mathématiques.
Exemple
Supposons pour l'instruction suivante: A=5, B=10, C=1
X = 2*A+3*B+4*C;
L'ordinateur évalue d'abord les multiplications: 2*A ==> 10 , 3*B ==> 30 , 4*C ==> 4
Ensuite, il fait l'addition des trois résultats obtenus: 10+30+4 ==> 44
A la fin, il affecte le résultat général à la variable: X = 44

Priorité d'un opérateur


On dit alors que la multiplication a la priorité sur l'addition et que la multiplication et l'addition ont la priorité sur
l'affectation. Si nous voulons forcer l'ordinateur à commencer par un opérateur avec une priorité plus faible, nous devons
(comme en mathématiques) entourer le terme en question par des parenthèses.

Exemple
Dans l'instruction: X = 2*(A+3)*B+4*C; l'ordinateur évalue d'abord l'expression entre parenthèses, ensuite les
multiplications, ensuite l'addition et enfin l'affectation. (En reprenant les valeurs de l'exemple ci-dessus, le résultat sera
164)
Entre les opérateurs que nous connaissons jusqu'ici, nous pouvons distinguer les classes de priorités suivantes:
Classes de priorités
Priorité 1 (la plus forte): ( )
Priorité 2: ! ++ --
Priorité 3: * / %
Priorité 4: + -
Priorité 5: < <= > >=
Priorité 6: == !=
Priorité 7: &&
Priorité 8: ||
Priorité 9 (la plus faible): = += -= *= /= %=

Evaluation d'opérateurs de la même classe


--> Dans chaque classe de priorité, les opérateurs ont la même priorité. Si nous avons une suite d'opérateurs binaires de la
même classe, l'évaluation se fait en passant de la gauche vers la droite dans l'expression.
<-- Pour les opérateurs unaires (!, ++, --) et pour les opérateurs d'affectation (=, +=, -=, *=, /=, %=), l'évaluation se fait de
droite à gauche dans l'expression.

Exemples
L'expression 10+20+30-40+50-60 sera évaluée comme suit:
10+20 ==> 30
30+30 ==> 60
60-40 ==> 20
20+50 ==> 70
70-60 ==> 10
Pour A=3 et B=4, l'expression A *= B += 5 sera évaluée comme suit:

Pour A=1 et B=4, l'expression !--A==++!B sera évaluée comme ci-dessus

Page 17
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M

Les parenthèses
Les parenthèses sont seulement nécessaires si nous devons forcer la priorité, mais elles sont aussi
permises si elles ne changent rien à la priorité. En cas de parenthèses imbriquées, l'évaluation se fait de
l'intérieur vers l'extérieur.
Exemple
En supposant à nouveau que A=5, B=10, C=1 l'expression suivante s'évaluera à 134:
X = ((2*A+3)*B+4)*C
Observez la priorité des opérateurs d'affectation :
X *= Y + 1 <=> X = X * (Y + 1)
X *= Y + 1 n'équivaut PAS à X = X * Y + 1

Exercice 3.5
Evaluer les expressions suivantes en supposant
a=20 b=5 c=-10 d=2 x=12 y=15
Notez chaque fois la valeur rendue comme résultat de l'expression et les valeurs des variables dont le contenu a changé.

(1) (5*X)+2*((3*B)+4)
(2) (5*(X+2)*3)*(B+4)
(3) A == (B=5)
(4) A += (X+5)
(5) A != (C *= (-D))
(6) A *= C+(X-D)
(7) A %= D++
(8) A %= ++D
(9) (X++)*(A+C)
(10) A = X*(B<C)+Y*!(B<C)
(11) !(X-D+C)||D
(12) A&&B||!0&&C&&!D
(13) ((A&&B)||(!0&&C))&&!D
(14) ((A&&B)||!0)&&(C&&(!D))

Exercice 3.6
Eliminer les parenthèses superflues dans les expressions de l'exercice 3.5.

3.6. Les fonctions arithmétiques standard


Les fonctions suivantes sont prédéfinies dans la bibliothèque standard <math.h>.
Pour pouvoir les utiliser, le programme doit contenir la ligne:
#include <math.h>

Type des données


Les arguments et les résultats des fonctions arithmétiques sont du type double.

Fonctions arithmétiques
COMMANDE C EXPLICATION
exp(X) fonction exponentielle
log(X) logarithme naturel
log10(X) logarithme à base 10
pow(X,Y) X exposant Y
sqrt(X) racine carrée de X
fabs(X) valeur absolue de X
floor(X) arrondir en moins
ceil(X) arrondir en plus
fmod(X,Y) reste rationnel de X/Y (même signe que X)
sin(X) cos(X) tan(X) sinus, cosinus, tangente de X
asin(X) acos(X) atan(X) arcsin(X), arccos(X), arctan(X)
sinh(X) cosh(X) tanh(X) sinus, cosinus, tangente hyperboliques de X

Remarque avancée
La liste des fonctions ne cite que les fonctions les plus courantes. Pour la liste complète et les constantes prédéfinies voir
<math.h>.
Page 18
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M

Exercice 3.7
Essayez le programme suivant et modifiez-le de façon à ce qu'il affiche:
* AB,
* l'hypoténuse d'un triangle rectangle de côtés A et B,
* la tangente de A en n'utilisant que les fonctions sin et cos,
* la valeur arrondie (en moins) de A/B,
* la valeur arrondie (en moins) à trois positions derrière la virgule de A/B.

#include <stdio.h>
main()
{
double A;
double B;
double RES;
/* Saisie de A et B */
printf("Introduire la valeur pour A : ");
scanf("%lf", &A);
printf("Introduire la valeur pour B : ");
scanf("%lf", &B);
/* Calcul */
RES = A*A;
/* Affichage du résultat */
printf("Le carré de A est %f \n", RES);
/* Calcul */
RES = B*B;
/* Affichage du résultat */
printf("Le carré de B est %f \n", RES);
return 0;

3.7. Les conversions de type


La grande souplesse du langage C permet de mélanger des données de différents types dans une expression. Avant de
pouvoir calculer, les données doivent être converties dans un même type. La plupart de ces conversions se passent
automatiquement, sans l'intervention du programmeur, qui doit quand même prévoir leur effet. Parfois il est nécessaire de
convertir une donnée dans un type différent de celui que choisirait la conversion automatique; dans ce cas, nous devons
forcer la conversion à l'aide d'un opérateur spécial ("cast").
3.7.1. Les conversions de type automatiques
Calculs et affectations
Si un opérateur a des opérandes de différents types, les valeurs des opérandes sont converties automatiquement dans un
type commun. Ces manipulations implicites convertissent en général des types plus 'petits' en des types plus 'larges'; de
cette façon on ne perd pas en précision.
Lors d'une affectation, la donnée à droite du signe d'égalité est convertie dans le type à gauche du signe d'égalité. Dans ce
cas, il peut y avoir perte de précision si le type de la destination est plus faible que celui de la source.

Exemple
Considérons le calcul suivant:
int I = 8;
float X = 12.5;
double Y;
Y = I * X;
Pour pouvoir être multiplié avec X, la valeur de I est convertie en float (le type le plus large des deux). Le résultat de la
multiplication est du type float, mais avant d'être affecté a Y, il est converti en double. Nous obtenons comme résultat:
Y=100.00

Appels de fonctions
Lors de l'appel d'une fonction, les paramètres sont automatiquement convertis dans les types déclarés dans la définition de
la fonction.

Exemple
Au cours des expressions suivantes, nous assistons à trois conversions automatiques:
int A = 200;
int RES;
RES = pow(A, 2);
A l'appel de la fonction pow, la valeur de A et la constante 2 sont converties en double, parce que pow est définie pour des
données de ce type. Le résultat (type double) retourné par pow doit être converti en int avant d'être affecté à RES.

Page 19
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M

Règles de conversion automatique


Conversions automatiques lors d'une opération avec :
(1) deux entiers: D'abord, les types char et short sont convertis en int. Ensuite, l'ordinateur choisit le plus large des deux
types dans l'échelle suivante: int, unsigned int, long, unsigned long
(2) un entier et un rationnel: Le type entier est converti dans le type du rationnel.
(3) deux rationnels: L'ordinateur choisit le plus large des deux types selon l'échelle suivante: float, double, long double
(4) affectations et opérateurs d'affectation: Lors d'une affectation, le résultat est toujours converti dans le type de la
destination. Si ce type est plus faible, il peut y avoir une perte de précision.

Exemple
Observons les conversions nécessaires lors d'une simple division:
int X;
float A=12.48;
char B=4;
X=A/B;
B est converti en float (règle 2). Le résultat de la division est du type float (valeur 3.12) et sera converti en int avant d'être
affecté à X (règle 4), ce qui conduit au résultat X=3 .

Phénomènes imprévus ...


Le mélange de différents types numériques dans un calcul peut inciter à ne pas tenir compte des phénomènes de conversion
et conduit parfois à des résultats imprévus.

Exemple
Dans cet exemple, nous divisons 3 par 4 à trois reprises et nous observons que le résultat ne dépend pas seulement du type
de la destination, mais aussi du type des opérandes.

char A=3;
int B=4;
float C=4;
float D,E;
char F;
D = A/C;
E = A/B;
F = A/C;
* Pour le calcul de D, A est converti en float (règle 2) et divisé par C. Le résultat (0.75) est affecté à D qui est aussi du type
float. On obtient donc: D=0.75.
* Pour le calcul de E, A est converti en int (règle 1) et divisé par B. Le résultat de la division (type int, valeur 0) est converti
en float (règle 4). On obtient donc: E=0.000.
* Pour le calcul de F, A est converti en float (règle 2) et divisé par C. Le résultat (0.75) est retraduit en char (règle 4). On
obtient donc: F=0

Perte de précision
Lorsque nous convertissons une valeur en un type qui n'est pas assez précis ou pas assez grand, la valeur est coupée sans
arrondir et sans nous avertir.

Exemple
unsigned int A = 70000;
/* la valeur de A sera: 70000 mod 65536 = 4464 */

Exercice 3.8
Soient les déclarations:
long A = 15;
char B = 'A'; /* code ASCII : 65 */
short C = 10;
Quels sont le type et la valeur de chacune des expressions:
(1) C+3
(2) B+1
(3) C+B
(4) 3*C+2*B
(5) 2 * B + (A + 10) / C
(6) 2 * B + (A + 10.0) / C

3.7.2. Les conversions de type forcées (casting)

Page 20
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Il est possible de convertir explicitement une valeur en un type quelconque en forçant la transformation à l'aide de la
syntaxe: Casting (conversion de type forcée)
(<Type>) <Expression>

Exemple
Nous divisons deux variables du type entier. Pour avoir plus de précision, nous voulons avoir un résultat de type rationnel.
Pour ce faire, nous convertissons l'une des deux opérandes en float. Automatiquement C convertira l'autre opérande en float
et effectuera une division rationnelle:
char A=3;
int B=4;
float C;
C = (float)A/B;
La valeur de A est explicitement convertie en float. La valeur de B est automatiquement convertie en float (règle 2). Le
résultat de la division (type rationnel, valeur 0.75) est affecté à C. (Résultat: C=0.75 )

Attention !
Les contenus de A et de B restent inchangés; seulement les valeurs utilisées dans les calculs sont converties !

Page 21
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
chapitre 4
LECTURE ET ECRITURE DES DONNEES

La bibliothèque standard <stdio.h> contient un ensemble de fonctions qui assurent la communication de la machine avec le
monde extérieur. Dans ce chapitre, nous allons en discuter les plus importantes:

printf() écriture formatée de données


scanf() lecture formatée de données
putchar() écriture d'un caractère
getchar() lecture d'un caractère

4.1. Écriture formatée de données


printf()
La fonction printf est utilisée pour transférer du texte, des valeurs de variables ou des résultats d'expressions vers le fichier
de sortie standard stdout (par défaut l'écran).

Ecriture formatée en C
printf("<format>",<Expr1>,<Expr2>, ... )

"<format>" : format de représentation


<Expr1>,... : variables et expressions dont les valeurs sont à représenter

La partie "<format>" est en fait une chaîne de caractères qui peut contenir:
* du texte
* des séquences d'échappement
* des spécificateurs de format
Les spécificateurs de format indiquent la manière dont les valeurs des expressions <Expr1..N> sont imprimées.
La partie "<format>" contient exactement un spécificateur de format pour chaque expression <Expr1..N>.
Les spécificateurs de format commencent toujours par le symbole % et se terminent par un ou deux caractères qui indiquent
le format d'impression.
Les spécificateurs de format impliquent une conversion d'un nombre en chaîne de caractères. Ils sont encore appelés
symboles de conversion.

Exemple 1
La suite d'instructions:
int A = 1234;
int B = 567;
printf("%i fois %i est %li\n", A, B, (long)A*B);

va afficher sur l'écran: 1234 fois 567 est 699678


Les arguments de printf sont
- la partie format "%i fois %i est %li"
- la variable A
- la variable B
- l'expression (long)A*B
- Le 1er spécificateur (%i) indique que la valeur de A sera imprimée comme entier relatif ==>1234
- Le 2ème spécificateur (%i) indique que la valeur de B sera imprimée comme entier relatif ==> 567
- Le 3ème spécificateur (%li) indique que la valeur de (long)A*B sera imprimée comme entier relatif long ==> 699678

Exemple 2
La suite d'instructions:
char B = 'A';
printf("Le caractère %c a le code %i !\n", B, B);
va afficher sur l'écran: Le caractère A a le code 65 !. La valeur de B est donc affichée sous deux formats différents:
- %c comme caractère: A
%i entier relatif: 65

Spécificateurs de format pour printf


SYMBOLE TYPE IMPRESSION COMME
%d ou %i int entier relatif
%u int entier naturel (unsigned)
Page 22
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
%o int entier exprimé en octal
%x int entier exprimé en hexadécimal
%c int caractère
%f double rationnel en notation décimale
%e double rationnel en notation scientifique
%s char* chaîne de caractères

1. Arguments du type long


Les spécificateurs %d, %i, %u, %o, %f peuvent seulement représenter des valeurs du type int ou unsigned int. Une valeur
trop grande pour être codée dans deux octets est coupée sans avertissement si nous utilisons %d. Pour pouvoir traiter
correctement les arguments du type long, il faut utiliser les spécificateurs %ld, %li, %lu, %lo, %lf.

Exemple
long N = 1500000;
printf("%ld, %lf", N, N);
==> 1500000, 16e360
printf("%f, %f" , N);
==> e360, 16
printf("%d, %d" , N);
==> -7328, 22
2. Arguments rationnels
Les spécificateurs %f et %e peuvent être utilisés pour représenter des arguments du type float ou double. La mantisse des
nombres représentés par %e contient exactement un chiffre (non nul) devant le point décimal. Cette représentation s'appelle
la notation scientifique des rationnels. Pour pouvoir traiter correctement les arguments du type long double, il faut utiliser
les spécificateurs %Lf et %Le.

Exemple
float N = 12.1234;
double M = 12.123456789;
long double P = 15.5;
printf("%f", N);
==> 12.123400
printf("%f", M);
==> 12.123457
printf("%e", N);
==> 1.212340e+01
printf("%e", M);
==> 1.212346e+01
printf("%Le", P);
==> 1.550000e+01
3. Largeur minimale pour les entiers
Pour les entiers, nous pouvons indiquer la largeur minimale de la valeur à afficher. Dans le champ ainsi réservé, les
nombres sont justifiés à droite.

Exemples
( _ <=> position libre)
printf("%4d", 123);
==> _123
printf("%4d", 1234);
==> 1234
printf("%4d", 12345);
==> 12345
printf("%4u", 0);
==> ___0
printf("%4X", 123);
==> __7B
printf("%4x", 123);
__7b

Exercice 4.1
#include <stdio.h>
main()
{
int N=10, P=5, Q=10, R;
char C='S';
N = 5; P = 2;
Q = N++ > P || P++ != 3;

Page 23
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
printf ("C : N=%d P=%d Q=%d\n", N, P, Q);
N = 5; P = 2;
Q = N++ < P || P++ != 3;
printf ("D : N=%d P=%d Q=%d\n", N, P, Q);
N = 5; P = 2;
Q = ++N == 3 && ++P == 3;
printf ("E : N=%d P=%d Q=%d\n", N, P, Q);
N=5; P=2;
Q = ++N == 6 && ++P == 3;
printf ("F : N=%d P=%d Q=%d\n", N, P, Q);
N=C;
printf ("G : %c %c\n", C, N);
printf ("H : %d %d\n", C, N);
printf ("I : %x %x\n", C, N);
return 0;
}
a) Sans utiliser l'ordinateur, trouvez et notez les résultats du programme ci-dessus.
b) Vérifiez vos résultats à l'aide de l'ordinateur.

4.2. Lecture formatée de données


scanf()
La fonction scanf est la fonction symétrique à printf; elle nous offre pratiquement les mêmes conversions que printf, mais
en sens inverse.
Lecture formatée en C
scanf("<format>",<AdrVar1>,<AdrVar2>, ...)
"<format>": format de lecture des données
<AdrVar1> : adresses des variables auxquelles les données seront attribuées

* La fonction scanf reçoit ses données à partir du fichier d'entrée standard stdin (par défaut le clavier).
* La chaîne de format détermine comment les données reçues doivent être interprétées.
* Les données reçues correctement sont mémorisées successivement aux adresses indiquées par <AdrVar1>,... .
* L'adresse d'une variable est indiquée par le nom de la variable précédé du signe &.

Exemple
int JOUR, MOIS, ANNEE;
scanf("%i %i %i", &JOUR, &MOIS, &ANNEE);

La suite d'instructions (ci-dessus) lit trois entiers relatifs, séparés par des espaces, tabulations ou interlignes. Les valeurs
sont attribuées respectivement aux trois variables JOUR, MOIS et ANNEE.
* scanf retourne comme résultat le nombre de données correctement reçues (type int).

Spécificateurs de format pour scanf

SYMBOLE TYPE LECTURE D'UN(E)


%d ou %i int * entier relatif
%u int * entier naturel (unsigned)
%o int * entier exprimé en octal
%x int * entier exprimé en hexadécimal
%c int * caractère
%f float * rationnel en notation décimale
%e float * rationnel en notation scientifique
%s char* chaîne de caractères

Le symbole * indique que l'argument n'est pas une variable, mais l'adresse d'une variable de ce type (c.-à-d.: un pointeur sur
une variable).

1. Le type long
Si nous voulons lire une donnée du type long, nous devons utiliser les spécificateurs %ld, %li, %lu, %lo, %lx. (Sinon, le
nombre est simplement coupé à la taille de int).
2. Le type double
Si nous voulons lire une donnée du type double, nous devons utiliser les spécificateurs %le ou %lf.
3. Le type long double
Si nous voulons lire une donnée du type long double, nous devons utiliser les spécificateurs %Le ou %Lf.
4. Indication de la largeur maximale

Page 24
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Pour tous les spécificateurs, nous pouvons indiquer la largeur maximale du champ à évaluer pour une donnée. Les chiffres
qui passent au-delà du champ défini sont attribués à la prochaine variable qui sera lue !

Exemple
Soient les instructions:
int A,B;
scanf("%4d %2d", &A, &B);
Si nous entrons le nombre 1234567, nous obtiendrons les affectations suivantes: A=1234 et B=56. Le
chiffre 7 sera gardé pour la prochaine instruction de lecture.
5. Les signes d'espacement
Lors de l'entrée des données, une suite de signes d'espacement (espaces, tabulateurs, interlignes) est évaluée comme un seul
espace. Dans la chaîne de format, les symboles \t, \n, \r ont le même effet qu'un simple espace.

Exemple
Pour la suite d'instructions
int JOUR, MOIS, ANNEE;
scanf("%i %i %i", &JOUR, &MOIS, &ANNEE);
les entrées suivantes sont correctes et équivalentes:
12 4 1980 ou 12 004 1980 ou 12 4 1980

6. Formats 'spéciaux'
Si la chaîne de format contient aussi d'autres caractères que des signes d'espacement, alors ces symboles doivent être
introduits exactement dans l'ordre indiqué.

Exemple
La suite d'instructions
int JOUR, MOIS, ANNEE;
scanf("%i/%i/%i", &JOUR, &MOIS, &ANNEE);

accepte les entrées rejette les entrées


12/4/1980 12 4 1980
12/04/01980 12 /4 /1980
7. Nombre de valeurs lues
Lors de l'évaluation des données, scanf s'arrête si la chaîne de format a été travaillée jusqu'à la fin ou si une donnée ne
correspond pas au format indiqué. scanf retourne comme résultat le nombre d'arguments correctement reçus et affectés.

Exemple
La suite d'instructions
int JOUR, MOIS, ANNEE, RECU;
RECU = scanf("%i %i %i", &JOUR, &MOIS, &ANNEE);

Exercice 4.2
En vous référant aux exemples du paragraphe 4.2, écrivez un programme qui lit la date du clavier et écrit les données ainsi
que le nombre de données correctement reçues sur l'écran.

Exemple:
Introduisez la date (jour mois année): 11 11 1991
données reçues : 3
jour : 11
mois : 11
année : 1991
* Testez les réactions du programme à vos entrées. Essayez d'introduire des nombres de différents formats et différentes
grandeurs.
* Changez la partie format du programme de façon à séparer les différentes données par le symbole '-' .

4.3. Écriture d'un caractère


La commande, putchar('a'); transfère le caractère ‘a’ vers le fichier standard de sortie stdout. Les arguments de la fonction
putchar sont ou bien des caractères (c.-à-d. des nombres entiers entre 0 et 255) ou bien le symbole EOF (End Of File).
EOF est une constante définie dans <stdio> qui marque la fin d'un fichier. La commande putchar(EOF); est utilisée dans le
cas où stdout est dévié vers un fichier.

Page 25
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Type de l'argument
Pour ne pas être confondue avec un caractère, la constante EOF doit nécessairement avoir une valeur qui sort du domaine
des caractères (en général EOF a la valeur -1). Ainsi, les arguments de putchar sont par définition du type int et toutes les
valeurs traitées par putchar (même celles du type char) sont d'abord converties en int.

Exemples
char A = 225;
char B = '\a';
int C = '\a';
putchar('x'); /* afficher la lettre x */
putchar('?'); /* afficher le symbole ? */
putchar('\n'); /* retour à la ligne */
putchar(65); /* afficher le symbole avec */ /* le code 65 (ASCII: 'A') */
putchar(A); /* afficher la lettre avec */ /* le code 225 (ASCII: 'ß') */
putchar(B); /* beep sonore */
putchar(C); /* beep sonore */
putchar(EOF); /* marquer la fin du fichier */

4.4. Lecture d'un caractère


Une fonction plus souvent utilisée que putchar est la fonction getchar, qui lit le prochain caractère du fichier d'entrée
standard stdin.

Type du résultat
Les valeurs retournées par getchar sont ou bien des caractères (0 - 255) ou bien le symbole EOF. Comme la valeur du
symbole EOF sort du domaine des caractères, le type résultat de getchar est int. En général, getchar est utilisé dans une
affectation:
int C;
C = getchar();
getchar lit les données de la zone tampon de stdin et fournit les données seulement après confirmation par 'Enter'. La
bibliothèque <conio> contient une fonction du nom getch qui fournit immédiatement le prochain caractère entré au clavier.
La fonction getch n'est pas compatible avec ANSI-C et elle peut seulement être utilisée sous MS-DOS.

Exercice 4.3
Ecrire un programme qui lit un caractère au clavier et affiche le caractère ainsi que son code numérique:
a) en employant getchar et printf,
b) en employant getch et printf.

4.5. Exercices d'application


Exercice 4.4
Ecrire un programme qui permute et affiche les valeurs de trois variables A, B, C de type entier qui sont entrées au clavier :
A ==> B , B ==> C , C ==> A
Exercice 4.5
Ecrire un programme qui affiche le quotient et le reste de la division entière de deux nombres entiers entrés au clavier ainsi
que le quotient rationnel de ces nombres.
Exercice 4.6
Ecrire un programme qui affiche la résistance équivalente à trois résistances R1, R2, R3 (type double),
- si les résistances sont branchées en série:
- si les résistances sont branchées en parallèle:
Exercice 4.7
Ecrire un programme qui calcule et affiche l'aire d'un triangle dont il faut entrer les longueurs des trois côtés. Utilisez la
formule : S2 = P(P-A)(P-B)(P-C)
Où : A, B, C sont les longueurs des trois côtés (type int) et P le demi-périmètre du triangle.
Exercice 4.8
Ecrire un programme qui calcule la somme de quatre nombres du type int entrés au clavier,
a) en se servant de 5 variables (mémorisation des valeurs entrées) ;
b) en se servant de 2 variables (perte des valeurs entrées) ;
Exercice 4.9
a) Ecrire un programme qui calcule le prix brut (type double) d'un article à partir du prix net (type int) et du pourcentage de
TVA (type int) à ajouter. Utilisez la formule suivante en faisant attention aux priorités et aux conversions automatiques de
type: Pbrut = Pnet + Pnet .TVA/100
b) Ecrire un programme qui calcule le prix net d'un article (type double) à partir du prix brut (type double) et du
pourcentage de TVA (type int) qui a été ajoutée. (Déduisez la formule du calcul de celle indiquée ci-dessus).
Exercice 4.10
Page 26
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Ecrire un programme qui calcule et affiche la distance DIST (type double) entre deux points A et B du plan dont les
coordonnées (XA, YA) et (XB, YB) sont entrées au clavier comme entiers.

Page 27
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M

Chapitre 5
LA STRUCTURE ALTERNATIVE

Les structures de contrôle définissent la suite dans laquelle les instructions sont effectuées. Dans ce chapitre, nous allons
voir comment les instructions de sélection connues fonctionnent en C et nous allons faire connaissance d'un couple
d'opérateurs spécial qui nous permet de choisir entre deux valeurs à l'intérieur d'une expression.
Constatons déjà que la particularité la plus importante des instructions de contrôle en C est le fait que les 'conditions' en C
peuvent être des expressions quelconques qui fournissent un résultat numérique. La valeur zéro correspond à la valeur
logique faux et toute valeur différente de zéro est considérée comme vrai.

5.1. la structure if - else


La structure alternative en C
if ( <expression> )
<bloc d'instructions 1>
else
<bloc d'instructions 2>

* Si l'<expression> fournit une valeur différente de zéro, alors le <bloc d'instructions 1> est exécuté
* Si l'<expression> fournit la valeur zéro, alors le <bloc d'instructions 2> est exécuté

La partie <expression> peut désigner : une variable d'un type numérique, une expression fournissant un résultat numérique.
La partie <bloc d'instructions> peut désigner : un (vrai) bloc d'instructions compris entre accolades, une seule instruction
terminée par un point-virgule ( ;).

Exemple 1
if (a > b) max = a;
else max = b;
Exemple 2
if (EGAL) printf("A est égal à B\n");
else printf("A est différent de B\n");
Exemple 3
if (A-B) printf("A est différent de B\n");
else printf("A est égal à B\n");
Exemple 4
if (A > B)
{
AIDE = A;
A = C;
C = AIDE;
}
else
{
AIDE = B;
B = C;
C = AIDE;
}

5.2. la structure if sans else


La partie else est facultative. On peut donc utiliser if de la façon suivante:

if ( <expression> )
<bloc d'instructions>
Attention !
Comme la partie else est optionnelle, les expressions contenant plusieurs structures if et if - else peuvent mener à des
confusions.
Exemple
L'expression suivante peut être interprétée de deux façons:

if (N>0)
if (A>B)
MAX=A;
else
MAX=B;

Page 26
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
if (N>0)
if (A>B)
MAX=A;
else
MAX=B;

Pour N=0, A=1 et B=2,


* dans la première interprétation, MAX reste inchangé ;
* dans la deuxième interprétation, MAX obtiendrait la valeur de B (2).
Sans règle supplémentaire, le résultat de cette expression serait donc imprévisible.

Convention
En C une partie else est toujours liée au dernier if qui ne possède pas de partie else. Dans notre exemple, C utiliserait donc
la première interprétation.

Solution
Pour éviter des confusions et pour forcer une certaine interprétation d'une expression, il est recommandé d'utiliser des
accolades { } .
Exemple
Pour forcer la deuxième interprétation de l'expression ci-dessus, nous pouvons écrire:
if (N>0) { if (A>B) MAX=A; }
else MAX=B;

Exercice 5.1
Considérez la séquence d'instructions suivante:
if (A>B) printf ("premier choix \n"); else
if (A>10) printf ("deuxième choix \n");
if (B<10) printf ("troisième choix \n");
else printf ("quatrième choix \n");
a) Copiez la séquence d'instructions en utilisant des tabulateurs pour marquer les blocs if - else appartenant ensemble.
b) Déterminez les réponses du programme pour chacun des couples de nombres suivants.
- A=10 et B=5
- A=5 et B=5
- A=5 et B=10
- A=10 et B=10
- A=20 et B=10
- A=20 et B=20

5.3. Structure if - else if - ... - else


En combinant plusieurs structures if - else en une expression nous obtenons une structure qui est très courante pour prendre
des décisions entre plusieurs alternatives: if - else - ... - else

if ( <expr1> )
<bloc1>
else if (<expr2>)
<bloc2>
else if (<expr3>)
<bloc3>
else if (<exprN>)
<blocN>
else <blocN+1>

Les expressions <expr1> ... <exprN> sont évaluées du haut vers le bas jusqu'à ce que l'une d'elles soit différente de zéro. Le
bloc d'instructions y lié est alors exécuté et le traitement de la commande est terminé.

Exemple
#include <stdio.h>
main()
{
int A,B;
printf("Entrez deux nombres entiers :");
scanf("%i %i", &A, &B);
if (A > B)
printf("%i est plus grand que %i\n", A, B);
else if (A < B)

Page 27
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
printf("%i est plus petit que %i\n", A, B);
else
printf("%i est égal à %i\n", A, B);
return 0;
}
La dernière partie else traite le cas où aucune des conditions n'a été remplie. Elle est optionnelle, mais elle peut être utilisée
très confortablement pour détecter des erreurs.
Exemple
printf("Continuer (O)ui / (N)on ?");
getchar(C);
if (C=='O')
{
...
}
else if (C=='N')
printf("Au revoir ...\n");
else
printf("\aErreur d'entrée !\n");

Exercice 5.2
Considérez la séquence d'instructions suivante:
if (A>B)
if (A>10)
printf ("premier choix \n"); else if (B<10)
printf ("deuxième choix \n"); else
if (A==B) printf ("troisième choix \n");
else printf ("quatrième choix \n");
a) Copiez la séquence d'instructions en utilisant des tabulateurs pour marquer les blocs if - else appartenant ensemble.
b) Pour quelles valeurs de A et B obtient-on les résultats: premier choix, deuxième choix, ... sur l'écran?
c) Pour quelles valeurs de A et B on n’obtient pas de réponse sur l'écran?
d) Notez vos réponses et choisissez vous-mêmes des valeurs pour A et B pour les vérifier l'aide de l'ordinateur.

5.4. Les opérateurs conditionnels


Le langage C possède une paire d'opérateurs un peu exotiques qui peut être utilisée comme alternative à if - else et qui a
l'avantage de pouvoir être intégrée dans une expression: Les opérateurs conditionnels

<expr1> ? <expr2> : <expr3>

* Si <expr1> fournit une valeur différente de zéro, alors la valeur de <expr2> est fournie comme résultat
* Si <expr1> fournit la valeur zéro, alors la valeur de <expr3> est fournie comme résultat

Exemple
if (A>B)
MAX=A;
else
MAX=B;
La suite d'instructions peut être remplacée par: MAX = (A > B) ? A : B;

Employés de façon irréfléchie, les opérateurs conditionnels peuvent nuire à la lisibilité d'un programme, mais si on les
utilise avec précaution, ils fournissent des solutions très élégantes:
Exemple
printf("Vous avez %i carte%c \n", N, (N==1) ? ' ' : 's');

Les règles de conversion de types s'appliquent aussi aux opérateurs conditionnels ? : Ainsi, pour un entier N du type int et
un rationnel F du type float, l'expression (N>0) ? N : F va toujours fournir un résultat du type float, n'importe si N est
plus grand ou plus petit que zéro!

Page 28
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
5.5. Exercices d'application
Exercice 5.3
Ecrivez un programme qui lit trois valeurs entières (A, B et C) au clavier et qui affiche la plus grande des trois valeurs, en
utilisant:
a) if - else et une variable d'aide MAX
b) if - else if - ... - else sans variable d'aide
c) les opérateurs conditionnels et une variable d'aide MAX
d) les opérateurs conditionnels sans variable d'aide

Exercice 5.4
Ecrivez un programme qui lit trois valeurs entières (A, B et C) au clavier. Triez les valeurs A, B et C par échanges
successifs de manière à obtenir : val(A) val(B) val(C)
Affichez les trois valeurs.

Exercice 5.5
Ecrivez un programme qui lit deux valeurs entières (A et B) au clavier et qui affiche le signe du produit de A et B sans faire
la multiplication.

Exercice 5.6
Ecrivez un programme qui lit deux valeurs entières (A et B) au clavier et qui affiche le signe de la somme de A et B sans
faire l'addition. Utilisez la fonction fabs de la bibliothèque <math>.

Exercice 5.7
Ecrivez un programme qui calcule les solutions réelles d'une équation du second degré ax2+bx+c = 0 en discutant la
formule:

Utilisez une variable d'aide D pour la valeur du discriminant b2-4ac et décidez à l'aide de D, si l'équation a une, deux ou
aucune solution réelle. Utilisez des variables du type int pour A, B et C.
Considérez aussi les cas où l'utilisateur entre des valeurs nulles pour A; pour A et B; pour A, B et C. Affichez les résultats
et les messages nécessaires sur l'écran.

Page 29
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Chapitre 6
LA STRUCTURE REPETITIVE

En C, nous disposons de trois structures qui nous permettent la définition de boucles conditionnelles:
1) la structure : while
2) la structure : do - while
3) la structure : for

Théoriquement, ces structures sont interchangeables, c.-à-d. il serait possible de programmer toutes sortes de boucles
conditionnelles en n'utilisant qu'une seule des trois structures. Comme en Pascal, il est quand même absolument
recommandé de choisir toujours la structure la mieux adaptée au cas actuel (voir paragraphe 6.4.).

6.1. La structure while


La structure while correspond tout à fait à la structure tant que du langage algorithmique. (Si on néglige le fait qu'en C les
conditions peuvent être formulées à l'aide d'expressions numériques.)
La structure while en C
while ( <expression> )
<bloc d'instructions>

* Tant que l'<expression> fournit une valeur différente de zéro, le <bloc d'instructions> est exécuté.
* Si l'<expression> fournit la valeur zéro, l'exécution continue avec l'instruction qui suit le bloc d'instructions.
* Le <bloc d'instructions> est exécuté zéro ou plusieurs fois.

La partie <expression> peut désigner une variable d'un type numérique ou une expression fournissant un résultat
numérique.
La partie <bloc d'instructions> peut désigner un (vrai) bloc d'instructions compris entre accolades ou une seule instruction
terminée par un point-virgule.

Exemple 1
/* Afficher les nombres de 0 à 9 */
int I = 0;
while (I<10)
{
printf("%i \n", I);
I++;
}

Exemple 2
int I;
/* Afficher les nombres de 0 à 9 */
I = 0;
while (I<10)
printf("%i \n", I++);
/* Afficher les nombres de 1 à 10 */
I = 0;
while (I<10)
printf("%i \n", ++I);

Exemple 3
/* Afficher les nombres de 10 à 1 */
int I=10;
while (I)
printf("%i \n", I--);

Remarque
Parfois nous voulons seulement attendre un certain événement, sans avoir besoin d'un traitement de données. Dans ce cas,
la partie <bloc d'instructions> peut être vide (notation: ; ou {} ). La ligne suivante ignore tous les espaces entrés au clavier
et peut être utilisée avant de lire le premier caractère significatif: while (getch()==' ');

6.2. La structure do - while


La structure do - while est semblable à la structure while, avec la différence suivante :
* while évalue la condition avant d'exécuter le bloc d'instructions,

Page 30
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
* do - while évalue la condition après avoir exécuté le bloc d'instructions. Ainsi le bloc d'instructions est exécuté au moins
une fois.
La structure do - while en C
do
<bloc d'instructions>
while ( <expression> );

Le <bloc d'instructions> est exécuté au moins une fois et aussi longtemps que l'<expression> fournit une valeur différente
de zéro.
En pratique, la structure do - while n'est pas si fréquente que while; mais dans certains cas, elle fournit une solution plus
élégante. Une application typique de do - while est la saisie de données qui doivent remplir une certaine condition:

Exemple 1
float N;
do
{
printf("Introduisez un nombre entre 1 et 10 :");
scanf("%f", &N);
}
while (N>1 || N<10);

Exemple 2
int n, div;
printf("Entrez le nombre à diviser : ");
scanf("%i", &n);
do
{
printf("Entrez le diviseur ( 0) : ");
scanf("%i", &div);
}
while (!div);
printf("%i / %i = %f\n", n, div, (float)n/div);

do - while est comparable à la structure répéter du langage algorithmique (repeat until en Pascal) si la condition finale est
inversée logiquement.

Exemple 3
Le programme de calcul de la racine carrée :

#include <stdio.h>
#include <math.h>
main()
{
float N;
do
{
printf("Entrer un nombre (>= 0) : ");
scanf("%f", &N)
}
while (N < 0);
printf("La racine carrée de %.2f est %.2f\n", N, sqrt(N));
return 0;
}

6.3. La structure for


La structure for en Pascal est utilisée pour faciliter la programmation de boucles de comptage. La structure for en C est plus
générale et beaucoup plus puissante.

for ( <expr1> ; <expr2> ; <expr3> )


<bloc d'instructions>

<expr1> est évaluée une fois avant le passage de la boucle. Elle est utilisée pour initialiser les données de la boucle.
<expr2> est évaluée avant chaque passage de la boucle. Elle est utilisée pour décider si la boucle est répétée ou non.
<expr3> est évaluée à la fin de chaque passage de la boucle. Elle est utilisée pour réinitialiser les données de la boucle.
Le plus souvent, for est utilisé comme boucle de comptage :

Page 31
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
for ( <init.> ; <cond. répétition> ; <compteur> )
<bloc d'instructions>

Exemple
int I;
for (I=0 ; I<=20 ; I++)
printf("Le carré de %d est %d \n", I, I*I);

En pratique, les parties <expr1> et <expr2> contiennent souvent plusieurs initialisations ou réinitialisations, séparées par
des virgules.
Exemple
int n, tot;
for (tot=0, n=1 ; n<101 ; n++)
tot+=n;
printf("La somme des nombres de 1 à 100 est %d\n", tot);

Exemple
Cet exemple nous présente différentes variations pour réaliser le même traitement et nous montre la puissance de la
structure for. Les expressions suivantes lisent un caractère au clavier et affichent son code numérique en notation binaire :

/* a */
/* notation utilisant la structure while */
int C, I;
C=getchar();
I=128;
while (I>=1)
{
printf("%i ", C/I);
C%=I;
I/=2;
}

/* b */
/* notation utilisant for - très lisible - */
/* préférée par les débutants en C */
int C, I;
C=getchar();
for (I=128 ; I>=1 ; I/=2)
{
printf("%i ", C/I);
C%=I;
}

/* c */
/* notation utilisant for - plus compacte - */
/* préférée par les experts en C */
int C, I;
C=getchar();
for (I=128 ; I>=1 ; C%=I, I/=2)
printf("%i ", C/I);

/* d */
/* notation utilisant for - à déconseiller - */
/* surcharge et mauvais emploi de la structure */
int C, I;
for(C=getchar(),I=128; I>=1 ;printf("%i ",C/I),C%=i,i/=2);

6.4. Choix de la structure répétitive


Dans ce chapitre, nous avons vu trois façons différentes de programmer des boucles (while, do - while, for). Utilisez la
structure qui reflète le mieux l'idée du programme que vous voulez réaliser, en respectant toutefois les directives suivantes :
* Si le bloc d'instructions ne doit pas être exécuté si la condition est fausse, alors utilisez while ou for.
* Si le bloc d'instructions doit être exécuté au moins une fois, alors utilisez do - while.
* Si le nombre d'exécutions du bloc d'instructions dépend d'une ou de plusieurs variables qui sont modifiées à la fin de
chaque répétition, alors utilisez for.
* Si le bloc d'instructions doit être exécuté aussi longtemps qu'une condition extérieure est vraie (p.ex aussi longtemps qu'il
y a des données dans le fichier d'entrée), alors utilisez while. Le choix entre for et while n'est souvent qu'une question de
préférence ou d'habitudes:

Page 32
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
* for nous permet de réunir avantageusement les instructions qui influencent le nombre de répétitions au début de la
structure.
* while a l'avantage de correspondre plus exactement aux structures d'autres langages de programmation (while, tant que).
* for a le désavantage de favoriser la programmation de structures surchargées et par la suite illisible.
* while a le désavantage de mener parfois à de longues structures, dans lesquelles il faut chercher pour trouver les
instructions qui influencent la condition de répétition.

6.5. Exercices d'application


Exercice 6.1
Ecrivez un programme qui lit N nombres entiers au clavier et qui affiche leur somme, leur produit et leur moyenne.
Choisissez un type approprié pour les valeurs à afficher. Le nombre N est à entrer au clavier. Résolvez ce problème,
a) en utilisant while,
b) en utilisant do - while,
c) en utilisant for.
d) Laquelle des trois variantes est la plus naturelle pour ce problème?

Exercice 6.2
Complétez la 'meilleure' des trois versions de l'exercice 6.7 :
Répétez l'introduction du nombre N jusqu'à ce que N ait une valeur entre 1 et 15.
Quelle structure répétitive utilisez-vous? Pourquoi?

Exercice 6.3
Calculez par des soustractions successives le quotient entier et le reste de la division entière de deux entiers entrés au
clavier.

Exercice 6.4
Calculez la factorielle N! = 123...(N-1)N d'un entier naturel N en respectant que 0!=1.
a) Utilisez while,
b) Utilisez for.

Exercice 6.5
Calculez par multiplications successives XN de deux entiers naturels X et N entrés au clavier.

Exercice 6.6
Calculez la somme des N premiers termes de la série harmonique : 1 + 1/2 + 1/3 + ... + 1/N

Exercice 6.7
Calculez la somme, le produit et la moyenne d'une suite de chiffres non nuls entrés au clavier, sachant que la suite est
terminée par zéro. Retenez seulement les chiffres (0, 1 ... 9) lors de l'entrée des données et effectuez un signal sonore si les
données sortent de ce domaine.

Exercice 6.8
Calculez le nombre lu à rebours d'un nombre positif entré au clavier en supposant que le fichier d'entrée standard contient
une suite de chiffres non nuls, terminée par zéro (Contrôlez s'il s'agit vraiment de chiffres). Exemple: Entrée: 1 2 3 4 0
Affichage: 4321

Exercice 6.9
Calculez le nombre lu à rebours d'un nombre positif entré au clavier en supposant que le fichier d'entrée standard contient
le nombre à inverser. Exemple: Entrée: 1234 Affichage: 4321

Exercice 6.10
Calculez pour une valeur X donnée du type float la valeur numérique d'un polynôme de degré n:
P(X) = AnXn + An-1Xn-1 + ... + A1X + A0

Les valeurs de n, des coefficients An, ... , A0 et de X seront entrées au clavier.


Utilisez le schéma de Horner qui évite les opérations d'exponentiation lors du calcul:

Page 33
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M

Exercice 6.11
Calculez le N-ième terme UN de la suite de FIBONACCI qui est donnée par la relation de récurrence:

U1=1 U2=1 UN=UN-1 + UN-2 (pour N>2)

Déterminez le rang N et la valeur UN du terme maximal que l'on peut calculer si on utilise pour UN :
- le type int
- le type long
- le type double
- le type long double

Exercice 6.12
Affiche la table des produits pour N variant de 1 à 10 :

Page 34
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Chapitre 7
LES VARIABLES STRUCTUREES
I- LES TABLEAUX
Les tableaux sont certainement les variables structurées les plus populaires. Ils sont disponibles dans tous les langages
de programmation et servent à résoudre une multitude de problèmes. Dans une première approche, le traitement des
tableaux en C ne diffère pas de celui des autres langages de programmation.
Nous allons cependant voir plus loin (paragraphe 3 : Les Pointeurs), que le langage C permet un accès encore plus
direct et plus rapide aux données d'un tableau.

Les chaînes de caractères sont déclarées en C comme tableaux de caractères et permettent l'utilisation d'un certain
nombre de notations et de fonctions spéciales. Les particularités des tableaux de caractères seront traitées dans le 2 ème
paragraphe.

I.1. Les tableaux à une dimension

Définitions
Un tableau (uni-dimensionnel) A est une variable structurée formée d'un nombre entier N de variables simples du
même type, qui sont appelées les composantes du tableau. Le nombre de composantes N est alors la dimension du
tableau.

En faisant le rapprochement avec les mathématiques, on dit encore que "A est un vecteur de dimension N"

Exemple
La déclaration

int JOURS[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

Cette ligne définit un tableau du type int de dimension 12. Les 12 composantes sont initialisées par les valeurs
respectives 31, 28, 31, ... , 31.
On peut accéder à la première composante du tableau par JOURS[0], à la deuxième composante par JOURS[1], . . . , à
la dernière composante par JOURS[11].

A°). Déclaration et mémorisation


Déclaration
<TypeSimple> <NomTableau>[<Dimension>];

Les noms des tableaux sont des identificateurs qui doivent correspondre aux restrictions définies précédemment.

Exemples
Les déclarations suivantes en C :
int A[25]; ou bien long A[25];
float B[100]; ou bien double B[100];
int C[10];
char D[30];

Page 35
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Mémorisation
En C, le nom d'un tableau est le représentant de l'adresse du premier élément du tableau. Les adresses des autres
composantes sont calculées (automatiquement) relativement à cette adresse.

Exemple:
short A[5] = {1200, 2300, 3400, 4500, 5600};

Si un tableau est formé de N composantes et si une composante a besoin de M octets en mémoire, alors le tableau
occupera N*M octets.

Exemple
En supposant qu'une variable du type long occupe 4 octets (c.-à-d: sizeof(long)=4), pour le tableau T déclaré par: long
T[15]; C réservera N*M = 15*4 = 60 octets en mémoire.

B°). Initialisation et réservation automatique


Initialisation
Lors de la déclaration d'un tableau, on peut initialiser les composantes du tableau, en indiquant la liste des valeurs
respectives entre accolades.

Exemples
int A[5] = {10, 20, 30, 40, 50};
float B[4] = {-1.05, 3.33, 87e-5, -12.3E4};
int C[10] = {1, 0, 0, 1, 1, 1, 0, 1, 0, 1};

Il faut évidemment veiller à ce que le nombre de valeurs dans la liste corresponde à la dimension du tableau. Si la liste
ne contient pas assez de valeurs pour toutes les composantes, les composantes restantes sont initialisées par zéro.

Réservation automatique
Si la dimension n'est pas indiquée explicitement lors de l'initialisation, alors l'ordinateur réserve automatiquement le
nombre d'octets nécessaires.

Exemples
int A[] = {10, 20, 30, 40, 50}; ==> réservation de 5*sizeof(int) octets (dans notre cas: 10 octets)
float B[] = {-1.05, 3.33, 87e-5, -12.3E4}; ==> réservation de 4*sizeof(float) octets (dans notre cas: 16 octets)
int C[] = {1, 0, 0, 1, 1, 1, 0, 1, 0, 1}; ==> réservation de 10*sizeof(int) octets (dans notre cas: 20 octets)

Exemples

Page 36
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
C°). Accès aux composantes
En déclarant un tableau par: int A[5];

On a défini un tableau A avec cinq composantes, auxquelles on peut accéder par: A[0], A[1], ... , A[4]

Exemple

Considérons un tableau T de dimension N . En C, on a :


- l'accès au premier élément du tableau se fait par T[0]
- l'accès au dernier élément du tableau se fait par T[N-1]

D°). Affichage et affectation


Affichage
La structure for se prête particulièrement bien au travail avec les tableaux. La plupart des applications se laissent
implémenter par simple modification des exemples types de l'affichage et de l'affectation.

- Affichage du contenu d'un tableau

main()
{
int A[5];
int I; /* Compteur */
for (I=0; I<5; I++)
printf("%d ", A[I]);
return 0;
printf("\n");
}

Remarques
* Avant de pouvoir afficher les composantes d'un tableau, il faut évidemment leur affecter des valeurs.
* Rappelez-vous que la deuxième condition dans la structure for n'est pas une condition d'arrêt, mais une condition de
répétition! Ainsi la commande d'affichage sera répétée aussi longtemps que I est inférieur à 5. La boucle sera donc bien
exécutée pour les indices 0,1,2,3 et 4 !
* Pour être sûr que les valeurs sont bien séparées lors de l'affichage, il faut inclure au moins un espace dans la chaîne
de format. Autres possibilités:

printf("%d\t", A[I]); /* tabulateur */


printf("%7d", A[I]); /* format d'affichage */

Affectation
- Affectation avec des valeurs provenant de l'extérieur
Le programme suivant permet de lire des valeurs à partir du clavier et les affectent aux composantes du tableau A

main()
{
int A[5];
int I; /* Compteur */
for (I=0; I<5; I++)
scanf("%d", &A[I]);
return 0;
Page 37
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
}
Remarques
* Comme scanf a besoin des adresses des différentes composantes du tableau, il faut faire précéder le terme A[I] par
l'opérateur adresse '&'.
* La commande de lecture scanf doit être informée du type exact des données à lire. (Ici: %d ou %i pour lire des
valeurs du type int)

Exercice 1
Ecrire un programme qui lit la dimension N d'un tableau T du type int (dimension maximale: 50 composantes), remplit
le tableau par des valeurs entrées au clavier et affiche le tableau. Calculer et afficher ensuite la somme des éléments du
tableau.
Exercice 2
Ecrire un programme qui lit la dimension N d'un tableau T du type int (dimension maximale: 50 composantes), remplit
le tableau par des valeurs entrées au clavier et affiche le tableau. Effacer ensuite toutes les occurrences de la valeur 0
dans le tableau T et tasser les éléments restants. Afficher le tableau résultant.
Exercice 3
Ecrire un programme qui lit la dimension N d'un tableau T du type int (dimension maximale: 50 composantes), remplit
le tableau par des valeurs entrées au clavier et affiche le tableau.
Ranger ensuite les éléments du tableau T dans l'ordre inverse sans utiliser de tableau d'aide. Afficher le tableau
résultant.
Idée: Echanger les éléments du tableau à l'aide de deux indices qui parcourent le tableau en commençant
respectivement au début et à la fin du tableau et qui se rencontrent en son milieu.
Exercice 4
Ecrire un programme qui lit la dimension N d'un tableau T du type int (dimension maximale: 50 composantes), remplit
le tableau par des valeurs entrées au clavier et affiche le tableau.
Copiez ensuite toutes les composantes strictement positives dans un deuxième tableau TPOS et toutes les valeurs
strictement négatives dans un troisième tableau TNEG. Afficher les tableaux TPOS et TNEG.

I.2. Les tableaux à deux dimensions

Définitions
En C, un tableau à deux dimensions A est à interpréter comme un tableau (uni-dimensionnel) de dimension L dont
chaque composante est un tableau (uni-dimensionnel) de dimension C.
On appelle L le nombre de lignes du tableau et C le nombre de colonnes du tableau. L et C sont alors les deux
dimensions du tableau. Un tableau à deux dimensions contient donc L*C composantes.

On dit qu'un tableau à deux dimensions est carré, si L est égal à C.


En faisant le rapprochement avec les mathématiques, on peut dire que "A est un vecteur de L vecteurs de dimension
C", ou mieux: "A est une matrice de dimensions L et C".

Exemple
Considérons un tableau NOTES à une dimension pour mémoriser les notes de 20 élèves d'une classe dans un devoir:
Page 38
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
int NOTE[20] = {45, 34, ... , 50, 48};

Pour mémoriser les notes des élèves dans les 10 devoirs d'un trimestre, nous pouvons rassembler plusieurs de ces
tableaux uni-dimensionnels dans un tableau NOTES à deux dimensions :

int NOTE[10][20] = {{45, 34, ... , 50, 48},


{39, 24, ... , 49, 45},
... ... ...
{40, 40, ... , 54, 44}};

Dans une ligne nous retrouvons les notes de tous les élèves dans un devoir. Dans une colonne, nous retrouvons toutes
les notes d'un élève.

a°) Déclaration et mémorisation


Déclarations
<TypeSimple> <NomTabl>[<DimLigne>][<DimCol>];
Exemples

int A[10][10]; ou bien long A[10][10];


float B[2][20]; ou bien double B[2][20];
int C[3][3];
char D[15][40];

Mémorisation
Comme pour les tableaux à une dimension, le nom d'un tableau est le représentant de l'adresse du premier élément du
tableau (c.-à-d. l'adresse de la première ligne du tableau). Les composantes d'un tableau à deux dimensions sont
stockées ligne par ligne dans la mémoire.

Exemple
short A[3][2] = {{1, 2 },
{10, 20 },
{100, 200}};

Un tableau de dimensions L et C, formé de composantes dont chacune a besoin de M octets, occupera L*C*M octets
en mémoire.

Exemple
En supposant qu'une variable du type double occupe 8 octets (c.-à-d: sizeof(double)=8), pour le tableau T déclaré par:
Page 39
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
double T[10][15];
C réservera L*C*M = 10*15*8 = 1200 octets en mémoire.

B°) Initialisation et réservation automatique


Initialisation
Lors de la déclaration d'un tableau, on peut initialiser les composantes du tableau, en indiquant la liste des valeurs
respectives entre accolades. A l'intérieur de la liste, les composantes de chaque ligne du tableau sont encore une fois
comprises entre accolades. Pour améliorer la lisibilité des programmes, on peut indiquer les composantes dans
plusieurs lignes.

Exemples
int A[3][10] = {{ 0,10,20,30,40,50,60,70,80,90},
{10,11,12,13,14,15,16,17,18,19},
{ 1,12,23,34,45,56,67,78,89,90}};

float B[3][2] = {{-1.05, -1.10 },


{86e-5, 87e-5 },
{12.5E4, -12.3E4}};

Lors de l'initialisation, les valeurs sont affectées ligne par ligne en passant de gauche à droite. Nous ne devons pas
nécessairement indiquer toutes les valeurs: Les valeurs manquantes seront initialisées par zéro. Il est cependant
défendu d'indiquer trop de valeurs pour un tableau.

Exemples

Réservation automatique

Si le nombre de lignes L n'est pas indiqué explicitement lors de l'initialisation, l'ordinateur réserve automatiquement le
nombre d'octets nécessaires.

int A[][10] = {{ 0,10,20,30,40,50,60,70,80,90},


Page 40
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
{10,11,12,13,14,15,16,17,18,19},
{ 1,12,23,34,45,56,67,78,89,90}}; réservation de 3*10*2 = 60 octets

float B[][2] = {{-1.05, -1.10 },


{86e-5, 87e-5 },
{-12.5E4, -12.3E4}}; réservation de 3*2*4 = 24 octets
Exemple

C°) Accès aux composantes


<NomTableau>[<Ligne>][<Colonne>]

Les éléments d'un tableau de dimensions L et C se présentent de la façon suivante:

| A[0][0] A[0][1] A[0][2] . . . A[0][C-1] |


| A[1][0] A[1][1] A[1][2] . . . A[1][C-1] |
| A[2][0] A[2][1] A[2][2] . . . A[2][C-1] |
| ... ... ... ... ... |
| A[L-1][0] A[L-1][1] A[L-1][2] . . . A[L-1][C-1] |

Attention !
Considérons un tableau A de dimensions L et C. En C, on a :
- les indices du tableau varient de 0 à L-1, respectivement de 0 à C-1.
- la composante de la Nième ligne et Mième colonne est notée: A[N-1][M-1]

D°) Affichage et affectation


Lors du travail avec les tableaux à deux dimensions, nous utiliserons deux indices (p.ex: I et J), et la structure for,
souvent imbriquée, pour parcourir les lignes et les colonnes des tableaux.

- Affichage du contenu d'un tableau à deux dimensions

main()
{
int A[5][10];
int I,J;
/* Pour chaque ligne ... */
for (I=0; I<5; I++)
{
/* ... considérer chaque composante */
for (J=0; J<10; J++)
printf("%7d", A[I][J]);
/* Retour à la ligne */
printf("\n");
}
return 0;
Page 41
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
}
Remarques
* Avant de pouvoir afficher les composantes d'un tableau, il faut leur affecter des valeurs.
* Pour obtenir des colonnes bien alignées lors de l'affichage, il est pratique d'indiquer la largeur minimale de l'affichage
dans la chaîne de format. Pour afficher des matrices du type int (valeur la plus 'longue': -32768), nous pouvons utiliser
la chaîne de format "%7d" : printf("%7d", A[I][J]);

- Affectation avec des valeurs provenant de l'extérieur

main()
{
int A[5][10];
int I,J;
/* Pour chaque ligne ... */
for (I=0; I<5; I++)
/* ... considérer chaque composante */
for (J=0; J<10; J++)
scanf("%d", &A[I][J]);
return 0;
}

II. LES CHAÎNES DE CARACTÈRES

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

II.1. Déclaration et mémorisation

Déclaration

char <NomVariable> [<Longueur>];

Exemples

char NOM [20];


char PRENOM [20];
char PHRASE [300];

Espace à réserver

Lors de la déclaration, nous devons indiquer l'espace à réserver en mémoire pour le stockage de la chaîne.

La représentation interne d'une chaîne de caractères est terminée par le symbole '\0' (NUL). Ainsi, pour un texte de n
caractères, nous devons prévoir n+1 octets.

Malheureusement, le compilateur C ne contrôle pas si nous avons réservé un octet pour le symbole de fin de chaîne;
l'erreur se
fera seulement remarquer lors de l'exécution du programme ...

Mémorisation

Le nom d'une chaîne est le représentant de l'adresse du premier caractère de la chaîne. Pour mémoriser une variable qui
Page 42
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
doit être capable de contenir un texte de N caractères, nous avons besoin de N+1 octets en mémoire:

Exemple: Mémorisation d'un tableau

char TXT[10] = "BONJOUR !";

II.2. Les chaînes de caractères constantes

* Les chaînes de caractères constantes (string literals) sont indiquées entre guillemets. La chaîne de caractères vide est
alors:
""

* Dans les chaînes de caractères, nous pouvons utiliser toutes les séquences d'échappement définies comme caractères
constants:

"Ce \ntexte \nsera réparti sur 3 lignes."

* Le symbole " peut être représenté à l'intérieur d'une chaîne par la séquence d'échappement \":

"Affichage de \"guillemets\" \n"

* Le symbole ' peut être représenté à l'intérieur d'une liste de caractères par la séquence d'échappement \' :

{'L','\'','a','s','t','u','c','e','\0'}

* Plusieurs chaînes de caractères constantes qui sont séparées par des signes d'espacement (espaces, tabulateurs ou
interlignes) dans le texte du programme seront réunies en une seule chaîne constante lors de la compilation:

"un " "deux"


" trois"

sera évalué à

"un deux trois"

Ainsi il est possible de définir de très longues chaînes de caractères constantes en utilisant plusieurs lignes dans le texte
du
programme.

Observation

Pour la mémorisation de la chaîne de caractères "Hello", C a besoin de six (!!) octets.

'x'
est un caractère constant, qui a une valeur numérique:

P.ex: 'x' a la valeur 120 dans le code ASCII.

Page 43
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
"x"
est un tableau de caractères qui contient deux caractères:

la lettre 'x' et le caractère NUL: '\0'

'x'
est codé dans un octet

"x"
est codé dans deux octets

II.3. Initialisation de chaînes de caractères


En général, les tableaux sont initialisés par l'indication de la liste des éléments du tableau entre accolades:
char CHAINE[] = {'H','e','l','l','o','\0'};
Pour le cas spécial des tableaux de caractères, nous pouvons utiliser une initialisation plus confortable en indiquant
simplement une chaîne de caractère constante: char CHAINE[] = "Hello";

Lors de l'initialisation par [], l'ordinateur réserve automatiquement le nombre d'octets nécessaires pour la chaîne, c.-
à-d.: le nombre de caractères + 1 (ici: 6 octets). Nous pouvons aussi indiquer explicitement le nombre d'octets à
réserver, si celui-ci est supérieur ou égal à la longueur de la chaîne d'initialisation.

Exemples

Exercice

Lesquelles des chaînes suivantes sont initialisées correctement ? Corrigez les déclarations fausses et indiquez pour
chaque chaîne de caractères le nombre d'octets qui sera réservé en mémoire.
a) char a[] = "un\ndeux\ntrois\n";
b) char b[12] = "un deux trois";
c) char c[] = 'abcdefg';
d) char d[10] = 'x';
Page 44
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
e) char e[5] = "cinq";
f) char f[] = "Cette " "phrase" "est coupée";
g) char g[2] = {'a', '\0'};
h) char h[4] = {'a', 'b', 'c'};
i) char i[4] = "'o'";

II.4. Accès aux éléments d'une chaîne


L'accès à un élément d'une chaîne de caractères peut se faire de la même façon que l'accès à un élément d'un tableau.
En déclarant une chaîne par: char A[6]; nous avons défini un tableau A avec six éléments, auxquels on peut accéder
par:
A[0], A[1], ... , A[5]

Exemple

II.5. Précédence alphabétique et lexicographique

Précédence alphabétique des caractères


La précédence des caractères dans l'alphabet d'une machine est dépendante du code de caractères utilisé. Pour le
code ASCII, nous pouvons constater l'ordre suivant:
. . . ,0,1,2, ... ,9, . . . ,A,B,C, ... ,Z, . . . ,a,b,c, ... ,z, . . .
Les symboles spéciaux (' ,+ ,- ,/ ,{ ,] , ...) et les lettres accentuées (é ,è ,à ,û , ...) se trouvent répartis autour des trois
grands groupes de caractères (chiffres, majuscules, minuscules). Leur précédence ne correspond à aucune règle
d'ordre spécifique.

Relation de précédence
De la précédence alphabétique des caractères, on peut déduire une relation de précédence 'est inférieur à' sur
l'ensemble des caractères. Ainsi, on peut dire que '0' est inférieur à 'Z' et noter ‘0' < 'Z' car dans l'alphabet de la
machine, le code du caractère '0' (ASCII: 48) est inférieur au code du caractère 'Z' (ASCII: 90).

Précédence lexicographique des chaînes de caractères


En nous basant sur cette relation de précédence alphabétique des caractères, nous pouvons définir une précédence
lexicographique pour les chaînes de caractères. Cette relation de précédence suit l'<<ordre du dictionnaire>> et est
définie de façon récurrente:

a) La chaîne vide "" précède lexicographiquement toutes les autres chaînes.


b) La chaîne A = "a1a2a ... ap" (p caractères) précède lexicographiquement la chaîne B = "b1b2 ... bm" (m
caractères) si l'une des deux conditions suivantes est remplie:
1) 'a1' < 'b1'
2) 'a1' = 'b1' et "a2a3 ... ap" précède lexicographiquement "b2b3 ... bm"

Exemples
"ABC" précède "BCD" car 'A'<'B'
"ABC" précède "B" car 'A'<'B'
"Abc" précède "abc" car 'A'<'a'
"ab" précède "abcd" car "" précède "cd"
" ab" précède "ab" car ' '<'a' (le code ASCII de ' ' est 32, et le code ASCII de 'a' est 97)

Page 45
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Remarque
Malheureusement, il existe différents codes de caractères (p.ex. ASCII, EBCDIC, ISO) et l'ordre lexicographique
est dépendant de la machine. Même la fonction strcmp qui indique la précédence lexicographique de deux chaînes
de caractères dépend du code de caractères utilisé.

Conversions et tests
En tenant compte de l'ordre alphabétique des caractères, on peut contrôler le type du caractère (chiffre, majuscule,
minuscule).

Exemples
if (C>='0' && C<='9') printf("Chiffre\n", C);
if (C>='A' && C<='Z') printf("Majuscule\n", C);
if (C>='a' && C<='z') printf("Minuscule\n", C);

Il est facile, de convertir des lettres majuscules dans des minuscules:

if (C>='A' && C<='Z') C = C-'A'+'a'; ou vice-versa: if (C>='a' && C<='z') C = C-'a'+'A';

Remarque
Le code EBCDIC est organisé en zones, de façon que les caractères des trois grands groupes ne sont pas codés
consécutivement. (P.ex.: les codes des caractères 'i' et 'j' diffèrent de 8 unités). Les méthodes de conversion
discutées ci-dessus ne fonctionnent donc pas correctement dans le code EBCDIC et un programme portable doit être
écrit à l'aide des fonctions (isalpha, islower, toupper, ...) de la bibliothèque <ctype> qui sont indépendantes du code
de caractères.

II.6. Travailler avec des chaînes de caractères


Les bibliothèques de fonctions de C contiennent une série de fonctions spéciales pour le traitement de chaînes de
caractères. Sauf indication contraire, les fonctions décrites dans ce chapitre sont portables conformément au
standard ANSI-C.

A°) Les fonctions de <stdio.h>

Comme nous l'avons déjà vu au chapitre 4, la bibliothèque <stdio> nous offre des fonctions qui effectuent l'entrée et
la sortie des données. A côté des fonctions printf et scanf que nous connaissons déjà, nous y trouvons les deux
fonctions puts et gets, spécialement conçues pour l'écriture et la lecture de chaînes de caractères.

- Affichage de chaînes de caractères


printf avec le spécificateur de format %s permet d'intégrer une chaîne de caractères dans une phrase.
En plus, le spécificateur %s permet l'indication de la largeur minimale du champ d'affichage. Dans ce champ, les
données sont justifiées à droite. Si on indique une largeur minimale négative, la chaîne sera justifiée à gauche. Un
nombre suivant un point indique la largeur maximale pour l'affichage.

Exemples
char NOM[] = "hello, world";

printf(":%s:", NOM); :hello, world:


printf(":%5s:", NOM); :hello, world:
printf(":%15s:", NOM); : hello, world:
printf(":%-15s:", NOM); :hello, world :
printf(":%.5s:", NOM); :hello:

puts est idéale pour écrire une chaîne constante ou le contenu d'une variable dans une ligne isolée.

Syntaxe: puts( <Chaîne> )


Page 46
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M

Effet: puts écrit la chaîne de caractères désignée par <Chaîne> sur stdout et provoque un retour à la ligne.
En pratique, puts(TXT); est équivalent à printf("%s\n",TXT);

Exemples
char TEXTE[] = "Voici une première ligne.";
puts(TEXTE);
puts("Voici une deuxième ligne.");

- Lecture de chaînes de caractères

scanf avec le spécificateur %s permet de lire un mot isolé à l'intérieur d'une suite de données du même ou d'un
autre type.

Effet: scanf avec le spécificateur %s lit un mot du fichier d'entrée standard stdin et le mémorise à l'adresse qui est
associée à %s.

Exemple
char LIEU[25];
int JOUR, MOIS, ANNEE;
printf("Entrez lieu et date de naissance : \n");
scanf("%s %d %d %d", LIEU, &JOUR, &MOIS, &ANNEE);

Remarques importantes
- La fonction scanf a besoin des adresses de ses arguments:
* Les noms des variables numériques (int, char, long, float, ...) doivent être marqués par le symbole '&' (voir
chap 4.4.).
* Comme le nom d'une chaîne de caractères est le représentant de l'adresse du premier caractère de la chaîne, il
ne
doit pas être précédé de l'opérateur adresse '&' !
- La fonction scanf avec plusieurs arguments présuppose que l'utilisateur connaisse exactement le nombre et l'ordre
des données à introduire! Ainsi, l'utilisation de scanf pour la lecture de chaînes de caractères est seulement
conseillée si on est forcé de lire un nombre fixé de mots en une fois.

gets est idéal pour lire une ou plusieurs lignes de texte (p.ex. des phrases) terminées par un retour à la ligne.

Syntaxe: gets( <Chaîne> )


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

Exemple
int MAXI = 1000;
char LIGNE[MAXI];
gets(LIGNE);

Exercice
1°) Ecrire un programme qui lit 5 mots, séparés par des espaces et qui les affiche ensuite dans une ligne, mais dans
l'ordre inverse.
Les mots sont mémorisés dans 5 variables M1, ... ,M5. (Exemple [voici une petite phrase !] [! phrase petite une
voici])

2°) Ecrire un programme qui lit une ligne de texte (ne dépassant pas 200 caractères) la mémorise dans une variable
TXT et affiche ensuite:
a) la longueur L de la chaîne.
Page 47
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
b) le nombre de 'e' contenus dans le texte.
c) toute la phrase à rebours, sans changer le contenu de la variable TXT.
d) toute la phrase à rebours, après avoir inversé l'ordre des caractères dans TXT:
( voici une petite phrase ! ! esarhp etitep enu iciov)

3°) Ecrire un programme qui lit un texte TXT (de moins de 200 caractères) et qui enlève toutes les apparitions du
charactère 'e' en tassant les éléments restants. Les modifications se feront dans la même variable TXT.

(Exemple: [ Cette ligne contient quelques lettres e.] [ Ctt lign contint qulqus lttrs .])
B°) Les fonctions de <string>

La bibliothèque <string> fournit une multitude de fonctions pratiques pour le traitement de chaînes de caractères.
Voici une brève description des fonctions les plus fréquemment utilisées.

Dans le tableau suivant, <n> représente un nombre du type int. Les symboles <s> et <t> peuvent être remplacés
par :
* une chaîne de caractères constante
* le nom d'une variable déclarée comme tableau de char
* un pointeur sur char (voir paragraphe III)

Fonctions pour le traitement de chaînes de caractères


strlen(<s>) fournit la longueur de la chaîne sans compter le '\0' final
strcpy(<s>, <t>) copie <t> vers <s>
strcat(<s>, <t>) ajoute <t> à la fin de <s>
strcmp(<s>, <t>) compare <s> et <t> lexicographiquement et fournit un résultat:
négatif si <s> précède <t>
zéro si <s> est égal à <t>
positif si <s> suit <t>
strncpy(<s>, <t>, <n>) copie au plus <n> caractères de <t> vers <s>
strncat(<s>, <t>, <n>) ajoute au plus <n> caractères de <t> à la fin de <s>

Exercices
1°) Ecrire un programme qui demande l'introduction du nom et du prénom de l'utilisateur et qui affiche alors la
longueur totale du nom sans compter les espaces. Employer la fonction strlen.
(Exemple: Introduisez votre nom et votre prénom: Mickey Mouse Bonjour Mickey Mouse ! Votre nom est
composé de 11 lettres.)

2°) Ecrire un programme qui lit deux chaînes de caractères CH1 et CH2, les compare lexicographiquement et
affiche le résultat:
(Exemple: Introduisez la première chaîne: ABC Introduisez la deuxième chaîne: abc "ABC" précède "abc")

3°) Ecrire un programme qui lit deux chaînes de caractères CH1 et CH2 et qui copie la première moitié de CH1 et la
première moitié de CH2 dans une troisième chaîne CH3. Afficher le résultat.
a) Utiliser les fonctions spéciales de <string>.
b) Utiliser uniquement les fonctions gets et puts.

4°) Ecrire un programme qui lit un verbe régulier en "er" au clavier et qui en affiche la conjugaison au présent de
l'indicatif de ce verbe. Contrôlez s'il s'agit bien d'un verbe en "er" avant de conjuguer. Utiliser les fonctions gets,
puts, strcat et strlen.
Exemple: verbe : fêter
je fête nous fêtons
tu fêtes vous fêtez
il fête ils fêtent

Page 48
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
C°) Les fonctions de <stdlib>

La bibliothèque <stdlib> contient des déclarations de fonctions pour la conversion de nombres en chaînes de
caractères et vice-versa.

D°) Les fonctions de <ctype>


Les fonctions de <ctype> servent à classifier et à convertir des caractères. Les symboles nationaux (é, è, ä, ü, ß,
ç, ...) ne sont pas considérés. Les fonctions de <ctype> sont indépendantes du code de caractères de la machine et
favorisent la portabilité des programmes. Dans la suite, <c> représente une valeur du type int qui peut être
représentée comme caractère.

Fonctions de classification et de conversion


Les fonctions de classification suivantes fournissent un résultat du type int différent de zéro, si la condition est
remplie, sinon zéro.

La fonction retourne une valeur différente de zéro


isupper(<c>) si <c> est une majuscule ('A'...'Z')
islower(<c>) si <c> est une minuscule ('a'...'z')
isdigit(<c>) si <c> est un chiffre décimal ('0'...'9')
isalpha(<c>) si islower(<c>) ou isupper(<c>)
isalnum(<c>) si isalpha(<c>) ou isdigit(<c>)
isxdigit(<c>) si <c> est un chiffre hexadécimal ('0'...'9' ou 'A'...'F' ou 'a'...'f')
isspace(<c>) si <c> est un signe d'espacement (' ', '\t', '\n', '\r', '\f')

Les fonctions de conversion suivantes fournissent une valeur du type int qui peut être représentée comme caractère;
la valeur originale de <c> reste inchangée:

tolower(<c>) retourne <c> converti en minuscule si <c> est une majuscule


toupper(<c>) retourne <c> converti en majuscule si <c> est une minuscule

Page 49
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
III LES POINTEURS
L'importance des pointeurs en C
La plupart des langages de programmation offrent la possibilité d'accéder aux données dans la mémoire de
l'ordinateur à l'aide de pointeurs, càd. à l'aide de variables auxquelles on peut attribuer les adresses d'autres
variables.
En C, les pointeurs jouent un rôle primordial dans la définition de fonctions: Comme le passage des paramètres en
C se fait toujours par la valeur, les pointeurs sont le seul moyen de changer le contenu de variables déclarées dans
d'autres fonctions.
Ainsi le traitement de tableaux et de chaînes de caractères dans des fonctions serait impossible sans l'utilisation de
pointeurs. En outre, les pointeurs nous permettent d'écrire des programmes plus compacts et plus efficaces et
fournissent souvent la seule solution raisonnable à un problème. Ainsi, la majorité des applications écrites en C
profitent extensivement des pointeurs.

III-1- Adressage de variables


Avant de parler de pointeurs, il est très utile de passer en revue les deux modes d'adressage principaux.

a°) Adressage direct


Dans la programmation, nous utilisons des variables pour stocker des informations. La valeur d'une variable se
trouve à un endroit spécifique dans la mémoire interne de l'ordinateur. Le nom de la variable nous permet alors
d'accéder directement à cette valeur.
Adressage direct: Accès au contenu d'une variable par le nom de la variable.

Exemple

b°) Adressage indirect


Si nous ne voulons (ou ne pouvons) pas utiliser le nom d'une variable A, nous pouvons copier l'adresse de cette
variable dans une variable spéciale P, appelée pointeur. Ensuite, nous pouvons retrouver l'information de la
variable A en passant par le pointeur P.
Adressage indirect: Accès au contenu d'une variable, en passant par un pointeur qui contient l'adresse de la
variable.

Exemple
Soit A une variable contenant la valeur 10 et P un pointeur qui contient l'adresse de A. En mémoire, A et P peuvent
se présenter comme suit:

III-2 Les pointeurs


Définition
Un pointeur est une variable spéciale qui peut contenir l'adresse d'une autre variable.
En C, chaque pointeur est limité à un type de données. Il peut contenir l'adresse d'une variable simple de ce type ou
l'adresse d'une composante d'un tableau de ce type.
Si un pointeur P contient l'adresse d'une variable A, on dit que 'P pointe sur A'.
Page 50
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M

Remarque
Les pointeurs et les noms de variables ont le même rôle: Ils donnent accès à un emplacement dans la mémoire. Il
faut quand même bien faire la différence:
* Un pointeur est une variable qui peut 'pointer' sur différentes adresses.
* Le nom d'une variable reste toujours lié à la même adresse.
A°) Les opérateurs de base
Lors du travail avec des pointeurs, nous avons besoin :
- d'un opérateur 'adresse de' : & pour obtenir l'adresse d'une variable.
- d'un opérateur 'contenu de' : * pour accéder au contenu d'une adresse.
- d'une syntaxe de déclaration pour pouvoir déclarer un pointeur.

L'opérateur 'adresse de' : &


&<NomVariable> fournit l'adresse de la variable <NomVariable>
L'opérateur & nous est déjà familier par la fonction scanf, qui a besoin de l'adresse de ses arguments pour pouvoir
leur attribuer de nouvelles valeurs.

Exemple
int N;
printf("Entrez un nombre entier : ");
scanf("%d", &N);

Attention !
L'opérateur & peut seulement être appliqué à des objets qui se trouvent dans la mémoire interne, c.-à-d. à des
variables et des tableaux. Il ne peut pas être appliqué à des constantes ou des expressions.

Représentation schématique
Soit P un pointeur non initialisé et A une variable (du même type) contenant la valeur 10 :

Alors l'instruction P = &A affecte l'adresse de la variable A à la variable pointeur P.


On peut représenter, schématiquement, le fait que 'P pointe sur A' par une flèche:

L'opérateur 'contenu de' : * *<NomPointeur>


désigne le contenu de l'adresse référencée par le pointeur <NomPointeur>

Exemple
Soit A une variable contenant la valeur 10, B une variable contenant la valeur 50 et P un pointeur non initialisé:

Après les instructions,


P = &A;
B = *P;
Page 51
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
*P = 20;
- P pointe sur A ;
- le contenu de A (référencé par *P) est affecté à B ;
- le contenu de A (référencé par *P) est mis à 20.

Déclaration d'un pointeur <Type> *<NomPointeur>


déclare un pointeur <NomPointeur> qui peut recevoir des adresses de variables du type <Type>
Une déclaration comme int *PNUM; peut être interprétée comme suit:

"*PNUM est du type int" ou "PNUM est un pointeur sur int" ou


"PNUM peut contenir l'adresse d'une variable du type int"

Exemple
Le programme complet effectuant les transformations de l'exemple ci-dessus peut se présenter comme suit:
main()
{
/* déclarations */
short A = 10;
short B = 50;
short *P;
/* traitement */
P = &A;
B = *P;
*P = 20;
return 0;
}

Remarque
Lors de la déclaration d'un pointeur en C, ce pointeur est lié explicitement à un type de données. Ainsi, la variable
PNUM déclarée comme pointeur sur int ne peut pas recevoir l'adresse d'une variable d'un autre type que int.
Nous allons voir que la limitation d'un pointeur à un type de variables n'élimine pas seulement un grand nombre de
sources d'erreurs très désagréables, mais permet une série d'opérations très pratiques sur les pointeurs.

B°) Les opérations élémentaires sur les pointeurs


En travaillant avec des pointeurs, nous devons observer les règles suivantes:
Priorité de * et &
Les opérateurs * et & ont la même priorité que les autres opérateurs unaires (la négation !, l'incrémentation ++, la
décrémentation --). Dans une même expression, les opérateurs unaires *, &, !, ++, -- sont évalués de droite à gauche.
+ Si un pointeur P pointe sur une variable X, alors *P peut être utilisé partout où on peut écrire X.

Exemple
Après l'instruction P = &X; les expressions suivantes, sont équivalentes:
Y = *P+1 Y = X+1
*P = *P+10 X = X+10
*P += 2 X += 2
++*P ++X

Page 52
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
(*P)++ X++

Dans le dernier cas, les parenthèses sont nécessaires, comme les opérateurs unaires * et ++ sont évalués de droite à
gauche, sans les parenthèses le pointeur P serait incrémenté et non pas l'objet sur lequel il pointe. On peut
uniquement affecter des adresses à un pointeur.

Le pointeur NUL
Seule exception: La valeur numérique 0 (zéro) est utilisée pour indiquer qu'un pointeur ne pointe 'nulle part'.

int *P; P = 0;

Finalement, les pointeurs sont aussi des variables et peuvent être utilisés comme telles. Soit P1 et P2 deux pointeurs
sur int, alors l'affectation P1 = P2; copie le contenu de P2 vers P1. P1 pointe alors sur le même objet que P2.

Remarque P1=&A;
Int *P ; P2=*P1/=*P2;
Int A ; return 0;
P désigne l'adresse du pointeur P }
*A est illégal (puisque A n'est pas un pointeur) Copiez le tableau suivant et complétez-le pour
chaque instruction du programme ci-dessus.
Exercice 9.1 A B C P1
main() P2
{ Init. 1 2 3 /
int A = 1; /
int B = 2; P1=&A 1 2 3 &A
int C = 3; /
int *P1, *P2; P2=&C
P1=&A; *P1=(*P2)++
P2=&C; P1=P2
*P1=(*P2)++; P2=&B
P1=P2; *P1-=*P2
P2=&B; ++*P2
*P1-=*P2; *P1*=*P2
++*P2; A=++*P2**P1
*P1*=*P2; P1=&A
A=++*P2**P1; *P2=*P1/=*P2

C°) Pointeurs et tableaux


En C, il existe une relation très étroite entre tableaux et pointeurs. Ainsi, chaque opération avec des
indices de tableaux peut aussi être exprimée à l'aide de pointeurs. En général, les versions formulées
avec des pointeurs sont plus compactes et plus efficaces, surtout à l'intérieur de fonctions.

a°) Adressage des composantes d'un tableau


Comme nous l'avons déjà constaté précédemment, le nom d'un tableau représente l'adresse de son premier élément.
En d'autre termes:
&tableau[0] et tableau sont une seule et même adresse.
En simplifiant, nous pouvons retenir que le nom d'un tableau est un pointeur constant sur le premier élément du
tableau.

Exemple
En déclarant un tableau A de type int et un pointeur P sur int,

int A[10];
Page 53
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
int *P;
l'instruction: P = A; est équivalente à P = &A[0];

Si P pointe sur une composante quelconque d'un tableau, alors P+1 pointe sur la composante suivante. Plus
généralement, P+i pointe sur la ième composante derrière P et P-i pointe sur la i ème composante devant P.

Ainsi, après l'instruction, P = A;


le pointeur P pointe sur A[0],
*(P+1) désigne le contenu de A[1]
*(P+2) désigne le contenu de A[2]
...
*(P+i) désigne le contenu de A[i]

Remarque
Au premier coup d'oeil, il est bien surprenant que P+i n'adresse pas le i-ième octet derrière P, mais la i-ième
composante derrière P ...
Ceci s'explique par la stratégie de programmation 'défensive' des créateurs du langage C:
Si on travaille avec des pointeurs, les erreurs les plus perfides sont causées par des pointeurs mal placés et des
adresses mal calculées. En C, le compilateur peut calculer automatiquement l'adresse de l'élément P+i en ajoutant à
P la grandeur d'une composante multipliée par i. Ceci est possible, parce que:
- chaque pointeur est limité à un seul type de données ;
- le compilateur connaît le nombre d'octets des différents types.

Exemple
Soit A un tableau contenant des éléments du type float et P un pointeur sur float:
float A[20], X;
float *P;

Après les instructions,


P = A;
X = *(P+9);

X contient la valeur du 10-ième élément de A, (c.-à-d. celle de A[9]). Une donnée du type float ayant besoin de 4 octets, le
compilateur obtient l'adresse P+9 en ajoutant 9 * 4 = 36 octets à l'adresse dans P.

Attention !
Il existe toujours une différence essentielle entre un pointeur et le nom d'un tableau:
- Un pointeur est une variable, donc des opérations comme P = A ou P++ sont permises.
- Le nom d'un tableau est une constante, donc des opérations comme A = P ou A++ sont impossibles.

Résumons, soit un tableau A d'un type quelconque et i un indice pour les composantes de A, alors

A désigne l'adresse de A[0]


A+i désigne l'adresse de A[i]
*(A+i) désigne le contenu de A[i]

Si P = A, alors
P pointe sur l'élément A[0]
P+i pointe sur l'élément A[i]
*(P+i) désigne le contenu de A[i]

Page 54
Support de cours OUTILS DE DEVELOPPEMENT SADIK .M
Formalisme tableau et formalisme pointeur
A l'aide de ce bagage, il nous est facile de 'traduire' un programme écrit à l'aide du 'formalisme tableau' dans un
programme employant le 'formalisme pointeur'.

Exemple
Les deux programmes suivants copient les éléments positifs d'un tableau T dans un deuxième tableau POS.

Page 55
Formalisme tableau
main() main()
{ {
int T[10] = {-3, 4, 0, -7, 3, 8, 0, -1, 4, -9}; int T[10] = {-3, 4, 0, -7, 3, 8, 0, -1, 4, -9};
int POS[10]; int POS[10];
int I,J; /* indices courants dans T et POS */ int I,J; /* indices courants dans T et POS */
for (J=0,I=0 ; I<10 ; I++) for (J=0,I=0 ; I<10 ; I++)
if (T[I]>0) if (*(T+I)>0)
{ {
POS[J] = T[I]; *(POS+J) = *(T+I);
J++; J++;
} }
return 0; return 0;
} }

Formalisme pointeur
Sources d'erreurs
Un bon nombre d'erreurs lors de l'utilisation de C provient de la confusion entre contenu et adresse, pointeur et
variable. Revoyons donc les trois types de déclarations que nous connaissons jusqu'ici et résumons les possibilités
d'accès aux données qui se présentent.

Les variables et leurs utilisations


int A déclare une variable simple du type int
A désigne le contenu de A
&A désigne l'adresse de A

int B[] déclare un tableau d'éléments du type int


B désigne l'adresse de la première composante de B.
(Cette adresse est toujours constante)
B[i] désigne le contenu de la composante i du tableau
&B[i] désigne l'adresse de la composante i du tableau

int *P déclare un pointeur sur des éléments du type int.


P peut pointer sur des variables simples du type int ou sur les composantes d'un tableau du type int.
P désigne l'adresse contenue dans P (Cette adresse est variable)
*P désigne le contenu de l'adresse dans P

Si P pointe dans un tableau, alors


P désigne l'adresse de la première composante
P+i désigne l'adresse de la i-ième composante derrière P
*(P+i) désigne le contenu de la i-ième composante derrière P

Exercice 2
Ecrire un programme qui lit deux tableaux A et B et leurs dimensions N et M au clavier et qui ajoute
les éléments de B à la fin de A. Utiliser le formalisme pointeur à chaque fois que cela est possible.

D°) Arithmétique des pointeurs

Comme les pointeurs jouent un rôle si important, le langage C soutient une série d'opérations
arithmétiques sur les pointeurs que l'on ne rencontre en général que dans les langages machines. Le
confort de ces opérations en C est basé sur le principe suivant:
Toutes les opérations avec les pointeurs tiennent compte automatiquement du type et de la grandeur des
objets pointés.

- Affectation par un pointeur sur le même type


Soient P1 et P2 deux pointeurs sur le même type de données, alors l'instruction
P1 = P2; fait pointer P1 sur le même objet que P2

- Addition et soustraction d'un nombre entier


Si P pointe sur l'élément A[i] d'un tableau, alors :
P+n pointe sur A[i+n]
P-n pointe sur A[i-n]

- Incrémentation et décrémentation d'un pointeur


Si P pointe sur l'élément A[i] d'un tableau, alors après l'instruction
P++; P pointe sur A[i+1]
P+=n; P pointe sur A[i+n]
P--; P pointe sur A[i-1]
P-=n; P pointe sur A[i-n]

Domaine des opérations


L'addition, la soustraction, l'incrémentation et la décrémentation sur les pointeurs sont seulement définies
à l'intérieur d'un tableau. Si l'adresse formée par le pointeur et l'indice sort du domaine du tableau, alors le
résultat n'est pas défini.
Seule exception: Il est permis de 'pointer' sur le premier octet derrière un tableau (à condition que cet octet
se trouve dans le même segment de mémoire que le tableau). Cette règle, introduite avec le standard
ANSI-C, légalise la définition de boucles qui incrémente le pointeur avant l'évaluation de la condition
d'arrêt.

Exemples
int A[10];
int *P;
P = A+9;
/* dernier élément -> légal */
P = A+10;
/* dernier élément + 1 -> légal */
P = A+11;
/* dernier élément + 2 -> illégal */
P = A-1;
/* premier élément - 1 -> illégal */

- Soustraction de deux pointeurs


Soient P1 et P2 deux pointeurs qui pointent dans le même tableau:
P1-P2 ; fournit le nombre de composantes comprises entre P1 et P2.
Le résultat de la soustraction P1-P2 est négatif, si P1 précède P2
zéro, P1 = P2
positif, si P2 precède P1
indéfini, si P1 et P2 ne pointent pas dans le même tableau
Plus généralement, la soustraction de deux pointeurs qui pointent dans le même tableau est équivalente à
la soustraction des indices correspondants.

- Comparaison de deux pointeurs


On peut comparer deux pointeurs par <, >, <=, >=, ==, !=.
La comparaison de deux pointeurs qui pointent dans le même tableau est équivalente à la comparaison des indices
correspondants. (Si les pointeurs ne pointent pas dans le même tableau, alors le résultat est donné par leurs positions
relatives dans la mémoire).

Exercice 1
Soit P un pointeur qui 'pointe' sur un tableau A:
int A[] = {12, 23, 34, 45, 56, 67, 78, 89, 90};
int *P;
P = A;
Quelles valeurs ou adresses fournissent ces expressions:
a) *P+2 b) *(P+2) c) &P+1 d) &A[4]
e) A+3 f) &A[7]-P g) P+(*P-10) h) *(P+*(P+8)-A[7])

Exercice 2
Ecrire un programme qui lit un entier X et un tableau A du type int au clavier et élimine toutes les occurrences de X
dans A en tassant les éléments restants. Le programme utilisera les pointeurs P1 et P2 pour parcourir le tableau.

Exercice 3
Ecrire un programme qui range les éléments d'un tableau A du type int dans l'ordre inverse. Le programme utilisera
des pointeurs P1 et P2 et une variable numérique AIDE pour la permutation des éléments.

9.3.3. Pointeurs et chaînes de caractères

De la même façon qu'un pointeur sur int peut contenir l'adresse d'un nombre isolé ou d'une composante
d'un tableau, un pointeur sur char peut pointer sur un caractère isolé ou sur les éléments d'un tableau de
caractères. Un pointeur sur char peut, en plus, contenir l'adresse d'une chaîne de caractères constante et il
peut même être initialisé avec une telle adresse.
A la fin de ce paragraphe, nous allons anticiper avec un exemple et montrer que les pointeurs sont les
éléments indispensables mais effectifs des fonctions en C.

- Pointeurs sur char et chaînes de caractères constantes

Affectation
On peut attribuer l'adresse d'une chaîne de caractères constante à un pointeur sur char:
Exemple
char *C;
C = "Ceci est une chaîne de caractères constante";
Nous pouvons lire cette chaîne constante (p.ex: pour l'afficher), mais il n'est pas recommandé de la modifier, parce
que le résultat d'un programme qui essaie de modifier une chaîne de caractères constante n'est pas prévisible en
ANSI-C.

Initialisation
Un pointeur sur char peut être initialisé lors de la déclaration si on lui affecte l'adresse d'une chaîne de caractères
constante: char *B = "Bonjour !";

Attention !
Il existe une différence importante entre les deux déclarations:
char A[] = "Bonjour !"; /* un tableau */
char *B = "Bonjour !"; /* un pointeur */
A est un tableau qui a exactement la grandeur pour contenir la chaîne de caractères et la terminaison '\0'. Les
caractères de la chaîne peuvent être changés, mais le nom A va toujours pointer sur la même adresse en mémoire.
B est un pointeur qui est initialisé de façon à ce qu'il pointe sur une chaîne de caractères constante stockée quelque
part en mémoire. Le pointeur peut être modifié et pointer sur autre chose. La chaîne constante peut être lue, copiée
ou affichée, mais pas modifiée.

Modification
Si nous affectons une nouvelle valeur à un pointeur sur une chaîne de caractères constante, nous risquons de perdre
la chaîne constante. D'autre part, un pointeur sur char a l'avantage de pouvoir pointer sur des chaînes de n'importe
quelle longueur:

Exemple
char *A = "Petite chaîne";
char *B = "Deuxième chaîne un peu plus longue";
A = B;

Maintenant A et B pointent sur la même chaîne; la "Petite chaîne" est perdue:

Attention !
Les affectations discutées ci-dessus ne peuvent pas être effectuées avec des tableaux de caractères:
Exemple
char A[45] = "Petite chaîne";
char B[45] = "Deuxième chaîne un peu plus longue";
char C[30];
A = B; /* IMPOSSIBLE -> ERREUR !!! */
C = "Bonjour !"; /* IMPOSSIBLE -> ERREUR !!! */

Dans cet exemple, nous essayons de copier l'adresse de B dans A, respectivement l'adresse de la chaîne constante
dans C. Ces opérations sont impossibles et illégales parce que l'adresse représentée par le nom d'un tableau reste
toujours constante.
Pour changer le contenu d'un tableau, nous devons changer les composantes du tableau l'une après l'autre (p.ex.
dans une boucle) ou déléguer cette charge à une fonction de <stdio> ou <string>.

Conclusions:
Utilisons des tableaux de caractères pour déclarer les chaînes de caractères que nous voulons modifier.
Utilisons des pointeurs sur char pour manipuler des chaînes de caractères constantes (dont le contenu ne change
pas).
Utilisons de préférence des pointeurs pour effectuer les manipulations à l'intérieur des tableaux de caractères. (voir
aussi les remarques ci-dessous).

Perspectives et motivation
- Avantages des pointeurs sur char
Comme la fin des chaînes de caractères est marquée par un symbole spécial, nous n'avons pas besoin de connaître la
longueur des chaînes de caractères; nous pouvons même laisser de côté les indices d'aide et parcourir les chaînes à
l'aide de pointeurs.
Cette façon de procéder est indispensable pour traiter de chaînes de caractères dans des fonctions. En anticipant sur
la matière du chapitre 10, nous pouvons ouvrir une petite parenthèse pour illustrer les avantages des pointeurs dans
la définition de fonctions traitant des chaînes de caractères:

Pour fournir un tableau comme paramètre à une fonction, il faut passer l'adresse du tableau à la fonction. Or, les
paramètres des fonctions sont des variables locales, que nous pouvons utiliser comme variables d'aide. Bref, une
fonction obtenant une chaîne de caractères comme paramètre, dispose d'une copie locale de l'adresse de la chaîne.
Cette copie peut remplacer les indices ou les variables d'aide du formalisme tableau.
Discussion d'un exemple
Reprenons l'exemple de la fonction strcpy, qui copie la chaîne CH2 vers CH1. Les deux chaînes sont les arguments
de la fonction et elles sont déclarées comme pointeurs sur char. La première version de strcpy est écrite entièrement
à l'aide du formalisme tableau:

void strcpy (char *CH1, char *CH2)


{
int I;
I=0;
while ((CH1[I]=CH2[I]) != '\0')
I++;
}

Dans une première approche, nous pourrions remplacer simplement la notation tableau[I] par *(tableau + I), ce qui
conduirait au programme:

void strcpy (char *CH1, char *CH2)


{
int I;
I=0;
while ((*(CH1+I)=*(CH2+I)) != '\0')
I++;
}
Cette transformation ne nous avance guère, nous avons tout au plus gagné quelques millièmes de secondes lors de la
compilation. Un 'véritable' avantage se laisse gagner en calculant directement avec les pointeurs CH1 et CH2 :

void strcpy(char *CH1, char *CH2)


{
while ((*CH1=*CH2) != '\0')
{
CH1++;
CH2++;
}
}
Un vrai professionnel en C escaladerait les 'simplifications' jusqu'à obtenir:

void strcpy(char *CH1, char *CH2)


{
while (*CH1++ = *CH2++)
;
}

Exercice 9.7
Ecrire un programme qui lit deux tableaux d'entiers A et B et leurs dimensions N et M au clavier et qui ajoute les
éléments de B à la fin de A. Utiliser deux pointeurs PA et PB pour le transfer et afficher le tableau résultant A.
Exercice 9.8
Ecrire de deux façons différentes, un programme qui vérifie sans utiliser une fonction de <string>, si une chaîne CH
introduite au clavier est un palindrome:
a) en utilisant uniquement le formalisme tableau
b) en utilisant des pointeurs au lieu des indices numériques
Rappel: Un palindrome est un mot qui reste le même qu'on le lise de gauche à droite ou de droite à gauche:
Exemples:
PIERRE ==> n'est pas un palindrome
OTTO ==> est un palindrome
23432 ==> est un palindrome

Exercice 9.9
Ecrire un programme qui lit une chaîne de caractères CH et détermine la longueur de la chaîne à l'aide d'un pointeur
P. Le programme n'utilisera pas de variables numériques.

Exercice 9.10
Ecrire un programme qui lit une chaîne de caractères CH et détermine le nombre de mots contenus dans la chaîne.
Utiliser un pointeur P, une variable logique, la fonction isspace et une variable numérique N qui contiendra le
nombre des mots.

Exercice 9.11
Ecrire un programme qui lit une chaîne de caractères CH au clavier et qui compte les occurrences des lettres de
l'alphabet en ne distinguant pas les majuscules et les minuscules. Utiliser un tableau ABC de dimension 26 pour
mémoriser le résultat et un pointeur PCH pour parcourir la chaîne CH et un pointeur PABC pour parcourir ABC.
Afficher seulement le nombre des lettres qui apparaissent au mois une fois dans le texte.

Exemple:
Entrez une ligne de texte (max. 100 caractères) :
Jeanne
La chaîne "Jeanne" contient :
1 fois la lettre 'A'
2 fois la lettre 'E'
1 fois la lettre 'J'
3 fois la lettre 'N'

Exercice 9.12
Ecrire un programme qui lit un caractère C et une chaîne de caractères CH au clavier. Ensuite toutes les occurrences
de C dans CH seront éliminées. Le reste des caractères dans CH sera tassé à l'aide d'un pointeur et de la fonction
strcpy.

Vous aimerez peut-être aussi