Chapitre 1 Les bases de la programmation en C
1.1 Historique
Le C a été conçu en 1972 par Dennis Richie et Ken Thompson, chercheurs aux Bell Labs, afin de développer un système d'exploitation UNIX sur un DEC PDP-11. En 1978, Brian Kernighan et Dennis
Richie publient la définition classique du C dans le livre
The C Programming language [
6]. Le C devenant de plus en plus populaire dans les années 80, plusieurs groupes mirent sur le
marché des compilateurs comportant des extensions particulières. En 1983, l'ANSI (American National Standards Institute) décida de normaliser le langage ; ce travail s'acheva en 1989 par
la définition de la norme ANSI C. Celle-ci fut reprise telle quelle par l'ISO (International Standards Organization) en 1990. C'est ce standard, ANSI C, qui est décrit dans le présent
document.
1.2 La compilation
Le C est un langage
compilé (par opposition aux langages interprétés). Cela signifie qu'un programme C est décrit par un fichier texte, appelé
fichier source. Ce
fichier n'étant évidemment pas exécutable par le microprocesseur, il faut le traduire en langage machine. Cette opération est effectuée par un programme appelé
compilateur. La compilation
se décompose en fait en 4 phases successives :
-
Le traitement par le préprocesseur : le fichier source est analysé par le préprocesseur qui effectue des transformations purement textuelles (remplacement de chaînes de
caractères, inclusion d'autres fichiers source ...).
-
La compilation : la compilation proprement dite traduit le fichier généré par le préprocesseur en assembleur, c'est-à-dire en une suite d'instructions du microprocesseur
qui utilisent des mnémoniques rendant la lecture possible.
-
L'assemblage :cette opération transforme le code assembleur en un fichier binaire, c'est-à-dire en instructions directement compréhensibles par le processeur.
Généralement, la compilation et l'assemblage se font dans la foulée, sauf si l'on spécifie explicitement que l'on veut le code assembleur. Le fichier produit par l'assemblage est appelé
fichier objet.
-
L'édition de liens : un programme est souvent séparé en plusieurs fichiers source, pour des raisons de clarté mais aussi parce qu'il fait généralement appel à des
librairies de fonctions standard déjà écrites. Une fois chaque code source assemblé, il faut donc lier entre eux les différents fichiers objets. L'édition de liens produit alors un fichier dit
exécutable.
Les différents types de fichiers utilisés lors de la compilation sont distingués par leur suffixe. Les fichiers source sont suffixés par
.c, les fichiers prétraités par le
préprocesseur par
.i, les fichiers assembleur par
.s, et les fichiers objet par
.o. Les fichiers objets correspondant aux librairies pré-compilées ont pour suffixe
.a.
Le compilateur C sous UNIX s'appelle
cc. On utilisera de préférence le compilateur
gccdu projet GNU. Ce compilateur est livré gratuitement avec sa documentation et ses
sources. Par défaut,
gcc active toutes les étapes de la compilation. On le lance par la commande
gcc [options] fichier.c [-llibrairies]
Par défaut, le fichier exécutable s'appelle
a.out. Le nom de l'exécutable peut être modifié à l'aide de l'option
-o.
Les éventuelles librairies sont déclarées par la chaîne
-llibrairie. Dans ce cas, le système recherche le fichier
liblibrairie.a dans
le répertoire contenant les librairies pré-compilées (généralement
/usr/lib/). Par exemple, pour lier le programme avec la librairie mathématique, on spécifie
-lm. Le fichier
objet correspondant est
libm.a. Lorsque les librairies pré-compilées ne se trouvent pas dans le répertoire usuel, on spécifie leur chemin d'accès par l'option
-L.
Les options les plus importantes du compilateur
gcc sont les suivantes :
-
-c : supprime l'édition de liens ; produit un fichier objet.
-
-E : n'active que le préprocesseur (le résultat est envoyé sur la sortie standard).
-
-g : produit des informations symboliques nécessaires au débogueur.
-
-Inom-de-répertoire : spécifie le répertoire dans lequel doivent être recherchés les fichiers en-têtes à inclure (en plus du répertoire courant).
-
-Lnom-de-répertoire : spécifie le répertoire dans lequel doivent être recherchées les librairies précompilées (en plus du répertoire usuel).
-
-o nom-de-fichier : spécifie le nom du fichier produit. Par défaut, le exécutable fichier s'appelle a.out.
-
-O, -O1, -O2, -O3 : options d'optimisations. Sans ces options, le but du compilateur est de minimiser le coût de la compilation. En rajoutant l'une de ces
options, le compilateur tente de réduire la taille du code exécutable et le temps d'exécution. Les options correspondent à différents niveaux d'optimisation : -O1 (similaire à
-O) correspond à une faible optimisation, -O3 à l'optimisation maximale.
-
-S : n'active que le préprocesseur et le compilateur ; produit un fichier assembleur.
-
-v : imprime la liste des commandes exécutées par les différentes étapes de la compilation.
-
-W : imprime des messages d'avertissement (warning) supplémentaires.
-
-Wall : imprime tous les messages d'avertissement.
Pour plus de détails sur
gcc, on peut consulter le chapitre 4 de [
8].
1.4 Structure d'un programme C
Une
expressionest une suite de composants élémentaires syntaxiquement correcte, par exemple
x = 0
ou bien
(i >= 0) && (i < 10) && (p[i] != 0)
Une
instruction est une expression suivie d'un point-virgule. Le point-virgule signifie en quelque sorte ``évaluer cette expression''. Plusieurs instructions peuvent être rassemblées
par des accolades
{ et
} pour former une
instruction composéeou
blocqui est syntaxiquement équivalent à une instruction. Par exemple,
if (x != 0)
{
z = y / x;
t = y % x;
}
Une instruction composée d'un spécificateur de type et d'une liste d'identificateurs séparés par une virgule est une
déclaration.Par exemple,
int a;
int b = 1, c;
double x = 2.38e4;
char message[80];
En C, toute variable doit faire l'objet d'une déclaration avant d'être utilisée.
Un programme C se présente de la façon suivante :
[directives au préprocesseur]
[déclarations de variables externes]
[fonctions secondaires]
main()
{déclarations de variables internes
instructions
}
La fonction principale
mainpeut avoir des paramètres formels. On supposera dans un premier temps que la fonction
main n'a pas de valeur de retour. Ceci est toléré par le
compilateur mais produit un message d'avertissement quand on utilise l'option
-Wall de
gcc (
cf. chapitre 4).
Les fonctions secondaires peuvent être placées indifféremment avant ou après la fonction principale. Une fonction secondaire peut se décrire de la manière suivante :
type ma_fonction ( arguments )
{déclarations de variables internes
instructions
}
Cette fonction retournera un objet dont le type sera
type (à l'aide d'une instruction comme
return objet;). Les
arguments
de la fonction obéissent à une syntaxe voisine de celle des déclarations : on met en argument de la fonction une suite d'expressions
type objet séparées par des
virgules. Par exemple, la fonction secondaire suivante calcule le produit de deux entiers :
int produit(int a, int b)
{
int resultat;
resultat = a * b;
return(resultat);
}
Une des particularités du type
char en C est qu'il peut être assimilé à un entier : tout objet de type
char peut être utilisé dans une expression qui utilise des objets
de type entier. Par exemple, si
c est de type
char, l'expression
c + 1 est valide. Elle désigne le caractère suivant dans le code ASCII. La table précédente donne le code
ASCII (en décimal, en octal et en hexadécimal) des caractères imprimables. Ainsi, le programme suivant imprime le caractère 'B'.
main()
{
char c = 'A';
printf("%c", c + 1);
}
Suivant les implémentations, le type
char est signé ou non. En cas de doute, il vaut mieux préciser
unsigned char ou
signed char. Notons que tous les caractères
imprimables sont positifs.
1.5.2 Les types entiers
Le mot-clef désignant le type entier est
int. Un objet de type
int est représenté par un mot ``naturel'' de la machine utilisée, 32 bits pour un DEC alpha ou un PC
Intel.
Le type
int peut être précédé d'un attribut de précision (
short ou
long) et/ou d'un attribut de représentation (
unsigned).Un objet de type
short int a
au moins la taille d'un
char et au plus la taille d'un
int. En général, un
short int est codé sur 16 bits. Un objet de type
long int a au moins la taille
d'un
int (64 bits sur un DEC alpha, 32 bits sur un PC Intel).
|
|
DEC Alpha
|
PC Intel (Linux)
|
|
|
char
|
8 bits
|
8 bits
|
caractère
|
|
short
|
16 bits
|
16 bits
|
entier court
|
|
int
|
32 bits
|
32 bits
|
entier
|
|
long
|
64 bits
|
32 bits
|
entier long
|
|
long long
|
n.i.
|
64 bits
|
entier long (non ANSI)
|
Table 1.2: Les types entiers
Le bit de poids fort d'un entier est son signe. Un entier positif est donc représenté en mémoire par la suite de 32 bits dont le bit de poids fort vaut 0 et les 31 autres bits
correspondent à la décomposition de l'entier en base 2. Par exemple, pour des objets de type
char (8 bits), l'entier positif 12 sera représenté en mémoire par 00001100. Un entier
négatif est, lui, représenté par une suite de 32 bits dont le bit de poids fort vaut 1 et les 31 autres bits correspondent à la valeur absolue de l'entier représentée suivant la
technique dite du ``complément à 2''. Cela signifie que l'on exprime la valeur absolue de l'entier sous forme binaire, que l'on prend le complémentaire bit-à-bit de cette valeur et que l'on
ajoute 1 au résultat. Ainsi, pour des objets de type
signed char (8 bits), -1 sera représenté par 11111111, -2 par 11111110, -12 par par 11110100.Un
int peut donc
représenter un entier entre -2
31 et (2
31-1). L'attribut
unsigned spécifie que l'entier n'a pas de signe. Un
unsigned
int peut donc représenter un entier entre 0 et (2
32-1). Sur un DEC alpha, on utilisera donc un des types suivants en fonction de la taille des données à
stocker :
|
signed char
|
[-27;27[
|
|
unsigned char
|
[0;28[
|
|
short int
|
[-215; 215[
|
|
unsigned short int
|
[0;216[
|
|
int
|
[-231;231[
|
|
unsigned int
|
[0;232[
|
|
long int (DEC alpha)
|
[-263;263[
|
|
unsigned long int (DEC alpha)
|
[0;264[
|
Plus généralement, les valeurs maximales et minimales des différents types entiers sont définies dans la librairie standard
limits.h.
Le mot-clef
sizeof a pour syntaxe
sizeof(expression)
où
expression est un type ou un objet. Le résultat est un entier égal au nombre d'octets nécessaires pour stocker le type ou l'objet. Par exemple
unsigned short x;
taille = sizeof(unsigned short);
taille = sizeof(x);
Dans les deux cas,
taille vaudra 4.
Pour obtenir des programmes portables, on s'efforcera de ne jamais présumer de la taille d'un objet de type entier. On utilisera toujours une des constantes de
limits.h ou le résultat
obtenu en appliquant l'opérateur
sizeof.
1.5.3 Les types flottants
Les types
float,
double et
long double servent à représenter des nombres en virgule flottante. Ils correspondent aux différentes précisions possibles.
|
|
DEC Alpha
|
PC Intel
|
|
|
float
|
32 bits
|
32 bits
|
flottant
|
|
double
|
64 bits
|
64 bits
|
flottant double précision
|
|
long double
|
64 bits
|
128 bits
|
flottant quadruple précision
|
Table 1.3: Les types flottants
Les flottants sont généralement stockés en mémoire sous la représentation de la virgule flottante normalisée. On écrit le nombre sous la forme ``
signe
0,
mantisse Bexposant''. En général,
B=2. Le digit de poids fort de la mantisse n'est jamais nul.
Un flottant est donc représenté par une suite de bits dont le bit de poids fort correspond au signe du nombre. Le champ du milieu correspond à la représentation binaire de l'exposant alors que les
bits de poids faible servent à représenter la mantisse.
.1.7 Les opérateurs
1.7.1 L'affectation
En C, l'affectation est un opérateur à part entière. Elle est symbolisée par le signe
=. Sa syntaxe est la suivante :
variable = expression
Le terme de gauche de l'affectation peut être une variable simple, un élément de tableau mais pas une constante. Cette expression a pour effet d'évaluer
expression et
d'affecter la valeur obtenue à
variable. De plus, cette expression possède une valeur, qui est celle
expression. Ainsi, l'expression
i = 5 vaut 5.
L'affectation effectue une
conversion de type implicite : la valeur de l'expression (terme de droite) est convertie dans le type du terme de gauche.Par exemple, le programme suivant
main()
{
int i, j = 2;
float x = 2.5;
i = j + x;
x = x + i;
printf("n %f n",x);
}
imprime pour
x la valeur 6.5 (et non 7), car dans l'instruction
i = j + x;, l'expression
j + x a été convertie en entier.
1.7.2 Les opérateurs arithmétiques
Les opérateurs arithmétiques classiques sont l'opérateur unaire
- (changement de signe) ainsi que les opérateurs binaires
|
+
|
addition
|
|
-
|
soustraction
|
|
*
|
multiplication
|
|
/
|
division
|
|
%
|
reste de la division (modulo)
|
Ces opérateurs agissent de la façon attendue sur les entiers comme sur les flottants. Leurs seules spécificités sont les suivantes :
- Contrairement à d'autres langages, le C ne dispose que de la notation / pour désigner à la fois la division entière et la division entre flottants. Si les deux opérandes sont de type
entier, l'opérateur / produira une division entière (quotient de la division). Par contre, il délivrera une valeur flottante dès que l'un des opérandes est un flottant. Par exemple,
float x;
x = 3 / 2;
affecte à x la valeur 1. Par contre
x = 3 / 2.;
affecte à x la valeur 1.5.
- L'opérateur % ne s'applique qu'à des opérandes de type entier. Si l'un des deux opérandes est négatif, le signe du reste dépend de l'implémentation, mais il est en général le même
que celui du dividende.
Notons enfin qu'il n'y a pas en C d'opérateur effectuant l'élévation à la puissance. De façon générale, il faut utiliser la fonction
pow(x,y) de la librairie
math.h pour
calculer
xy.
1.7.3 Les opérateurs relationnels
>
|
strictement supérieur
|
|
>=
|
supérieur ou égal
|
<
|
strictement inférieur
|
|
<=
|
inférieur ou égal
|
|
==
|
égal
|
|
!=
|
différent
|
Leur syntaxe est
expression-1 op expression-2
Les deux expressions sont évaluées puis comparées. La valeur rendue est de type
int (il n'y a pas de type booléen en C); elle vaut 1 si la condition est vraie, et 0
sinon.
Attention à ne pas confondre l'opérateur de test d'égalité
== avec l'opérateur d'affection
=. Ainsi, le programme
main()
{
int a = 0;
int b = 1;
if (a = b)
printf("n a et b sont egaux n");
else
printf("n a et b sont differents n");
}
imprime à l'écran
a et b sont egaux !
1.7.4 Les opérateurs logiques booléens
|
&&
|
et logique
|
|
||
|
ou logique
|
|
!
|
négation logique
|
Comme pour les opérateurs de comparaison, la valeur retournée par ces opérateurs est un
int qui vaut 1 si la condition est vraie et 0 sinon.
Dans une expression de type
expression-1 op-1 expression-2 op-2 ...expression-n
l'évaluation se fait de gauche à droite et s'arrête dès que le résultat final est déterminé. Par exemple dans
int i;
int p[10];
if ((i >= 0) && (i <= 9) && !(p[i] == 0))
...
la dernière clause ne sera pas évaluée si
i n'est pas entre 0 et 9.
11.8.1 Branchement conditionnel if---else
La forme la plus générale est celle-ci :
if (expression-1 )
instruction-1
else if (expression-2 )
instruction-2
...
else if (expression-n )
instruction-n
else
instruction-¥
avec un nombre quelconque de
else if ( ... ). Le dernier
else est toujours facultatif. La forme la plus simple est
if (expression )
instruction
Chaque
instruction peut être un bloc d'instructions.
1.8.2 Branchement multiple switch
Sa forme la plus générale est celle-ci :
switch (expression )
{case constante-1:
liste d'instructions 1
break;
case constante-2:
liste d'instructions 2
break;
...
case constante-n:
liste d'instructions n
break;
default:
liste d'instructions ¥
break;
}
Si la valeur de
expression est égale à l'une des
constantes, la
liste d'instructions correspondant est exécutée. Sinon la
liste
d'instructions ¥ correspondant à
default est exécutée. L'instruction
default est facultative.
1.9 Les boucles
Les
boucles permettent de répéter une série d'instructions tant qu'une certaine condition n'est pas vérifiée.
1.9.1 Boucle while
La syntaxe de
while est la suivante :
while (expression )
instruction
Tant que
expression est vérifiée (
i.e., non nulle),
instruction est exécutée. Si
expression est nulle au départ,
instruction ne sera jamais exécutée.
instruction peut évidemment être une instruction composée. Par exemple, le programme suivant imprime les entiers de 1 à 9.
i = 1;
while (i < 10)
{
printf("n i = %d",i);
i++;
}
1.9.2 Boucle do---while
Il peut arriver que l'on ne veuille effectuer le test de continuation qu'après avoir exécuté l'instruction. Dans ce cas, on utilise la boucle
do---while. Sa syntaxe est
do
instruction
while (expression );
Ici,
instruction sera exécutée tant que
expression est non nulle. Cela signifie donc que
instruction est toujours exécutée au moins une
fois. Par exemple, pour saisir au clavier un entier entre 1 et 10 :
int a;
do
{
printf("n Entrez un entier entre 1 et 10 : ");
scanf("%d",&a);
}
while ((a <= 0) || (a > 10));
1.9.3 Boucle for
La syntaxe de
for est :
for (expr 1 ;expr 2 ;expr 3)
instruction
Une version équivalente plus intuitive est :
expr 1;
while (expr 2 )
{instruction
expr 3;
}
Par exemple, pour imprimer tous les entiers de 0 à 9, on écrit :
for (i = 0; i < 10; i++)
printf("n i = %d",i);
A la fin de cette boucle,
i vaudra 10. Les trois expressions utilisées dans une boucle
for peuvent être constituées de plusieurs expressions séparées par des virgules.
Cela permet par exemple de faire plusieurs initialisations à la fois. Par exemple, pour calculer la factorielle d'un entier, on peut écrire :
int n, i, fact;
for (i = 1, fact = 1; i <= n; i++)
fact *= i;
printf("%d ! = %d n",n,fact);
On peut également insérer l'instruction
fact *= i; dans la boucle
for ce qui donne :
int n, i, fact;
for (i = 1, fact = 1; i <= n; fact *= i, i++);
printf("%d ! = %d n",n,fact);
On évitera toutefois ce type d'acrobaties qui n'apportent rien et rendent le programme difficilement lisible.
1.11 Les fonctions d'entrées-sorties classiques
Il s'agit des fonctions de la librairie standard
stdio.h utilisées avec les unités classiques d'entrées-sorties, qui sont respectivement le clavier et l'écran. Sur certains
compilateurs, l'appel à la librairie
stdio.h par la directive au préprocesseur
#include <stdio.h>
n'est pas nécessaire pour utiliser
printf et
scanf.
1.11.1 La fonction d'écriture printf
La fonction
printf est une fonction d'impression formatée, ce qui signifie que les données sont converties selon le format particulier choisi. Sa syntaxe est
printf("chaîne de contrôle ",expression-1, ..., expression-n);
La
chaîne de contrôle contient le texte à afficher et les spécifications de format correspondant à chaque expression de la liste. Les spécifications de format ont pour but
d'annoncer le format des données à visualiser. Elles sont introduites par le caractère
%, suivi d'un caractère désignant le format d'impression. Les formats d'impression en C sont
donnés à la table
1.5.
En plus du caractère donnant le type des données, on peut éventuellemnt préciser certains paramètres du format d'impression, qui sont spécifiés entre le
% et le caractère de conversion
dans l'ordre suivant :
- largeur minimale du champ d'impression : %10d spécifie qu'au moins 10 caractères seront réservés pour imprimer l'entier. Par défaut, la donnée sera cadrée à droite du champ. Le
signe - avant le format signifie que la donnée sera cadrée à gauche du champ (%-10d).
- précision : %.12f signifie qu'un flottant sera imprimé avec 12 chiffres après la virgule. De même %10.2f signifie que l'on réserve 12 caractères (incluant le caractère
.) pour imprimer le flottant et que 2 d'entre eux sont destinés aux chiffres après la virgule. Lorsque la précision n'est pas spécifiée, elle correspond par défaut à 6 chiffres
après la virgule. Pour une chaîne de caractères, la précision correspond au nombre de caractères imprimés : %30.4s signifie que l'on réserve un champ de 30 caractères pour imprimer
la chaîne mais que seulement les 4 premiers caractères seront imprimés (suivis de 26 blancs).
|
format
|
conversion en
|
écriture
|
|
%d
|
int
|
décimale signée
|
|
%ld
|
long int
|
décimale signée
|
|
%u
|
unsigned int
|
décimale non signée
|
|
%lu
|
unsigned long int
|
décimale non signée
|
|
%o
|
unsigned int
|
octale non signée
|
|
%lo
|
unsigned long int
|
octale non signée
|
|
%x
|
unsigned int
|
hexadécimale non signée
|
|
%lx
|
unsigned long int
|
hexadécimale non signée
|
|
%f
|
double
|
décimale virgule fixe
|
|
%lf
|
long double
|
décimale virgule fixe
|
|
%e
|
double
|
décimale notation exponentielle
|
|
%le
|
long double
|
décimale notation exponentielle
|
|
%g
|
double
|
décimale, représentation la plus courte parmi %f et %e
|
|
%lg
|
long double
|
décimale, représentation la plus courte parmi %lf et %le
|
|
%c
|
unsigned char
|
caractère
|
|
%s
|
char*
|
chaîne de caractères
|
Table 1.5: Formats d'impression pour la fonction printf
Exemple :
#include <stdio.h>
main()
{
int i = 23674;
int j = -23674;
long int k = (1l << 32);
double x = 1e-8 + 1000;
char c = 'A';
char *chaine = "chaine de caracteres";
printf("impression de i: n");
printf("%d t %u t %o t %x",i,i,i,i);
printf("nimpression de j: n");
printf("%d t %u t %o t %x",j,j,j,j);
printf("nimpression de k: n");
printf("%d t %o t %x",k,k,k);
printf("n%ld t %lu t %lo t %lx",k,k,k,k);
printf("nimpression de x: n");
printf("%f t %e t %g",x,x,x);
printf("n%.2f t %.2e",x,x);
printf("n%.20f t %.20e",x,x);
printf("nimpression de c: n");
printf("%c t %d",c,c);
printf("nimpression de chaine: n");
printf("%s t %.10s",chaine,chaine);
printf("n");
}
Ce programme imprime à l'écran :
impression de i:
23674 23674 56172 5c7a
impression de j:
-23674 4294943622 37777721606 ffffa386
impression de k:
0 0 0
4294967296 4294967296 40000000000 100000000
impression de x:
1000.000000 1.000000e+03 1000
1000.00 1.00e+03
1000.00000001000000000000 1.00000000001000000000e+03
impression de c:
A 65
impression de chaine:
chaine de caracteres chaine de
1.11.2 La fonction de saisie scanf
La fonction
scanf permet de saisir des données au clavier et de les stocker aux adresses spécifiées par les arguments de la fonctions.
scanf("chaîne de contrôle",argument-1,...,argument-n)
La
chaîne de contrôle indique le format dans lequel les données lues sont converties. Elle ne contient pas d'autres caractères (notamment pas de
n). Comme pour
printf, les conversions de format sont spécifiées par un caractère précédé du signe
%. Les formats valides pour la fonction
scanf diffèrent légèrement de ceux de la
fonction
printf.
Les données à entrer au clavier doivent être séparées par des blancs ou des
<RETURN> sauf s'il s'agit de caractères. On peut toutefois fixer le nombre de caractères de
la donnée à lire. Par exemple
%3s pour une chaîne de 3 caractères,
%10d pour un entier qui s'étend sur 10 chiffres, signe inclus.
Exemple :
#include <stdio.h>
main()
{
int i;
printf("entrez un entier sous forme hexadecimale i = ");
scanf("%x",&i);
printf("i = %dn",i);
}
Si on entre au clavier la valeur
1a, le programme affiche
i = 26.
|
format
|
type d'objet pointé
|
représentation de la donnée saisie
|
|
%d
|
int
|
décimale signée
|
|
%hd
|
short int
|
décimale signée
|
|
%ld
|
long int
|
décimale signée
|
|
%u
|
unsigned int
|
décimale non signée
|
|
%hu
|
unsigned short int
|
décimale non signée
|
|
%lu
|
unsigned long int
|
décimale non signée
|
|
%o
|
int
|
octale
|
|
%ho
|
short int
|
octale
|
|
%lo
|
long int
|
octale
|
|
%x
|
int
|
hexadécimale
|
|
%hx
|
short int
|
hexadécimale
|
|
%lx
|
long int
|
hexadécimale
|
|
%f
|
float
|
flottante virgule fixe
|
|
%lf
|
double
|
flottante virgule fixe
|
|
%Lf
|
long double
|
flottante virgule fixe
|
|
%e
|
float
|
flottante notation exponentielle
|
|
%le
|
double
|
flottante notation exponentielle
|
|
%Le
|
long double
|
flottante notation exponentielle
|
|
%g
|
float
|
flottante virgule fixe ou notation exponentielle
|
|
%lg
|
double
|
flottante virgule fixe ou notation exponentielle
|
|
%Lg
|
long double
|
flottante virgule fixe ou notation exponentielle
|
|
%c
|
char
|
caractère
|
|
%s
|
char*
|
chaîne de caractères
|
Table 1.6: Formats de saisie pour la fonction scanf
1.11.3 Impression et lecture de caractères
Les fonctions
getchar et
putchar permettent respectivement de lire et d'imprimer des caractères. Il s'agit de fonctions d'entrées-sorties non formatées.
La fonction
getchar retourne un
int correspondant au caractère lu. Pour mettre le caractère lu dans une variable
caractere, on écrit
caractere = getchar();
Lorsqu'elle détecte la fin de fichier, elle retourne l'entier
EOF (End Of File), valeur définie dans la librairie
stdio.h. En général, la constante
EOF
vaut -1.
La fonction
putchar écrit
caractere sur la sortie standard :
putchar(caractere);
Elle retourne un
int correspondant à l'entier lu ou à la constante
EOF en cas d'erreur.
Par exemple, le programme suivant lit un fichier et le recopie caractère par caractère à l'écran.
#include <stdio.h>
main()
{
char c;
while ((c = getchar()) != EOF)
putchar(c);
}
Pour l'exécuter, il suffit d'utiliser l'opérateur de redirection d'Unix :
programme-executable < nom-fichier
Notons que l'expression
(c = getchar()) dans le programme précédent a pour valeur la valeur de l'expression
getchar() qui est de type
int. Le test
(c = getchar()) != EOF compare donc bien deux objets de type
int (signés).
Ce n'est par contre pas le cas dans le programme suivant :
#include <stdio.h>
main()
{
char c;
do
{
c = getchar();
if (c != EOF)
putchar(c);
}
while (c != EOF);
}
Ici, le test
c != EOF compare un objet de type
char et la constante
EOF qui vaut -1. Si le type
char est non signé par défaut, cette condition est donc
toujours vérifiée. Si le type
char est signé, alors le caractère de code 255, y, sera converti en l'entier -1. La rencontre du caractère y sera donc interprétée comme une fin
de fichier. Il est donc recommandé de déclarer de type
int (et non
char) une variable destinée à recevoir un caractère lu par
getchar afin de permettre la détection de fin
de fichier.
1.12 Les conventions d'écriture d'un programme C
Il existe très peu de contraintes dans l'écriture d'un programme C. Toutefois ne prendre aucune précaution aboutirait à des programmes illisibles. Aussi existe-t-il un certain nombre de
conventions.
- On n'écrit qu'une seule instruction par ligne : le point virgule d'une instruction ou d'une déclaration est toujours le dernier caractère de la ligne.
- Les instructions sont disposées de telle façon que la structure modulaire du programme soit mise en évidence. En particulier, une accolade ouvrante marquant le début d'un bloc doit être seule
sur sa ligne ou placée à la fin d'une ligne. Une accolade fermante est toujours seule sur sa ligne.
- On laisse un blanc
- entre les mots-clefs if, while, do, switch et la parenthèse ouvrante qui suit,
- après une virgule,
- de part et d'autre d'un opérateur binaire.
- On ne met pas de blanc entre un opérateur unaire et son opérande, ni entre les deux caractères d'un opérateur d'affectation composée.
- Les instructions doivent être indentées afin que toutes les instructions d'un même bloc soient alignées. Le mieux est d'utiliser le mode C d'Emacs.
-
realiser par jaidi abdellah.
-
et MRECI