Vous êtes sur la page 1sur 57

Le langage C ANSI

Jean-Paul DALBAN Universit de Nice Sophia-Antipolis 2005

Introduction
Histoire...
Le langage C est cr en 1972 aux Bell Labs (ATT) par Denis Ritchie pour dvelopper le systme d'exploitation UNIX. A partir de 1978, le livre "The C Programming Langage" de Brian Kernighan et Denis Ritchie sert de rfrence pour le langage C. Il faut attendre 1982 pour que l'ANSI (American National Standard Institute) propose un groupe de travail (X3J11) pour normaliser le langage C et sa bibliothque standard. Le langage C ainsi normalis est nomm couramment C ANSI.

Premier programme
Ce premier programme doit pouvoir tre compris grossirement avec les explications fournies. Il donne une ide de la forme d'un programme.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* premier.c: moyenne de deux entiers jp dalban - Aut 1992 */ #include <stdio.h> main() { int a,b; float c; printf("entrez deux entiers: "); scanf("%d %d",&a,&b); c = (a+b)*0.5; printf("leur moyenne est: %f \n",c); }

Les lignes de ce programme ont t numrots pour la convenance des explications. Lignes 1-4: Commentaires. Ils sont dlimits par /* et */. Ligne 5: Directive d'inclusion du fichier stdio.h ncessaire pour utiliser les entres/sorties Ligne 6: Ligne vide. Pour arer un peu. Ligne 7: Entte de la fonction main. C'est la fonction principale du programme. Elle n'a pas de paramtres (si il y en avait ils seraient entre les parenthses). Ligne 8: Dbut du corps de la fonction. Ligne 9: Dclaration de deux entiers a et b. Ligne 10: Dclaration d'un nombre flottant c (rel). Ligne 11: Ligne vide. Pour faire joli. Ligne 12: Printf affiche "entrez deux entiers: " Ligne 13: Scanf attend du clavier une rponse au format "%d %d" c'est dire deux dcimaux. Ces dcimaux taps au clavier seront placs le premier l'adresse de a (c'est dire dans la variable a), l'autre l'adresse de b (c'est dire dans la variable b).

Ligne 14: Calcul de c, moyenne de a et b. Ligne 15: Printf affiche "leur moyenne est %f \n". A la place de %f est affich la valeur de c. Le caractre \n indique un passage la ligne suivante. Ligne 16: Fin de la fonction main.

Forme gnrale d'un programme


Noms (ou identificateurs)
Le texte d'un programme est constitu: - de symboles: parenthses, accolades, ponctuation... - de valeurs: nombre, chane ... - de divers lments: variables, fonctions, types, oprateurs ... Pour distinguer ces lments il est ncessaire de les nommer. On appelle identificateur (ou simplement nom) le nom que l'on attribue dans un programme un de ces lments. Un certain nombre de noms sont prdfinis dans le langage. En dehors de ces mots-cls, le programmeur est libre du choix de ses noms. Il doit cependant respecter certaines limites. Un nom ou identificateur: - est constitu de lettres (a-zA-Z), de chiffres (0-9), du caractre '_' (underscore), - ne doit pas commencer par un chiffre, - doit avoir au plus 31 caractres - ne doit pas tre un mot cl du langage. Exemples: BON: MAL: x15, toto, nommoyennementlong, nom_moyennement_long, _a 2a, 123

Les nom qui commencent par '_' sont normalement rservs un usage particulier.

Mots-cls du langage
Un mot-cl est un nom prdfini du langage. En C ANSI, les mots-cls ne peuvent tre redfinis. Note: En C++, les mots-cls sont redfinissables (surcharge) Ces mots-cls sont: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

Forme du programme
Le langage respecte un certaine syntaxe dans laquelle interviennent diffrents symboles: parenthses, accolades, ponctuation, espaces. Ces symboles sont ncessaires pour assurer la structure du programme. Si cette syntaxe est respecte, la forme peut tre libre. On peut crire un programme entier sur une ligne...
void main(void){int a,b;float c;c=(a+b)*0.5;}

Dans cet exemple (correct), on a enlev tous les espaces et toutes les fin de lignes superflues. Les seuls espaces restants sont indispensables car sinon des identificateurs seraient 'colls'... Evidemment, un programme doit pouvoir tre lu facilement. Il doit tre - ar (lignes vides), - indent (retraits indiquant une structure) - bien comment (mais pas trop non plus) Dans le programme suivant on a ajout des fins de lignes (ret) et des tabulations (tab) pour indenter le texte.
void main(void) ret { ret tab int a,b; ret tab float c; ret tab c=(a+b)*0.5; ret } ret

Des espaces peuvent en outre tre ajouts si le besoin se fait sentir.

Commentaires
Les commentaires sont compris entre /* et */. Le compilateur ignore compltement ce qui est entre ces deux limites. Exemple:
/* Ceci est un commentaire */ /* Ceci aussi */

Certains compilateurs acceptent les commentaires la C++: Exemple:


// // // // Ceci est un commentaire en C++

Les types de base


Les types de base du langage C sont: les entiers, les flottants, les caractres. De manire simple, un type est un modle pour les objets utiliss par le programme. Des objets plus complexes pourront tre dfinis l'aide des types de base et de constructeurs: les tableaux et les structures. En outre, des objets de type pointeur peuvent tre utiliss pour manipuler les objets complexes.

Les tableaux, les structures et les pointeurs sont tudis plus loin.

Pourquoi un type ?
Rien ne sert de connatre la valeur d'un objet si on n'en pas le type... En effet, on peut toujours connatre la valeur d'un objet, ne serait-ce qu'en regardant dans la mmoire. On verra par exemple dans la mmoire l'adresse de l'objet : 01001101 Cette configuration binaire peut tre aussi bien: - le nombre 77 - la lettre M (code ASCII) - ou toute autre chose, tout dpend de la signification de l'objet pour le programmeur. Si l'on connait le type, alors parler de valeur a une signification. Si cet objet est un entier dire que sa valeur est 77 est correct. Si c'est un caractre, c'est le caractre M. Plus prcisment, le type d'un objet dfinit: - quel ensemble de valeurs qu'il peut recevoir, - comment une valeur est code en binaire dans la mmoire, - quelles sont les oprations qui lui sont applicables. La notion de type assure ainsi une certaine qualit de la programmation car le compilateur peut faire des vrifications sur la bonne utilisation de objets (et des paramtres des fonctions).

Les entiers
Le type entier est le type int. Par dfaut, il s'agit d'entier sign.

Varits d'entiers

Le type int peut tre prfix de long ou short. Ce prfixe permet de choisir entre entier 16 bits (short int) et entier 32 bits (long int). Quant au type int, il est au format le plus long que la machine est capable de traiter efficacement : 16 bits sur les machines 16 bits (courant en informatique industrielle) ou 32 bits sur la plupart des machines actuelles. Les entiers sont signs par dfaut. Si l'on souhaite que le bit de signe soit supprim pour permettre la numration d'aller plus loin, on prfixe le type entier par unsigned. unsigned signed unsigned signed 16 16 32 32 0 -32 768 0 -2 147 483 648 65 535 32 767 4 294 967 295 2 147 483 647

Formats en mmoire

La reprsentation mmoire habituelle est celle des nombre signs en complments deux, 16 ou 32 bits (2 ou 4 octets).

Ecriture des constantes

Le prfixe de la constante indique la base:

- pas de prfixe : dcimal -0 : octal (lire zro et non lettre o) - 0x : hexadcimal Il est possible aussi d'indiquer avec l'aide d'un suffixe le type de la constante. A dfaut de suffixe, la constante est int ou long int si sa valeur est trop grande pour tre de type int. Le suffixe indique le type: - u,U - l,L -> unsigned int -> long int

Notes: Seuls les nombres exprims en dcimal peuvent tre signs. Les constantes short n'existent pas. Exemples: 99 99L -1 0xff 0111 0xffffffffU entier en dcimal, valeur +99 entier long en dcimal, valeur +99, rang sur 32 bits entier en dcimal, valeur -1 entier en hexadcimal, valeur 255 soit 15*16+15*1 entier en octal, valeur 73 soit 1*64+1*8+1*1 entier non sign en hexadcimal, valeur 4 294 967 295 et non -1

Les caractres
Le type caractre est char. Un caractre est considr en C comme un entier. Par exemple lcriture: c = A+1 ; est lgale. Ceci permet de manipuler les caractres comme des nombres et notamment de les comparer.

Valeurs des caractres

Chaque caractre possde une valeur entire comprise entre 0 et 127, code en mmoire sur 8 bits. Le langage C ne spcifie pas si le type char est sign ou non. Il est donc imprudent daffecter une variable de type char une valeur dont le bit 7 serait gal 1. Ce problme ne devrait pas se poser si lon se limite aux 128 caractres ASCII (voir table en annexe). Un caractre peut cependant tre dclar unsigned char pour signifier au compilateur que le bit 7 ne doit pas tre considr comme un bit de signe. Ceci permet de manipuler des caractres de valeurs comprises entre 0 et 255. A linverse, un caractre peut tre dclar signed char. Exemple:
char c ; int i ; c = \xFF ; i = c ;

Le bit 7 de c vaut 1. Le caractre peut tre considr par certains compilateurs comme positif et ngatif par dautres... La variable i peut donc valoir -1 ou 255 !!! En dclarant unsigned char c ou signed char c, on lve le doute: dans le premier cas, i vaut 255, dans le second, il vaut -1.

Constantes caractres

Les caractres sont toujours reprsents entre deux guillemets simples. Exemples:

A, B, C ... a, b, c ... 0, 1, 2 ... +, =, ? ... Certains caractres non imprimables trs courants sont reprsents prfixs par un back-slash (\): \f \n \r \t
saut de page (form feed) saut la ligne (new line) retour debut ligne (return) tabulation (tab)

Tous les caractres peuvent tre reprsents en indiquant leur code ASCII en octal ou en hexadcimal de la manire suivante: \ooo caractre de code octal oo \xhh caractre de code hexadcimal hh

Les flottants
Le type float permet de mmoriser des nombres dcimaux.

Varits de flottants

Les variables de type float sont habituellement ranges sur 32 bits sous une forme M.10N. Comme cet espace mmoire ne permet pas de ranger une mantisse (M) et un exposant (N) suffisamment grands, on dispose dun type double prcision double qui occupe normalement un espace mmoire double (64 bits) et un type de prcision tendue long double (80 bits). On trouve dans les fichiers <limits.h> et <float.h> des informations sur ces types pour le systme sur lequel vous travaillez.

Formats en mmoire

La reprsentation mmoire habituelle est celle normalise par l'IEEE.

Ecriture des constantes


3.14159 -4.0

Les flottants peuvent tre crits en virgule fixe:

oubien en virgule flottante:


6.02e24 -2e5 123e-3 qui signifie 6.02.1024 -2.105 123.10-3

Dans cette dernire notation, ma mantisse est un dcimal relatif et lexposant est un entier relatif. Le signe introduisant lexposant est e ou E.

Notion de variable
Une variable est une zone de mmoire affecte d'un type. Le rle d'une variable est de mmoriser une valeur correspondant son type. Toutes variable doit tre dclar avant son utilisation.
int x; int a,b,c; char c; float u,v; /* /* /* /* entier x */ trois entiers: a,b,c */ c est un caractre */ u et v sont des flottants */

Il est possible d'initialiser une variable au moment de sa dclaration.

int x=1000; int a=1,b,c; char c='A'; float u=1.0,v=2.5;

Oprateurs
Oprateurs arithmtiques
BINAIRES (deux oprandes) + somme diffrence * produit / quotient % reste UNAIRES (un seul oprande) + identit oppos -dcrmentation ++ incrmentation

Les quatre oprations: + - * /

Les oprateurs + - et * s'appliquent aussi bien des nombres entiers que flottants. Exemple:
/* moyenne.c : moyenne de deux nombres flottants */ #include <stdio.h> void main(void) { float a,b,m; scanf("%f%f",&a,&b); m = (a+b)/2; printf("moyenne:%f\n",m); }

Division des entiers

L'oprateur / appliqu des entiers ralise une division entire. Mais si un des deux oprandes est flottant, il ralise la division flottante.
3 / 2donne 1 (division entire) 3.0 / 2 donne 1.5 (division des flottants car un oprande est flottant)

L'oprateur % n'a de signification que pour des entiers. Exemple:


/* euclide.c : division euclidienne de deux entiers */ #include <stdio.h> void main(void) { int a,b,q,r; scanf("%d%d",&a,&b); q = a/b; r = a%b; printf("quotient:%d\nreste:%d\n",q,r); }

Incrmentation et dcrmentation

Les oprations d'incrmentation et de dcrmentation dcrmentent ou incrmentent un entier, un flottant.ou un pointeur. Elles sont surtout utiles dans des boucles pour grer des indices ou des compteurs entiers. Applique AVANT le nom de l'objet une telle opration est excute AVANT l'instruction qui la contient. Applique APRES, elle est excute APRES. Exemple:
/* increm.c : incrementation d'un compteur entier #include <stdio.h> void main(void) { int c1=0, c2=0; printf("%d %d\n", c1 , c2); c1++; ++c2; printf("%d %d\n", c1 , c2); printf("%d %d\n", c1++ , ++c2); } */

/* 0 et 0 */ /* 1 et 1 */ /* 1 et 2 */

L'utilisation de ces oprateurs pour les pointeurs est prsent au paragraphe des pointeurs.

Oprateurs sur les bits


Les oprateurs bit bit permettent de de manipuler les bits des nombres entiers. BINAIRE & | ^ >> << UNAIRE ~

ET bit bit OU bit bit OU exclusif bit bit dcalage droite dcalage gauche complment 1 bit bit

ATTENTION: Ne pas confondre ces oprateurs bit bit avec les oprateurs logiques qui sont prsents plus loin.

Oprations bit bit: & | ^ ~

L'oprateur est appliqu bit bit c'est dire: R = A op B <=> Rk = Ak op Bk pour k variant de 0 n-1 o n vaut 8 pour les caractres, 16 pour les short et 32 pour les long. Exemple:
/* bit.c : oprations #include <stdio.h> void main(void) { int a,b,c; a = 0xFF00; b = 0x1234; c = a & b; printf("%x\n",c); c = ~c; printf("%x\n",c); } bit bit, ici un ET et un NON */

/* /* /* /* /* /*

a= 1111 b= 0001 c= 0001 1200 en c= 1110 EDFF en

1111 0010 0010 hexa 1101 hexa

0000 0011 0000 */ 1111 */

0000 */ 0100 */ 0000 */ 1111 */

Dcalages: >> et <<

Ces oprateurs permettent de dcaler un entier droite ou gauche d'un nombre donn de bits: R>>n a pour rsultat R dcal de n bits droite

R<<n a pour rsultat R dcal de n bits gauche Dans les deux cas: - les bits sortant sont perdus - les bits entrant sont des 0 Note: Le dcalage na pas lieu dans le premier oprande. Celui-ci nest donc pas affect par lopration. Exemple:
/* shift.c : oprations de dcalage */ #include <stdio.h> void main(void) { int a,b; a = 0x1234; /* a= 0001 0010 0011 0100 */ b = a>>4; /* b= 0000 0001 0010 0011 , a inchang */ printf("%x\n",b); /* 0123 en hexa */ b = a<<4; /* b= 0010 0011 0100 0000 , a inchang */ printf("%x\n",b); /* 2340 en hexa */ }

Oprateurs relationnels
Les oprateurs relationnels permettent de raliser des comparaisons. < > <= >= == != infrieur suprieur infrieur ou gal suprieur ou gal gal diffrent

Le rsultat d'une comparaison est un entier qui vaut: - 0 quand le rsultat est FAUX - 1 quand le rsultat est VRAI Exemple:
/* compare.c : oprateurs relationnels */ #include <stdio.h> void main(void) { int a,b,c; a = 1; b = 2; c = (a>b) printf("%d\n",c); /* rponse 0, donc (a>b) est FAUX */ b = (a<b); printf("%d\n",c); /* rponse 1, donc (a<b) est VRAI */ }

Mais videmment, c'est dans les test des instruction if, for, while que ces oprateurs ont leur pleine utilit. Exemple:
/* test.c : oprateurs relationnels */ #include <stdio.h> void main(void) { int a,b; a = 1; b = 2; if (a>b) printf("a suprieur b\n"); else printf("a infrieur ou gal b\n"); }

Oprateurs logiques
Ces oprateurs sont faits pour combiner plusieurs comparaisons en tests complexes. && || ! ET logique OU logique (ou inclusif) NON logique

Ils fonctionnent avec la logique habituelle: VRAI VRAI FAUX FAUX Exemple:
/* tests2.c : oprateurs logiques */ #include <stdio.h> void main(void) { int a,b,c; a = 1; b = 2; c = 3; if ( (a<=b) && (b<=c) ) printf("b appartient [a,c]\n"); /* notre cas */ else printf("b n'appartient pas [a,c]\n"); if ( !( (b==a) || (b==c) ) ) printf("b n'est ni a ni c\n"); } /* notre cas */

&& && && &&

VRAI FAUX VRAI FAUX

== == == ==

VRAI FAUX FAUX FAUX

VRAI VRAI FAUX FAUX

|| || || ||

VRAI FAUX VRAI FAUX

== == == ==

VRAI VRAI VRAI FAUX

!VRAI == FAUX !FAUX == VRAI

ATTENTION

1) Ces oprateurs considrent que FAUX=0 et que tout ce qui nest pas faux est VRAI. 2) Les oprateurs && et || nvaluent pas toujours leurs deux oprandes: - dans le calcul de c1&&c2, si c1 est FAUX, c2 nest pas valu car quelque soit sa valeur le rsultat est FAUX, - dans le calcul de c1||c2, si c1 est VRAI, c2 nest pas valu car quelque soit sa valeur le rsultat est VRAI.

Avez-vous bien compris ?

On confond souvent lusage des oprateurs bit bit et des oprateurs logiques. 1) Les oprateurs bit bit sont faits pour manipuler les bits dun entier. Par exemple, pour tester un bit lu sur un port, on peut appliquer un masque avec un & afin dcraser les bits inutiles:
if ( port & 0x4 == 0 ) printf(bit 2 du port ZERO \n); else printf(bit 2 du port UN \n);

2) Les oprateurs logiques permettent de combiner des conditions. Les oprandes dun oprateur logique sont normalement une comparaison ou un rsultat de comparaison. Par exemple, crire
if ( port && 0x4 ) ....

veut dire que lon considre port comme une condition: VRAI si un des bits vaut 1 ou FAUX si tous les bits sont 0. Ce qui est dj un peu trange, mais ventuellement acceptable... Quant 0x4, il doit tre considr comme toujours VRAI. et en ce sens il est quivalent 0x1 et la condition se rsume
if ( port ) ....

On a donc dans cet exemple utilis && de travers ! ...

10

Moralit: Il faut savoir ce que lon veut faire: manipulation de bits ou combinaison de conditions.

Affectation
L'affectation est un oprateur qui permet d'affecter (attribuer) une valeur un objet. = Affectation

A gauche du signe gal, on indique l'objet qui reoit. A droite, on indique la valeur. Exemple : "x=100" signifie bien que la variable x reoit la valeur 100. Mais l'affectation est un oprateur comme les autres et cela va nous permettre certaines critures tranges et parfois commodes. Comme tout oprateur l'affectation possde un rsultat qui est simplement la valeur affecte. Exemple1 : "c=a+(b=3)" est une expression dont l'valuation est ralise ainsi: - b est affect de la valeur 3 et l'expression devient "c=a+3", - a+3 est calcul, - c est affect du rsultat. Exemple2 : "a=b=c=0" est une expression quivalente "a=(b=(c=0))" du fait de l'associativit de droite gauche de l'affectation. Elle est donc value ainsi: - c=0 et l'expression devient a=(b=0), - b=0 et l'expression devient a=0, - a=0.

Oprateur sizeof
L'oprateur sizeof permet de connatre la taille d'un objet ou d'un type. sizeof (T) taille de T en octets o T est le nom d'un objet ou d'un type

Cet oprateur s'utilise comme une fonction dont le rsultat est un entier. Exemple:
/* sizeof.c : oprateur sizeof() */ #include <stdio.h> void main(void) { char s[] = "hello"; int L; /* taille d'une variable */ L = sizeof(s); printf("%d\n",L); caractres) */ /* taille d'un type */ printf("%d\n",sizeof(int)); printf("%d\n",sizeof(char)); }

/* 6 (voir chanes de

/* 4 (entiers 32bits) */ /* 1 */

11

Pointeurs
Adresse d'une variable et notion de pointeur
L'oprateur & permet d'obtenir l'adresse d'un objet. Exemple:
{ int x; printf ("%d \n",x); printf ("%x \n, &x); }

Dans cet exemple on affiche en dcimal la VALEUR de x, puis en hexadcimal, son ADRESSE. Note: C'est une habitude courante d'afficher les adresses en hexadcimal car les cartes mmoire sont plus facilement lisibles si les adresses sont en hexa. Une adresse peut tre mmorise dans une variable. Une telle variable est appele pointeur. Quand un pointeur a reu ladresse dun objet, on dit quil pointe vers cet objet. Appliqu un pointeur l'oprateur * dsigne l'objet point. Exemple:
{ int x,y; ... y = *&x; } /* identique y=x */

L'expression &x a pour valeur l'adresse de x. Elle pointe donc vers x. L'expression *&x est quivalente *(&x) dsigne l'objet point par &x c'est dire x.

Dclaration d'un pointeur


Bien qu'une adresse ne soit finalement qu'un entier long, les variables de type pointeur doivent tre dclares comme "pointeur vers un type". Les pointeurs sur les types simples sont dclars ainsi: int *p; float *p; char *p; p est un pointeur vers un entier p est un pointeur vers un flottant p est un pointeur vers un caractre

Voir plus loin pour les dclaration de pointeurs vers d'autres types. Exemple de pointeur vers un entier:
{ int x,y; int *px; ... px = &x; y = *px; }

/*

identique y=x

*/

Ici, la variable px est un pointeur vers un entier. La dclaration "int *px" indique en effet que la valeur pointe par px ("*px") est un entier. Ce pointeur est ensuite affect de l'adresse de x. Il pointe donc effectivement vers x. Ensuite le pointeur est utilis pour accder la valeur de x et l'affecter y.

12

Utilit des pointeurs


De manire gnrale, les pointeurs sont souvent utiliss ds que l'on manipule des types structurs surtout lorsque ceux-ci sont de grosse taille. En effet, en C les passages de paramtres (ainsi que le rsultat) des fonctions sont faits par valeur. Ceci ne pose pas de problme quand il s'agit de types simples, mais quand ce sont des types structurs le passage par valeur devient trs coteux du fait de la recopie du paramtre (ou du rsultat). Il est alors intressant de passer non la valeur de l'objet mais son adresse (qui est aussi une valeur). Une seconde raison pour passer l'adresse d'un objet une fonction est de pouvoir modifier cet objet. Exemple:
struct info { int a; ... } void traiter (struct info x) { x.a = 0 ; ... } void traiter2 (struct info *p) { (*p).a = 0 ; ... } void main(void) { struct info I;

/* dclaration de I */

traiter(I); /* application de traiter */ traiter2(&I); /* application de traiter2 */ }

Dans cet exemple, la fonction traiter prsente deux inconvnients: - I est pass par valeur, d'o recopie de la valeur de I dans x ==> efficacit mdiocre, - traiter travaille sur une copie de I et non sur I ==> le traitement effectu ne porte pas sur I ! La fonction traiter2 ne possde aucun de ces deux problmes.

Oprations sur les pointeurs


Outre les oprateurs & et *, trois oprateurs peuvent tre appliqus aux pointeurs: +, - et [ ]. Ces oprateurs sont trs lis aux tableaux. Voir ce paragraphe.

Expressions et instructions
Notion d'expression
Une expression est la combinaison de valeurs d'objets au moyen d'oprateurs et de fonctions. Les oprateurs ont t prsents prcdemment.

13

Quand une expression comporte plusieurs oprateurs, l'ordre d'excution des oprateurs est fix soit par l'utilisation de parenthses soit par dfaut par les rgles de priorit et d'associativit. Pour les oprateurs courants, ces rgles sont celles utilises en mathmatiques. Quand on n'est pas sr, le mieux est d'utiliser des parenthses ou bien de consulter la table des priorits (voir plus loin). Exemple: Dans l'expression "a*2+b*3", a est multipli par deux, b par 3, puis les deux rsultats sont additionns. La plupart des oprateurs ont pour seule action de produire un rsultat partir des oprandes (en particulier, ils ne modifient pas leurs oprandes). On dit que ces oprateurs n'ont pas d'effet de bord. Les seuls oprateurs qui prsentent des effets de bord sont l'affectation ainsi que les oprateurs d'incrmentation et de dcrmentation. Dans ces deux cas, en effet, un oprande est modifi. Exemple: Dans l'expression "a+(b=3)" l'opration "(b=3)" produit le rsultat 3 et en effet de bord affecte 3 b. Dans l'expression "x = i++" l'opration "i++" produit comme rsultat la valeur de i et en effet de bord (aprs instruction) incrmente i.

Notion d'instruction
Une instruction est une entit excutable. Cela peut tre une instruction simple ou une instruction complexe. Une instruction simple est une expression valuer: - les affectations - les appels de fonction ou bien une des instructions goto, break et continue. Une instruction complexe est une des instructions if, for, while, switch. On dit complexe car elles sont composes de plusieurs parties: condition, instructions,.... Ces instructions sont tudies plus loin. Les instructions simples sont toujours termines par un point-virgule.

Priorits des oprateurs dans une expression


En l'absence de parenthses, les oprateurs d'une expression sont appliqus en fonction de leur priorit et priorit gale en fonction de leur associativit. CATEGORIE appel,index,slection unaires arithmtiques forts arithmtiques faibles dcalages relationnels forts relationnels faibles bit bit trs fort bit bit moyen bit bit faible logique fort logique faible affectation OPERATEURS () [] -> . + - ++ -- ! ~ * & (cast) sizeof * / % + << >> < > <= >= == != & ^ | && || = ASSOCIATIVITE --------> <-----------> --------> --------> --------> --------> --------> --------> --------> --------> --------> <---PRIORITE FORTE | | | | | | | | | | | FAIBLE

14

On notera que tous les oprateurs ont une associativit de gauche droite sauf les oprateurs unaires et l'affectation. Exemple1: Dans l'expression "a*x+b" les oprateurs * et + sont de priorits diffrentes. Ils sont appliqus dans l'ordre * puis +. Exemple 2: Dans l'expression "a-b+c" les oprateurs - et + sont de priorits gales. Ils sont appliqus dans l'ordre (de gauche droite) indiqu par leur associativit: - puis +. Exemple 3: Dans l'expression "*&v+1" les oprateurs * et & sont de priorits gales et plus fortes que celle de +. Les oprateurs sont appliqus dans l'ordre: & (&v est l'adresse de v), puis * (qui dsigne la valeur pointe par &v, c'est dire v), puis +. Cette expression est donc identique "v+1". Exemple 4: "a=b=c=0" est une expression quivalente "a=(b=(c=0))" (voir le paragraphe sur l'affectation). Cette expression affecte 0 a,b et c.

Conversions de type
Conversions implicites dans les expressions
Dans une expression, l'homognt des types est thoriquement souhaitable (on nous a souvent dit de n'ajouter des pommes qu' des pommes). Cependant, dans la pratique, on est souvent amen vouloir combiner dans des expressions des valeurs de type diffrents mais voisins, par exemple int et float... Que fait donc le compilateur quand nous mixons les types?

Principe

Les deux types qui doivent tre combins par un oprateur sont souvent voisins. Et il y a toujours un type qui est "plus fort" que l'autre. Le compilateur convertit simplement la valeur du type faible en type fort et l'on se retrouve dans le cas o les deux oprandes sont de mmes types. Une telle conversion est dite implicite. Exemple: Dans l'expression "a+b", - si "int a,b" alors a+b est un int (aucune conversion) - si "float a,b" alors a+b est un float (aucune conversion) - si "int a" et "float b", alors a est converti en float et a+b est un float

Rgles de conversion pour les types signs

En fait, la conversion est effectue en deux tapes: 1) la promotion consiste lever les valeurs des types char ou short au type int (on parle souvent de la promotion entire). Cette promotion est systmatique. 2) l'ajustement consiste galiser les types des deux oprandes comme il a t expliqu ci-dessus. La "force" des types est: int < long < float < double < long double

15

Exemple: Dans l'expression "a+b", - si "short a" et "short b" alors a et b sont promus int et a+b est un int - si "short a" et "double b" alors a est promu int puis ajust double, le rsultat est double.

Rgles de conversion pour les types non-signs

Conseil d'ami: travaillez toujours en sign. Et si le non-sign est indispensable, ne le mixez pas avec du sign! Sinon, voir un ouvrage prcis.

Conversions explicite l'affectation


Une conversion a lieu lors d'une affectation si le type de l'objet dsign dans le membre de gauche est diffrent du type de la valeur du membre de droite. Cette conversion force peut amener une dgradation de la valeur. Exemple: Dans l'affectation "a=b", - si a est un float et b un double, il a perte de prcision, - si a est un int et b un float, il y a perte de la partie dcimale. Ce type de conversion relve des mmes rgles que les conversions explicites qui sont vues plus loin. Un bon compilateur doit normalement signaler les conversions dgradantes sauf si elles sont explicitement indiques par le programme. Exemple: Si a est un int et b un float, - l'affectation dgradante "a=b" est signale pour attirrer l'attention du programmeur, - l'affectation dgradante "a=(int)b" est accepte car elle est voulue (casting).

Conversion des paramtres d'une fonction


Le prototype d'une fonction dfinit le type de ses paramtres. Si une valeur passe comme paramtre est du type attendu (celui indiqu dans le prototype), elle est passe sans modification, sinon elle subit un forage de type analogue au forage par casting (voir plus loin). Exemple:
/* param.c : forage de type pour les paramtres */ void f(int x,float y) { } void main(void) { int a=1,b=2; f(a,b); /* a est pass sans changement, b est converti float */ }

Si par contre la fonction n'a pas de prototype, on a les conversions implicites suivantes: - char et short sont convertis en int, - float est converti en double.

16

Conversion explicite par le programmeur (cast)


Le programmeur peut dans une expression appliquer un forage (casting) de type. Il y a alors conversion explicite de la valeur sur laquelle s'applique le forage. Le forage de type est indiqu en prfixant la valeur forcer par le nom du type souhait entre parenthses. Exemple:
/* cast.c : oprateur de forage de type */ #include <stdio.h> void main(void) { int a=3,b=2; float x; x = a/b; /* division entire, resultat entier converti en float*/ printf("%f\n",x); /* resultat: 1.0 */ x = (float)a/(float)b; printf("%f\n",x); } /* division flottante */ /* rsultat: 1.5 */

La norme ANSI admet comme lgales toutes les conversions. On peut donc tout faire. Reste savoir si ce que l'on fait rpond notre problme. Donc attention, dans la pratique, il faut se limiter ce dont on connait les effets: - entier vers flottant: - flottant vers entier: valeur intacte troncature

Les conversions explicites sont aussi utilisables sur les pointeurs. L'objectif est souvent dans ce cas de faire certains tours de passe-passe par exemple pointer avec un mme pointeur sur des objets de types diffrents. Mais ceci est dj de la programmation avance...

Instructions de contrle
Les instructions de contrle ont pour fonction de pouvoir orienter (contrler) l'excution du programme en modifiant son cours. On a besoin habituellement de deux types de structure de contrle: 1) Les boucles permettent de REPETER une instruction ou un groupe d'instructions. Le langage C offre pour cela les instructions while et for. 2) Les aiguillages permettent d'ORIENTER le traitement vers une ou un groupe d'instructions en fonction d'une condition. On dispose de deux instructions: if et switch. Pour prsenter les diffrentes instructions de contrle, nous allons tudier un mme exercice au long des paragraphes qui suivent.

L'exercice

Il s'agit de compter les caractres 'a', 'b', et 'c' d'une chane entre au clavier. On compte aussi le nombre de caractres diffrents. Le programme pourrait avoir l'allure suivante:
/* exo.c : comptage des a,b et c dans une chane */ #include <stdio.h> void main(void) { char ch[20]; int na,nb,nc,n;

/* chaine de 19 caractres */ /* compteurs de caractres */

17

int i; na=nb=nc=n=0; scanf("%s",ch);

/* indice */

/* ICI faire le comptage des caractres a b et c */ printf("%d %d %d %d \n",na,nb,nc,n); }

Il est clair que le comptage des caractres consiste parcourir la chane (boucle) et selon qu'un caractre est un 'a', un 'b' ou un 'c' incrmenter le compteur correspondant (aiguillage). Nous allons donc examiner les diffrentes possibilits d'criture de ce traitement au cours des paragraphes qui suivent.

goto
L'instruction goto permet d'effectuer un branchement un endroit du programme dsign par une tiquette. goto etiquette L'usage de goto ne conduit pas une programmation de qualit. Le texte du programme est souvent difficile lire et donc corriger. Nous dconseillons donc son usage. L'exercice propos a cependant t trait.

L'exercice
i = 0; debut_boucle: if (ch[i] == '\0') /*

La boucle peut tre ralis avec un if et deux goto:


goto fin_boucle; */

ICI tester ch[i]

i++; goto debut_boucle; fin_boucle:

Le test de chaque caractre peut lui aussi tre fait avec goto et if:
if ( ch[i] == 'a' ) goto A; if ( ch[i] == 'b' ) goto B; if ( ch[i] == 'c' ) goto C; n++; goto suite; A: na++; goto suite; B: nb++; goto suite; C: nc++; goto suite; suite:

Le programme peut donc s'crire:


/* exo1.c : comptage des a,b et c dans une chane, version 1 */ #include <stdio.h> void main(void) { char ch[20]; int na,nb,nc,n; int i; na=nb=nc=n=0; scanf("%s",ch); i = 0; debut_boucle: if (ch[i] == '\0')

/* chaine de 19 caractres */ /* compteurs de caractres */ /* indice */

goto fin_boucle;

if ( ch[i] == 'a' ) goto A;

18

if ( ch[i] == 'b' ) goto B; if ( ch[i] == 'c' ) goto C; n++; goto suite; A: na++; goto suite; B: nb++; goto suite; C: nc++; goto suite; suite: i++; goto debut_boucle; fin_boucle: printf("%d %d %d %d \n",na,nb,nc,n); }

On constate le style "BASIC" de la programmation. La lecture est difficile cause des nombreux goto qui obligent suivre le fil du programme. Dans ce programme, il y a cependant un effort de structuration. Mais quand le programme est plus long et le programmeur moins soigneux, on obtient ce que certains nomment "un plat de spaghetti".

if
L'instruction if sert orienter le traitement en fonction d'une condition. Le if peut tre utilis avec ou sans else. if ( condition ) action if ( condition ) action1 else action2 Une condition est habituellement une expression. Elle est FAUSSE si la valeur de l'expression est 0, VRAIE sinon. Une action peut tre soit une instruction soit un bloc de plusieurs instructions. Un bloc est dlimit par des accolades: { }.

L'exercice
if if if if ( ( ( ( ch[i] == 'a' ) na++ ; ch[i] == 'b' ) nb++ ; ch[i] == 'c' ) nc++ ; (ch[i]!='a')&&(ch[i]!='b')&&(ch[i]!='c') ) n++;

Le test des caractres peut tre effectu en quatre instructions if indpendantes:

On peut aussi imbriquer les instructions if:


if ( ch[i] == 'a' ) na++ ; else if ( ch[i] == 'b' ) nb++ ; else if ( ch[i] == 'c' ) nc++ ; else n++ ;

Il y a imbrication car le deuxime if est l'instruction contenue dans le else du premier et le troisime if dans le else du second. Cette imbrication est visualis par une prsentation classique nomme indentation (retrait d'une tabulation droite). Quand on a un doute sur la structure, ne pas hsiter faites des blocs:
if ( ch[i] == 'a' ) na++ ; else { if ( ch[i] == 'b' ) nb++ ; else {

19

if ( ch[i] == 'c' ) nc++ ; else n++ ; } }

L encore, l'indentation est utilise pour visualiser les imbrications de structures. Evidemment, c'est plus long. Mais il faut penser que le plus important est la lisibilit et la concision et non la compacit. Une trop grande compacit du texte n'apporte rien, mme pas un gain de vitesse d'excution...

switch
L'instruction switch permet d'orienter l'excution dans differentes directions en fonction des valeurs d'une expression. switch ( expression ) { case valeur_1 : liste_instructions case valeur_2 : liste_instructions case valeur_3 : liste_instructions ... case valeur_N : liste_instructions default: liste_instructions } La liste d'instruction est facultative. Elle est normalement termine par l'instruction break pour terminer le swtich quand une valeur est traite. Le cas default est utilis lorsqu'aucune des valeurs ne correspond celle de l'expression.

L'exercice

L'instruction switch est idale pour tester un caractre et effectuer diffrentes actions selon les valeurs possibles:
switch ( ch[i] ) { case 'a': na++ ; break ; case 'b': nb++ ; break ; case 'c': nc++ ; break ; default : n++ ; }

L'oubli de l'instruction break change le comportement de cette structure. Par exemple si on les oublit tous, quand le caractre est un 'a' les trois compteurs sont incrments, quand c'est un 'b' les compteurs nb et nc sont incrments, et quand c'est un 'c', seulement nc est incrment.

while
L'instruction while est une instruction pour raliser une boucle. L'action est excute de manire rptitive tant que la condition est vraie. Cette condition est value AVANT l'excution de l'action. Ceci veut dire que si la condition est fausse ds le dpart, l'action ne sera jamais excute. while ( condition ) action La condition est habituellement une expression. L'action peut tre soit une simple instruction soit un bloc d'instructions dlimit par des accolades { }.

20

L'exercice
i = 0; while ( ch[i] != '\0' ) { /* ICI mettre le test du caractre ch[i] */ /* par exemple le switch vu plus haut */ i++ ; }

L'instruction while nous permet de dfinir la boucle pour parcourir les caractres de la chane.

Cette boucle est excute tant que le caractre '\0' n'est pas dtect. Dans le cas d'une chane vide, ce caractre est dtect immdiatement et la boucle termine.

do .. while
L'instruction do ... while est une instruction voisine de while dans laquelle l'valuation de la condition est ralise APRES l'action. Ceci veut dire que si la condition est fausse ds le dpart, l'action est quand mme excute une fois. Dans certains cas c'est ncessaire, dans d'autres ce comportement est nfaste. do action while ( condition ) La condition est habituellement une expression. L'action peut tre soit une simple instruction soit un bloc d'instructions dlimit par des accolades { }.

L'exercice
i = 0; do { /* ICI mettre le test du caractre ch[i] */ /* par exemple le switch vu plus haut */ i++; } while ( ch[i-1] != '\0' ) ;

Cette instruction nous permet de dfinir la boucle pour parcourir les caractres de la chane.

L'utilisation du do...while pose dans notre cas deux difficults: 1) On remarque qu'il est ncessaire de tester le caractre ch[i-1] car l'incrmentation est dj ralise au moment du test. 2) Dans le cas d'une chane vide, le caractre de fin de chane n'est dtect qu'aprs excution de l'intrieur de la boucle. Comme '\0' n'est ni 'a' ni 'b' ni 'c', le compteur n sera incrment et laissera penser que la chane n'tait pas vide... Donc dans cet exercice le while sera prfr au do...while.

for
L'instruction for est une instruction de type while qui apporte un grande facilit d'criture des boucles. Elle rsulte de la constatation que dans un grand nombre de boucles, on a en plus de l'action et la condition: - une action d'initialisation - une action excuter avant chaque rebouclage

21

for ( action_initiale ; condition ; action_avant_rebouclage ) action L'action initiale est expression. Habituellement c'est une affectation. L'action avant rebouclage est aussi une expression valuer. Cela peut tre une incrmentation. La condition et l'action ont un rle analogue celui du while. L'instruction for est quivalente un while: action_initiale ; while ( condition ) { action action_avant_rebouclage ; }

L'exercice
i = 0; while ( ch[i] != '\0' ) { /* ICI mettre le test du caractre ch[i] */ /* par exemple le switch vu plus haut */ i++ ; }

Dans la boucle ralise avec un while:

on distingue: - " i=0 " qui est une action d'initialisation - " i++ " qui est une action avant rebouclage - " ch[i]!='\0' " qui est la condition d'excution L'criture avec un for est donc:
for ( i=0 ; ch[i]!='\0' ; i++ ) { /* ICI mettre le test du caractre ch[i] */ /* par exemple le switch vu plus haut */ }

Ruptures de boucle
Une boucle se termine normalement quand sa condition devient fausse. Dans certains cas, il est cependant ncessaire de rompre la boucle en cours. Les instructions break et continue peuvent tre utilises avec while, do .. while et for.

Break

L'instruction break, utilise par ailleurs dans le switch, provoque une terminaison de la boucle englobante. BOUCLE avec BREAK: while ( ... ) { .... break; .... } EQUIVALENCE: while ( ... ) { .... goto etiqu; .... } etiqu:

22

Continue

L'instruction continue provoque l'abandon de l'action et force un rebouclage. BOUCLE avec CONTINUE: while ( ... ) { .... continue; .... } EQUIVALENCE: while ( ... ) { .... goto etiqu; .... etiqu: }

Programmez structur !
On voit donc que les instructions de contrle du langage C (goto exclu) permettent une criture structure et claire d'un programme, gnralement de bien meilleure qualit que celle obtenue avec l'usage des goto.

L'exercice
/* exo2.c : comptage des a,b et c dans une chane, version 2 */ #include <stdio.h> void main(void) { char ch[20]; int na,nb,nc,n; int i; na=nb=nc=n=0; scanf("%s",ch); for ( i=0 ; ch[i]!='\0' ; i++ ) { switch ( ch[i] ) { case 'a': na++ ; break ; case 'b': nb++ ; break ; case 'c': nc++ ; break ; default : n++ ; } } printf("%d %d %d %d \n",na,nb,nc,n); }

En runissant les bons morceaux, notre programme pourrait s'crire:

/* chaine de 19 caractres */ /* compteurs de caractres */ /* indice */

Comparez-donc ce programme exo2.c et le programme exo1.c crit avec des goto et tirez-en votre conclusion.

Fonctions
Une fonction est un ensemble ordonn dinstructions dfini par le programmeur. Tout programme possde une fonction principale nomme main. Toute fonction peut son tour appeler dautres fonctions qui sont dfinies soit dans le programme, soit dans une librairie de fonctions. Chaque fonction possde un nom et ralise habituellement un travail prcis. Pour cela, elle reoit comme donnes les valeurs de ses paramtres et renvoie habituellement un rsultat. Le nom dune fonction est souvent choisi en rapport avec travail ralis. Une fonction a deux aspects: sa dfinition et son utilisation.

23

Dans ce paragraphe sur les fonctions, nous continuons dvelopper l'exercice dfini dans le paragraphe prcdent qui consiste calculer le nombre de caractres a, b et c d'une chane.

Intrt des fonctions


La fonction est la base de la construction des gros programmes en permettant de les rendre "modulaires". Le principe de la modularit est de dfinir des objets simples qui sassemblent et sutilisent simplement... On retrouve la notion de modularit dans: - llectronique: circuits intgrs, ... - le btiment: parpaings, lments prfabriqus... - linformatique En informatique, un module cest un ensemble de fonctions qui sont regroupes car lies un thme commun (par exemple, parce quelles oprent sur un mme type dobjets). Chaque module est document de telle sorte que lon puisse utiliser facilement ses fonctions.

Le gain dune conception modulaire

Les avantages de la modularit en informatique sont trs importants et mme fondamentaux dans le sens que les normes programmes qui pilotent certaines applications nauraient jamais pu tre envisags sans mthode et en particulier sans une conception modulaire. A titre dexemple, les programmes qui grent la navette Herms reprsentent plusieurs millions de lignes de programme en langage volu et ncessitent plusieurs milliers de dhomme x anne (par exemple 300 personnes pendant 5 ans). Ces avantages sont: 1) Le travail en quipe Chaque quipe travaille sur un module. Evidemment, ceci nest possible quaprs une solide analyse de lapplication qui permet de dfinir les fonctionalits des modules et leur interfaces. 2) La rutilisabilit Si un module a t utilis dans un programme, il pourra ltre dans dautres. Lors de la conception initiale dun module, il est important de le concevoir de manire quil soit le plus rutilisable possible: suffisamment gnral, facilement utilisable, bien document... 3) La testabilit Un module est suffisamment indpendant pour tre test par ses concepteurs avant dtre incorpor dans un grand programme. La certification de ce dernier sera ainsi facilite. 4) La maintenance Quand un grand programme prsente un mauvais fonctionnement, ou bien quand il doit tre rajeuni pour le rendre plus rapide ou plus sr, ou parce que lapplication a change, son organisation modulaire permettra de ne changer que les modules concerns. 5) La rduction de la complexit Les utilisateurs dun module ne connaissent que son interface. La manire dont il est ralis est prive et ne concerne que lquipe qui la ralis. Ainsi, chaque quipe ne voit que la complexit de son module et pas celle des autres.

Mise en uvre de la modularit

De manire pratique, un module peut tre un simple fichier qui regoupe un ensemble de fonctions. Pour raliser une application faite de plusieurs modules, on peut utiliser plusieurs techniques. Ces techniques sont dcrites dans le paragraphe sur la compilation spare.

24

Modle dune fonction

Une fonction doit tre considre comme un composant disposant dentres (les paramtres) et dune sortie (le rsultat). - Les entres possdent un nom et un type comme des variables. Elles sont dsignes sous le nom de paramtres formels. Ces paramtres formels reoivent des valeurs appeles paramtres effectifs. - Le rsultat possde un nom (celui de la fonction) et un type. - Le corps de la fonction est exprim en fonction des paramtres formels ce qui lui permet de pouvoir tre excut pour toute combinaison de paramtres effectifs pourvu que les types soient respects. Dans lexemple ci-dessous la fonction delta qui calcule le discriminant du trinme ax2+bx+c est reprsente schmatiquement comme un composant lectronique.

0.5

paramtres effectifs

float a

float b

float c

paramtres formels

delta = b*b - 4*a*c float delta nom de la fonction rsultat

-2

Dfinition et utilisation d'une fonction


La dfinition dune fonction indique: - son nom, - le nom et le type de ses paramtres, - le type de son rsultat, - son corps. Exemple: La fonction delta dcrite schmatiquement ci-dessus, scrit en C ainsi :
float delta ( float a , float b , float c ) { return b*b - 4*a*c ; }

La premire ligne dcrit les entres/sorties de la fonction: nom et types des paramtres, nom et type du rsultat. Cette ligne dclarative est nomme prototype de la fonction. Le corps de la fonction, compris entre des accolades { }, indique les actions qu'elle doit excuter. Les valeurs des paramtres sont utilises et le rsulat de la fonction est renvoy par l'instruction return. Utiliser une fonction consiste l'appeler en lui passant des paramtres et en recevant son rsultat. La dfinition d'une fonction est normalement place AVANT son utilisation. Exemple: Soit le trinme x2 + 2kx - 5 o k est une variable. Le test du discriminant peut tre ralis ainsi:
if ( delta (1,2*k,-5) ) >= 0 { .... } else { ....}

On notera que les paramtres effectifs passs lappel peuvent tre des expressions (valeurs de variables ou de constantes ou combinaison).

25

L'exercice

Il est possible dans notre exercice de dfinir une fonction qui calcule le nombre de fois qu'un caractre donn apparait dans une chane de caractres:
int compte( char s[], char c) { int i,n; n=0; for ( i=0 ; s[i]!='\0' ; i++ ) { if ( s[i] == c ) n++ ; } return n ; */ } /* indice et compteur */ /* compteur nul au dbut */ /* boucle */ /* on compte ... */ /* retour de n comme rsultat

Modle: Cette fonction accepte en entre une chane de caractres s et un caractre c. Le rsultat est un entier.

"hello"

'l'

char[] s

char c

int compte

2
Corps: Dans le corps, on utilise la fois les paramtres s et c mais aussi des variables locales i et n. Finalement, la valeur de n est renvoye comme rsultat. Pour utiliser cette fonction, il suffit par exemple d'crire:
na = compte(ch,'a') ; nb = compte(ch,'b') ; na = compte(ch,'c') ;

Dans ces trois appels, on passe la mme chane ch en premier paramtre, mais en second paramtre c'est successivement 'a', puis 'b' et enfin 'c'. Il y a trois excutions de la mme fonction sur des donnes diffrentes. De la mme manire, on peut crire une fonction qui calcule la longeur d'une chane:
int mesure( char s[] ) { int i,n; n=0; for ( i=0 ; s[i]!='\0' ; i++ ) return n ; }

n++ ;

Finalement, le programme pourrait avoir l'allure suivante:


/* exo3.c : comptage des a,b et c dans une chane, version 3 */ #include <stdio.h> int compte( char s[], char c) { int i,n; n=0; for ( i=0 ; s[i]!='\0' ; i++ ) { if ( s[i] == c ) n++ ; } return n ; } int mesure( char s[] ) { int i,n; n=0; for ( i=0 ; s[i]!='\0' ; i++ )

n++ ;

26

return n ; } void main(void) { char ch[20]; int na,nb,nc,n; scanf("%s",ch); na = compte(ch,'a') nb = compte(ch,'b') na = compte(ch,'c') n = mesure(ch) - na } ; ; ; - nb - nc ;

printf("%d %d %d %d \n",na,nb,nc,n);

Evidement, vous constatez que ce programme exo3.c est plus long que exo2.c. Mais d'un autre ct, il est sous une forme qui le prpare la modularit. Attendez donc la suite pour juger...

Paramtres
Comme il a t vu plus haut, les paramtres sont les entres-sorties dune fonction. Les paramtres sont employs en deux occasions: - lors de la dfinition de la fonction: On donne alors leur nom et leurs type. Ils servent pour crire les instructions du corps de fonction. On parle alors de paramtre formel car, ce stade, ils ne sont pas porteurs dune valeur particulire. - lors de lappel de la fonction: Une valeur est affecte ce moment chaque paramtre formel. Cette valeur est nomme paramtre effectif. Les paramtres peuvent tre manipuls dans une fonction comme des variables locales.

Variables locales et globales


Une variable locale est dclare l'intrieur d'une fonction, une variable globale l'extrieur de toute fonction, y compris la fonction main(). Une variable globale est utilisable par plusieurs fonctions, tandis qu'une variable locale n'est utilisable que par la fonction dans laquelle elle est dclare. Ainsi, l'intrieur d'une fonction, ne sont visibles que les variables globales et les variables locales de cette fonction (ainsi que ses paramtres que l'on peut considrer comme des variables locales). Exemple:
/* var.c : variables locales et globales int x; int f(void) { int a,b; .... a = a*x + b ; .... } void main(void) { int a,n; .... a = x * n ; .... } /* /* x GLOBALE */

/* a,b LOCALES */ /* utilisation de a,b et x */

/* a,n LOCALES */ /* utilisation de a,n et x */

Ici x peut tre utilise par f et main car elle est globale.

27

Les variables locales des fonctions f et main sont compltement indpendantes. Par exemple la variable a de la fonction f() et la variable a de la fonction main(), bien qu'ayant le mme nom ne dsignent pas le mme objet.

Porte de la dclaration d'une variable

La visibilit d'une variable globale s'tend de sa dclaration la fin du fichier courant. La visibilit d'une variable locale s'tend de sa dclaration la fin de la fonction. Pour cette raison, sauf pour une raison particulire, on dclare habituellement les variables globales avant toute fonction dans le fichier courant et on dclare les variables locales en dbut du corps d'une fonction.

Surcharge d'une variable globale par une variable locale

Si dans une fonction, une variable locale porte le mme nom qu'une variable globale alors le nom de cette variable dsigne dans cette fonction la variable locale. La variable globale est occulte: elle n'est plus accessible.

Dure de vie des variables

Les variables globales ont une existence permanente: elles persistent tout au long du programme. Les variables locales ont une existence phmre: elle ne durent que le temps dexcution de la fonction qui les contient.

Notion de prototype
Un prototype dcrit les entres-sorties dune fonction. Par exemple pour la fonction delta ci-dessus le prototype est:
float delta ( float a , float b , float c )

Un prototype possde deux utilisations: a) elle constitue lentte dans la dfinition dune fonction; b) elle constitue une dclaration pour une fonction dfinie dans un autre fichier et compile sparment (cf le paragraphe sur la compilation spare). Dans les deux cas le prototype permet au compilateur de vrifier que la fonction est utilise correctement. Note: La notion de prototype est la diffrence majeure entre le C K&R et le C ANSI. Elle a t introduite pour remdier la laxit bien connue de C. Les noms des paramtres peuvent tre omis dans le cas b) car comme le corps nest pas prsent ils ne servent rien. Ils peuvent cependant rester dans le prototype pour faciliter la lecture du programme.
float delta ( float , float , float )

L'exercice

Supposons que les fonctions compte et mesure ont t compiles sparment, le programme se contente alors de dclarer leurs prototypes avant leur utilisation.
/* exo4.c : comptage des a,b et c dans une chane, version 4 */ #include <stdio.h> int compte( char s[], char c ); /* prototype de compte */ int mesure( char s[]); /* prototype de mesure */ void main(void) {

28

char ch[20]; int na,nb,nc,n; scanf("%s",ch); na = compte(ch,'a') nb = compte(ch,'b') na = compte(ch,'c') n = mesure(ch) - na } ; ; ; - nb - nc ;

printf("%d %d %d %d \n",na,nb,nc,n);

Le type void
s Fonctions qui ne renvoient pas de rsultat
Les fonctions qui ne renvoient pas de rsultat doivent avoir le type void (vide) et n'utilisent pas l'instruction return. Il y a deux situations o l'on programme des fonctions qui ne renvoient pas de rsultat: 1) le rle de la fonction n'est pas de calculer un rsultat mais d'avoir une action sur l'environnement, par exemple, envoyer des informations, positionner un actionneur... 2) la fonction calcule un rsultat qu'elle dpose directement dans un objet global ou local dont elle connat l'adresse. Ces fonctions ne peuvent pas tre utilise dans une expression (puisqu'elles n'ont pas de valeur): elle sont appelles directement dans une instruction comme un ordre. Exemple: Supposons qu'un module robot dfinit des fonctions de pilotage de robot. Le programme suivant les utilise.
/* void void void void robot.c : pilotage d'un robot */ /* /* /* /* mise en activit du robot */ dsactivation du robot */ marche n mtres */ tourne de a degrs (trigo) */ robot_start(void); robot_stop(void); robot_walk(float n); robot_turn(int a);

void main(void) { /* un parcours carr */ robot_start(); for (i=0;i<4;i++) { robot_walk(1.0); robot_turn(90); } robot_stop(); }

Note: Dans le prototype des fonctions de contrle du robot, on a ajout des noms fictifs (a et n) aux paramtres. Ceci est autoris et permet un commentaire prcis.

Fonctions sans paramtres

Quand une fonction n'a pas de paramtre le type void doit tre indiqu leur place. Il y a deux situations o l'on programme des fonctions sans paramtres: 1) la fonction ralise une action qui n'a pas besoin d'tre prcise par un paramtre (comme robot_int() de l'exemple prcdent). 2) la fonction prend des informations dans des objets globaux. Exemple: La fonction ci-dessous opre sur des objets globaux. Elle ralise la somme de deux entiers, avec la particularit que les donnes et le rsultat sont lus et crits dans des objets globaux.
/* inutile.c : fonction sans intert */ #include <stdio.h>

29

int a,b,c; void add(void) { c=a+b; } void main(void) { a=1; b=2; add(); printf("%d \n",c); }

/* affiche 3 */

Ce type de fonction va contre sens du rle d'une fonction qui doit pouvoir servir dans diverses situation. En effet, cette fonction add ne saura jamais qu'additionner a et b pour mettre le rsultat en c. Elle ne peut pas tre utilise pour incrmenter a par exemple.

Pointeurs sur fonction


Le nom d'une fonction, employ sans parenthses, est un pointeur sur cette fonction: sa valeur est l'adresse d'implantation de cette fonction en mmoire. Note: En C, pour prendre ladresse dune fonction loprateur & peut optionnellement tre plac devant le nom de la fonction. En C++, la prsence explicite du & est souhaite. Quel intrt ? - Le premier est de pouvoir passer en paramtre dune fonction la rfrence une fonction. Par exemple pour initialiser un vecteur d'interruption, on passe la fonction d'initialisation le nom de la fonction invoquer lors de l'interruption ou un pointeur sur cette fonction. - Le second est de pouvoir constituer des tableaux de pointeurs vers des fonctions. Ceci est notamment utile pour raliser des automates.

Dclaration

La dclaration d'un pointeur sur fonction est un peu barbare mais elle est en fait simplement calque sur le prototype de la fonction sur laquelle on doit pointer: Si l'on doit pointer sur une fonction F de type: un pointeur P vers F doit tre dclar: Exemples de dclarations: FONCTION int f1 (int) { ... }; float f2 ( int, int ) { .. }; POINTEUR vers FONCTION int (*p1) (int); float (*p2) ( int, int ); rsultat F ( paramtres ) rsultat (*P) ( paramtres )

Affectation et appel

Pour appeler une fonction via un pointeur, il faut videmment que le pointeur possde comme valeur l'adresse d'une fonction. Ensuite, il suffit d'appliquer l'oprateur d'excution () la fonction pointe. Exemples d'affectation et d'appel (suite de l'exemple prcdent): AFFECTATION p1 = &f1; p2 = &f2; Exemple:
/* pf.c : pointeurs sur fonctions */ void f(void) { ... }

APPEL DIRECT x = f1 (10); y = f2 (1,2);

APPEL INDIRECT x = (*p1) (10); y = (*p2) (1,2);

30

void main(void) { void (*pf)(void); f */ pf = &f; f(); (*pf)(); */ }

/* pf est un pointeur sur une fonction comme /* pf reoit l'adresse de f */ /* exec de f par appel direct */ /* exec de f par appel indirect via pf

Tableaux
Notion de tableau
Un tableau est une collection d'objets de mme type rangs successivement dans la mmoire. Chacun de ces objets est nomm lment et possde un indice. Le premier lment du tableau possde l'indice 0. Les lments dun tableau sont des objets de type simple ou structur, par exemple: - des tableaux dont les lments sont des structures, - des tableaux dont les lments sont aussi des tableaux. Dans ce dernier cas, on parle souvent de tableaux multi-dimentionnels. Le premier indice est l'indice de ligne, le second celui de colonne.

Dclaration d'un tableau


La dclaration d'un tableau indique son nom, le type et le nombre de ses lments. Exemples de dclarations: int t[10]; float v[3]; float m[3][4]; struct couple f[100]; tableau t de 10 entiers tableau v de 3 flottants (vecteur) tableau m de 3 lignes x 4 colonnes de flottants (matrice) tableau f de 100 lments de type struct couple

0 0 1 2 0 1

m
La dclaration peut tre accompage d'une valeur d'initialisation. Exemples:
int v[3] = { -1, 0, +1 }; int m[3][3] = { {1,1,1,1},{2,2,2,2},{3,3,3,3} };

Les lments indiqus sont dans l'ordre o ils sont rangs en mmoire. Pour les tableaux bidimentionnels, le rangement est ligne par ligne. Donc dans le cas de m, la ligne 1 vaut 1, la ligne 2 vaut 2 et la ligne 3 vaut 3. On peut soigner la prsentation en crivant:
int m[3][3] = { {1,1,1,1},

31

{2,2,2,2}, {3,3,3,3} };

Note: Les dclarations initialisantes ne sont possibles pour les tableaux que pour des variables globales (ou pour des locales dclares static). Ceci est aussi vrai pour les structures.

Accs aux lments


L'oprateur [] qui sert lors de la dclaration pour indiquer le nombre dlments sert aussi pour accder un lment: on indique entre les crochets lindice de llment souhait. Exemples: v[0] est l'lment 0 de v m[1][3] est l'lment m(1,3) c'est dire l'lment ligne 1 colonne 3. Il est frquent de devoir parcourir un tableau, c'est dire oprer un certain traitement sur chacun des lments. Pour un tableau une seule dimension, on parcourt habituellement les lments partir de l'indice 0. Mais le parcours peut aussi tre fait partir de l'indice le plus lv. L'instruction for est approprie au parcours des tableaux. Exemple:
void main(void) { float v[3]; float m[3][3]; /* 1) parcours de v, indices de 0 2 */ for (i=0;i<3;i++) v[i]=0; /* 2) parcours de v, indices de 2 0 */ for (i=2;i>=0;i--) v[i]=0; /* 3) parcours de m, indices de 0 2, colonnes par colonnes */ for (j=0;j<3;i++) for (i=0;i<3;i++) m[i,j]=0; /* 4) parcours de m, indices de 0 2, lignes par lignes */ for (i=0;i<3;i++) for (j=0;j<3;j++) m[i,j]=0; }

0 0 0 1 2 0 1 2 1 2

2 0 1 2

Oprations sur les tableaux


Le langage C ne comporte pas d'opration spcifique pour manipuler les tableaux. L'affectation elle-mme ne peut tre utilise pour copier un tableau. Si un tableau doit tre copi, il faut le faire lment lment. Note: Cela semble trange car on a vu que l'on pouvait initialiser un tableau avec des valeurs lors de la dclaration. En fait, dans ce dernier cas, il ne s'agit pas d'une vritable affectation mais de l'installation de donnes la compilation dans la section de donnes.

32

Tableaux et pointeurs
Le nom d'un tableau est un pointeur vers ce tableau. Si t est le nom d'un tableau, t pointe vers ce tableau, c'est dire a pour valeur ladresse de t[0]. Ceci est fait pour rendre systmatique le passage de l'adresse des tableaux dans les appels de fonction. Exemple:
float somme(float t[], int n) { int i; float s; for (i=0;i<n;i++) s = s + t[i]; return s; } void main(void) { float tab[5] = {1.0, 2.0, 3.0, 4.0, 5.0}; float total; .... total = somme(tab,5); ... }

Dans l'appel "somme(tab)" la valeur de tab passe en paramtre est l'adresse du tableau et non le tableau lui-mme. On vite ainsi de copier 40 octets (5x4 octets) en passant une adresse de 4 octets.

Addition de pointeurs

Si un pointeur p pointe sur un objet donn alors p+1 est un pointeur sur un objet de mme type situ la suite du premier. De mme sont dfinis p+2, p+3, etc... Si t est le nom d'un tableau, on peut le considrer aussi comme pointeur et lon a: t == &t[0] t+1 == &t[1] t+2 == &t[2] .... t+i == &t[i] Exemple: Avec le tableau tab de lexemple prcdent. et, en appliquant * aux deux membres *t == t[0] *t == t[1] *t == t[2] *t == t[i]

tab[0] tab[1] tab[2]

tab[3] tab[4]

tab+1

tab+2

tab+3

tab+4

tab s
Soustraction de pointeurs

Si p1 et p2 sont deux pointeurs sur un mme type d'objet, alors p2-p1 = i signifie p2 = p1+i au sens dfini ci-dessus. Exemple:

33

Avec le mme tableau tab, on a par exemple: (tab+4) - (tab+1) == 3. Ce 3 est la diffrence dindice entre les lments points.

Chanes de caractres
Notion de chane de caractres
Une chane de caractre est une suite de caractres. Une chane de caractres est range dans un tableau de caractres. Elle est termine par une caractre spcial pour la dlimiter: le caractre not '\0' dont le code est zro.

Dclaration d'une chane


Une chane est dclare comme un tableau de caractres. Note: Un tableau de caractres dclar de taille N, peut recevoir au plus N-1 caractres compte tenu du caractre terminal '\0' ncessaire. Exemple:
char chaine[40]; /* peut recevoir 39 caractres */

Dans une dclaration initialisante, la taille du tableau n'est pas ncessaire. Elle est estime automatiquement par le compilateur. Exemple:
char ch1[] = "hello"; char ch2[40] = "hello";

La chane ch1 aura pour taille 5+1=6.

Accs un caractre
Comme dans tout tableau, il est possible d'accder aux lments avec l'oprateur []. Si C est une chane de caractres, C[0] dsigne le premier caractre, C[1] le second, etc ... Exemple:
/* inverse.c : inversion d'une chane de caractres */ #include <stdio.h> void main(void) { char ch[40]; char c; int i,n; scanf("%s", ch); /* On determine la taille de la chaine */ for (n=0; ch[n]!='\0'; n++) { } /* On parcours la chaine en permutant ch[i] et ch[n-i] */ /* tant que i reste infrieur n-i sinon on permute 2 fois! */ for (i=0; i<n-i ; i++) { c = ch[i]; ch[i] = ch[n-i]; ch[n-i] = c; } printf("%s \n", ch); }

34

Oprations sur les chanes


Comme pour les tableaux, il n'y a dans le langage C aucune opration spcifique aux chanes de caractres. Cependant les librairies offrent un grand nombre de possibilits. Pour les E/S, stdio permet de lire et crire des chanes avec: - printf et scanf (format %s) - puts et gets Pour les autres oprations , le module string offre les possibilits suivantes: - longeur: strlen, - copie: strcpy, - comparaison: strcmp, - concatnation: strcat etc... Exemple:
/* string.c : utilisation du module string */ #include <stdio.h> #include <string.h> int L; void main(void) { char ch1[40], ch2[40]; scanf("%s", ch1); L = strlen(ch1); strcpy(ch2,ch1); printf("%s - %d \n", ch2,L); } /* saisie de ch1 */ /* longeur de ch1 */ /* copie de ch1 dans ch2 */ /* affichage de ch2 et L */

Rangement d'une chane en mmoire


Dans une chane de caractre chaque caractre occupe un octet. Il est reprsent par son code ASCII (voir annexe). Exemple: La chane dclare:
char ch[10] = "hello";

est range en mmoire ainsi:

h e l l o \0
68 65 6C 6C 6F 00 xx xx xx xx

ch

35

Structures
Notion de structure
Une structure regroupe une collection d'objets de types diffrents nomms membres. Les membres d'une structure sont rangs conscutivement dans la mmoire dans l'ordre de leur dclaration. Alors que pour les tableaux, chaque lment est distingu par son indice, dans une structure un membre est distingu par un nom. Chaque membre d'une structure peut tre soit d'un type simple, soit un tableau, soit encore une structure.

Dclaration d'une structure


La dclaration d'une structure indique son nom, ainsi que le nom et le type de chaque membre. Le motcl struct introduit la dclaration. Exemples de dclarations:
struct { float a; int b; char c; } x, y;

Les objets x et y sont deux structures comportant trois membres, un flottant a, un entier b et un caractre c. Comme la dclaration d'une structure est souvent longue, il est possible de crer un modle de structure. Ce modle fait l'objet d'une dclaration et sert ensuite pour celle des variables. Exemple:
struct individu { char nom[40]; char prenom[40]; int age; float taille; } ;

Dans la dclaration ci-dessus on dfinit un modle de structure nomm individu. La prsentation est are pour une meilleure lisibilit. Cette structure possde quatre membres: nom, prenom, age et taille. Ce modle permet ensuite de dclarer des structures:
struct individu x,y,z;

Il est possible d'initialiser une structure au moment de sa dclaration. Exemple:


struct individu x = { "Dupond", "Robert", 35, 1.85 }; struct individu y = { "Durant", "Bernard", 34, 2.0 }; struct individu z = { "Dupond", "Elodie", 31, 1.70 };

Note: Les dclarations initialisantes ne sont possibles pour les structures que pour des variables globales (ou pour des locales dclares static).

Accs un membre
L'oprateur . permet de dsigner un membre d'une structure. Si la structure est pointe par un pointeur, une notation commode -> permet d'accder aux membres.

L'oprateur .

36

Exemple: "x.nom" dsigne le membre nom de x "y.age" dsigne le membre age de y


/* affichage de l'identit de x, y et z */ printf("%s %s - %d - %f \n", x.nom, x.prenom, x.age, x.taille); printf("%s %s - %d - %f \n", y.nom, y.prenom, y.age, y.taille); printf("%s %s - %d - %f \n", z.nom, z.prenom, z.age, z.taille);

L'oprateur ->

Cette notation est commode pour accder aux champs d'une structure quand celle-ci est pointe par un pointeur. Si p pointe vers une structure dont les champs sont u,v,w, ... le champ v peut tre dsign par: (*p).v ou encore par: p->v Exemple:
void main(void) { struct individu i,*p; p = &i; i.age = 30 ; (*p).age = 30 ; p->age = 30 ; } /* individu et pointeur sur individu */ /* affectation p avec l'adr. de i */ /* /* /* /* 3) via trois manires de faire la mme choses */ 1) directement */ 2) via p, notation pointe */ p, notation -> */

Oprations sur les structures


Le langage C comporte un seule opration spcifique aux structures: l'affectation. Celle-ci est possible seulement entre deux structures de mme nature. Le contenu d'une structure est copi octet par octet quel que soit le type des membres. Exemple:
/* Copie de y dans x */ x = y;

Autres possibilits
Ce paragraphe apporte de nouvelles possibilits pour structurer des donnes: lunion, le champ de bits et lnumration et prsente loprateur typedef pour crer de nouveaux noms de type. Note: Autant l'usage des unions et des champs de bits est marginale (voire dconseill pour les dbutants), autant l'usage des numrations et la cration de nouveaux types par typedef sont encourags car ils permettent une criture plus lisible des programmes.

Champ de bits
Un champ de bit est une forme particulire de structure dans laquelle on prcise le nombre de bits allous chaque membre. L'utilisation la plus intressante est d'conomiser la place dans la mmoire. Exemple:

37

On veut simplement stocker dans une grande matrice 100x100 des triplets d'entiers signs compris entre -10 et +10. Pour cela 5 bits suffisent pour chaque entier. La matrice pourra tre dfinie ainsi:
struct triplet { int x : 5; int y : 5; int z : 5; } struct triplet M[100][100] ;

Une autre utilisation possible est pour permettre un accs aux bits dun port reli un priphrique.
struct port { unsigned unsigned unsigned unsigned unsigned } ; int int int int int status : 1 ; ready : 1 ; data : 4 ; unused1 : 1 ; unused2 : 1 ;

void main(void) { struct port *pa = (struct port *) 0x1000 ; ... if ( pa->status == 1 ) { pa->data = 0xF; pa->ready = 1 ; } ... }

/* port en 0x1000 */

Cette faon de faire prsente deux gros inconvnients: 1) La norme NE DIT PAS dans quel ordre est ralise laffectation des bits: du MSB vers le LSB ou linverse. 2) Quand on opre sur un membre dun champ de bit, on peut penser que lon ne manipule QUE ce membre. En fait, au niveau machine, on ne peut pas faire moins que de lire ou dcrire entirement un octet cest dire le port entier. Or les ports sont souvent sensibles la lecture et/ou lcriture, par exemple, la lecture dun port configur en lecture synchrone (latch) provoque souvent un reset du bit dtat correspondant. Il en rsulte donc parfois des effets non dsirs.

Union
Une union est une variante de structure dans laquelle les membres sont superposs dans la mmoire. Exemple:
union etrange { int i ; float f ; }; union etrange u ;

Les membres u.i et u.f ne peuvent tre utiliss simultanments. Si on affecte u.i cela dtruit u.f et rciproquement Il n'y a pas de grande utilit des unions pour un programmeur modeste. Les utilisations les plus frquentes sont pour: - raliser des variantes dans des structures, - faire des conversions de type tranges, - conomiser la mmoire de manire froce...

38

Enumrations
Quand une variable prend un nombre limit de valeurs symboliques, il est souvent lgant de dfinir un type numr pour la modliser. Une variable dclare d'un type numr ne peut tre affecte que par les valeurs symboliques prvues dans l'numration. Le compilateur y veillera! Note: par dfaut, le compilateur affecte en fait aux valeurs symboliques des valeurs entires conscutives partir de 0. Le programmeur peut ventuellement prciser les valeurs quils souhaite affecter aux symboles. Exemples: Dans ce programme le type couleur est dfini par numration. Puis un tableau de couleur est dfini et initialis. Le traitement consiste trouver le nombre de chacune des trois couleurs dans le tableau. Pour cela, on utilise un tableau de trois compteurs dont les couleurs sont les indices.
/* enum.c : type enumr */ #include <stdio.h> enum couleur { rouge , vert , bleu } ; void main(void) { enum couleur t[6] = { vert, vert, rouge, vert, bleu, rouge } ; int nb[3] = { 0, 0, 0 }; enum couleur c ; int i ; for (i=0; i<6; i++ ) { c = t[i] ; switch (c) { case rouge : nb[rouge]++; break; case vert : nb[vert]++; break; case bleu : nb[bleu]++; break; } } printf("%d %d %d \n",nb[rouge],nb[vert],nb[bleu]); }

Note 1: le compilateur affecte considre que: rouge=0, vert=1, bleu=2. Mais, il tait possible de choisir les valeurs symboliques:
enum couleur { rouge=4 , vert=7 , bleu=13 } ;

Note 2: Le traitement ci-dessus peut tre crit de manire encore plus simple:
for (i=0; i<6; i++ ) { c = t[i] ; nb[c]++ ; }

typedef
Typedef est une commodit du langage pour dfinir des synonymes des noms de types. On cr un synonyme dun nom de type: - quand on prfre un nom vocateur de manire augmenter la lisibilit du programme, - quand le nom du type est long. Pour crer un nom de type avec typedef, il suffit de procder comme pour dclarer une variable du type que lon souhaite et de faire prcder le tout de typedef. Il est courant de mettre une majuscule un nom de type cr par typedef.

39

Exemples:
typedef typedef typedef typedef int Nombre ; struct individu Individu ; float Vecteur[3] ; float Matrice[3][3] ;

Le premier typedef manque dintrt, mais les suivants sont plus intressants. Dans le second exemple, Individu est synonyme de struct individu. Ainsi, les dclarations dobjet sont plus claires.
AU LIEU DE: int a,b ; struct individu x,y,z; float u[3],v[3] ; float m[3][3] ; ON ECRIT: Nombre a,b ; Individu x,y,z ; Vecteur u,v ; Matrice m ;

Lusage de mettre une majuscule aux noms de types crs par typedef prsente deux avantages: - dans le cas des structures, on lve lambigut du nom de la structure et du nom souhait (cf Individu), - idem pour les union et les numrations, - on distingue plus clairement les noms des types.

Objets complexes
Les objets complexes, comme tous les objets, sont toujours dfinis partir dun type qui sert de modle. Autrement dit, construire des objets revient construire des types. Pour dfinir des types de donnes complexes, on respecte souvent DEUX rgles: - utiliser intensivement le typedef, - dfinir des types complexes partir de types plus simples. Exemple 1:
typedef struct individu Individu ; typedef Individu *pIndividu ; typedef pIndividu Repertoire[1] ; Repertoire r ; /* Individu dfini plus haut */ /* pIndividu --> Individu */ /* tableau de 100 pIndividu */

Lobjet r, dclar Repertoire, est un tableau de pointeurs vers 100 Individus. Si r devait tre dclar dun coup sans typedef ni type intermdiaire, on aurait crit:
struct individu *r[100] ;

Cest plus court mais moins lisible, et pas du tout plus efficace... Exemple 2: On a souvent besoin de tableaux de pointeurs sur fonction quand on programme des automates. Supposons que ces fonctions sont des actions et quelles sont sans paramtre et ne retournent pas de rsultat.
typedef void Action(void) ; Action */ typedef Action *pAction ; Action */ /* fonction de type /* pointeur sur

enum etat { initial, marche, arret, panne, nEtats } ; typedef enum etat Etat ; /* Etat du systme */ enum evenement { nuit, jour, poussoir, porte, fenetre, nEvenements } ; typedef enum evenement Evenement ; void rien(void) {}; pAction a[nEtats][nEvenements] = daction */ /* Action nulle */ /* matrice

40

{ { { { { } void main(void) { ... (*a[marche,porte])() ; porte */ ... } rien,rien,rien,rien,rien rien,rien,rien,rien,rien rien,rien,rien,rien,rien rien,rien,rien,rien,rien }, }, }, } /* /* /* /* etat etat etat etat initial */ marche */ arret */ panne */

/* action correspondant lev. /* dans ltat marche ... */

Note: Les nombres dtats et dvnements (nEtats et nEvenements) sont dclares avec une petite astuce commode qui consiste dclarer ces constantes comme membre terminal dune numration.

E/S console
Les entres/sorties console permettent les changes entre le programme et lutilisateur qui travaille sur une console alpha-numrique. Ces e/s peuvent tre ralises de diffrentes manires: - les e/s formattes qui offrent de nombreuses possibilits, - les e/s caractres, - les e/s lignes (chanes de caractres). Lusage des e/s console ncessite linclusion de <stdio.h>. Pour plus de dtail sur les fonctions de/s voir leurs descriptions dans la librairie ANSI.

E/S formattes: gnralits


Les e/s formattes permettent de saisir et dafficher tous les types de base ainsi que les chanes de caractres. Pour chaque e/s, une chane format est donne qui indique lordre et la prsentation des informations saisies ou affiches. Les e/s formattes sont ralises avec les fonctions de bibliothque printf et scanf. Exemple introductif:
#include <stdio.h> void main(void) { int a; float b; scanf (%d %f,&a,&b); printf (%d %f,a,b); }

/* saisie de a et b */ /* affichage de a et b */

Formats

La plupart des formats ont la mme signification pour scanf et printf. On signalera les particularits de scanf et de printf dans les paragraphes qui leur sont consacrs.

41

FORMAT %d %o %x %ld %lo %lx %f %e %lf %le %c %s

TYPE int int int long long long float float double double char char *

FORME en dcimal en octal en hexadcimal en dcimal en octal en hexadcimal notation virgule fixe (ex: -12.345) notation virgule flottante (ex: -1.234e01) idem %f idem %e un caractre plusieurs caractres

E/S formattes: printf


printf Usage: printf ( chane_guide , liste_de_valeurs ); La chane guide indique la forme de la sortie. Elle contient des indications de formattage introduits par le caractre %. Fonctionnement: Les caractres de la chane guide sont recopis un un sur la sortie. Chaque fois quun format est rencontr, une valeur est formatte et place en sortie. Exemple: Linstruction
printf ( "La somme de %d et %d est %d \n" , a , b , a+b ) ;

criture formatte

produit:
a b a+b

La somme de 12 et 23 est 35

Affichage des entiers

Les formats de base sont %d (dcimal), %o (octal), %x (hexadcimal). Il est possible de fixer la taille de la fentre daffichage en indiquant le nombre de caractres aprs le %. Si la valeur noccupe pas toute la fentre, laffichage est cadr droite et complt par des espaces gauche. Si la valeur ne tient pas dans la fentre, celle-ci est automatiquement ajuste pour que le nombre ne soit pas tronqu. Un nombre de caractres ngatif provoque un cadrage gauche dans le fentre. Exemples:
printf(%-6d,m); printf(%4x,n); m=100 n=30 -> -> sortie = 100^^^ sortie = ^^1e

Pour les entiers longs, un l ou L doit tre plac en prfixe au format: %ld,%lo,%lx.

Affichage des flottants

Les formats de base sont %f (virgule fixe) et %e (notation exponentielle). Comme pour les entiers, il est possible de fixer la taille de la fentre. On peut prciser en outre le nombre de dcimales (par dfaut 6).

42

Exemples:
printf(%6.2f,pi); printf(%12.4e,m); ^^1.0000e+03 pi=3.14159 m=1000 -> -> sortie = ^^3.14 sortie =

Pour les double, un l ou L doit tre plac en prfixe au format: %lf,%le.

Affichage des caractres et des chanes

Le format est %c pour les caractres et %s pour les chanes. Dans le cas des chanes, une taille de fentre peut tre indique. Exemple:
printf(%c,c); printf(%s,s); printf(%4s,s); c=A s=hello s=hello -> -> -> sortie = A sortie = hello sortie = hell

La valeur de retour de printf

Elle indique le nombre de caractres effectivement affichs. Le plus souvent cette valeur de retour de printf nest pas utilise. Cependant elle peut tre utile pour dtecter un dpassement de format daffichage. Exemple:
n = printf(%10d %12.4f\n,a,x); if (n!=25) { /* pb de format */ }

E/S formattes: scanf


scanf Usage: scanf ( chane_guide , liste_dadresses_de_variables ) ; La chane guide indique la forme de lentre, cest dire de ce qui doit tre tap au clavier. Elle contient des formats introduits par %. Fonctionnement: La rgle gnrale est que ce qui est tap au clavier doit respecter la chane guide: - si le guide indique un caractre, ce caractre doit tre tap, - si le guide indique un format, une valeur doit tre tape en respectant ce format. Cette valeur est ensuite place ladresse indique. Dans un usage courant, une chane guide ne contient que des formats. La prsence de caractres est cependant possible. Lespace joue un rle particulier dans une chane guide. Les caractres spciaux comme les tabulations, les fins de lignes sont dconseills. Exemple1: Saisie de deux entiers (prsentation libre)
scanf(%d%d, &a, &b) ; entre = 12^^3 -> a=12 ; b=3

lecture formate

On spare habituellement les valeurs entres par un ou plusieurs espaces. Si les valeurs 12 et 3 taient attaches scanf aurait pris 123 pour a et scanf continuerait attendre la valeur de b ... On aurait obtenu le mme rsultat en sparant les valeurs dune tabulation ou dune fin de ligne. Exemple 2: Saisie de deux entiers (prsentation sous forme de couple)
scanf ((%d,%d),&a,&b); entre = (12,3) -> a=12 ; b=3

Ici le guide oblige taper des caractres en plus des valeurs. Ces caractres ne servent pas grandchose mais amliorent la prsentation des donnes lcran.

43

Saisie des entiers

Les formats de base sont %d (dcimal), %o (octal), %x (hexadcimal). Exemple:


scanf(%d%x%o,&a,&b,&c); a=b=c=10 entre = 10^a^^12 ->

Pour les entiers longs, un l ou L doit tre plac en prfixe au format: %ld,%lo,%lx. Exemple:
scanf(%lx,&a); entre = ff01 -> a=0xff01

Saisie des flottants

Les formats de base sont %f et %e. Exemples:


scanf (%f,&x ); scanf (%e,&x ); entre = 0.1 entre = 1.2e3 -> -> x = 0.1 x = 1200

Pour les double, un l ou L doit tre plac en prfixe au format: %lf,%le.

Saisie des caractres et des chanes

Le format est %c pour les caractres et %s pour les chanes. Exemple:


scanf(%c%c,&c1,&c2); c2 = b scanf(%s,m); entre = ab entre = au revoir -> -> c1 = a ; m = au

Dans le cas des chanes un nombre maximum de caractres peut tre indiqu. Ceci peut tre utile pour ne pas quune chane dborde de la variable qui lui a t alloue. Exemple:
char m[6]; scanf(%5s,m); entre = bonjour -> m = bonjo

La valeur de retour de scanf

Elle indique le nombre de paramtres affects correctement. Exemple:


scanf(%d%d%d,&a,&b,&c); valeur retour = 1 entre = 10 xyz 11 ->

Le scanf sest termin prmaturment quand x a t dtect alors quun dcimal tait attendu. Les caractres xyz 11 restent dans le buffer et pourront tre lus dans une prochaine lecture.

Le rle des espaces dans les scanf

De manire gnrale un espace dans une chane guide de scanf force le passage sur tous les caractres sparateurs: lespace, la tabulation, le saut de ligne. Exemple:
scanf(%d %d,&a,&b); = 11 entre = 10^^^^^^11 -> a = 10 ; b

En fait, dans ce cas, la chane guide %d%d produit le mme effet car le format %d demande dj llimination de tous les sparateurs qui prcdent la valeur dcimale. Il en va de mme pour tous les formats sauf pour le format %c. Exemple:
scanf(%c%c,&c1,&c2); c2 = ^ scanf(%c %c,&c1,&c2); c2 = b entre = a^^^^^^^b entre = a^^^^^^^b -> -> c1 = a ; c1 = a ;

44

E/S caractres
Les e/s caractres permettent de lire ou crire un seul caractre. Ceci peut tre fait avec les e/s formates en utilisant le format %c, mais avec plus de lourdeur. putchar getchar Exemple:
#include <stdio.h> main() { char c; c = getchar(); putchar(c); }

criture dun caractre lecture dun caractre

E/S lignes
Les e/s lignes permettent de lire ou crire une ligne. puts gets Exemple:
#include <stdio.h> main() { char ligne[40]; gets(ligne); puts(ligne); }

criture dune ligne lecture dune ligne

Ces fonctions transforment ligne en chane et vice-versa: gets: puts: au revoir\n au revoir\0 -> -> au revoir\0 au revoir\n

Programmes interactifs
Un programme interactif est un programme qui dialogue avec lutilisateur: lutilisateur gnralement donne des commandes, le programme agit et donne un rsultat. Un programme interactif est typiquement de la forme suivante:
#include <stdio.h> void main(void) { char com; float memoire,x; memoire=0; do { printf(\n>); scanf( %c,&com); switch (com) { case -: scanf(%f,&x); case +: scanf(%f,&x); case *: scanf(%f,&x); case /: scanf(%f,&x);

memoire=memoire-x;break; memoire=memoire+x;break; memoire=memoire*x;break; memoire=memoire/x;break;

45

case =: case z: case q: default : } }

printf(%f,memoire);break; memoire=0;break; break; printf(?\n);

} while ( com != q ) ;

Ce programme est une mini-calculette qui se comporte ainsi:


> > > > > > > > +2 *3 =6 +1 =7 z =0 q

On notera dans la saisie de la variable com le format %c (format caractre prcd dun espace) qui permet que les espaces (au sens dfini un peu plus haut) taps avant la commande soient ignors.

Dsynchronisation clavier/cran
Il arrive que lon ait des problmes de dsynchronisation clavier/cran du fait que les entres sont bufferises. Voici un exemple:
#include <stdio.h> void main(void) { int a,b,c; printf(entrez a: ); scanf(%d,&a); printf(entrez b: ); scanf(%d,&b); printf(entrez c: ); scanf(%d,&c); printf(fin\n); }

Premier exemple dexcution:


entrez a: 10 entrez b: 11 entrez c: 12 fin

Deuxime exemple dexcution:


entrez a: 10 11 entrez b: entrez c: 12 fin

La dsynchronisation constate dans le second exemple provient de ce que deux valeurs on t tapes en rponse au premier scanf. La premire seulement a t lue par ce scanf, la seconde est reste dans le buffer. Quand le second scanf est excut, il prend la valeur prsente dans le buffer sans attendre quune valeur soit tape au clavier. Dans ce cas, la dsynchronisation peut tre vite en vidant le buffer jusqu la fin de ligne aprs avoir lu la valeur souhaite. Voici le mme programme en version in-dsynchronisable:
#include <stdio.h> void main(void) { int a,b,c; printf(entrez a: ); scanf(%d,&a); while (getchar()!=\n){};

46

printf(entrez b: ); scanf(%d,&b); while (getchar()!=\n){}; printf(entrez c: ); scanf(%d,&c); while (getchar()!=\n){}; printf(fin\n); }

Cependant, si cette nouvelle version ne se dsynchronise plus, il peut y avoir encore des problmes. Par exemple, en cas derreur de frappe, on peut se retrouver avec une variable non affecte:
> entrez a: 10 > entrez b: a1 > entrez c: 4

Le second scanf ne peut accepter la valeur a1 qui reste donc dans le buffer et se trouve limine par le vidage de ligne. Le programme se termine avec a=10, b=NON_AFFECTEE et c=4... Pour remdier il faut encore btonner le programme en testant la valeur retour du scanf qui doit ici tre 1:
#include <stdio.h> void main(void) { int a,b,c; int n; do { printf(entrez a: ); n = scanf(%d,&a); while (getchar()!=\n){}; } while (n != 1); do { printf(entrez b: ); n = scanf(%d,&b); while (getchar()!=\n){}; } while (n != 1); do { printf(entrez c: ); n = scanf(%d,&c); while (getchar()!=\n){}; } while (n != 1); printf(fin\n); }

Fichiers
Un fichier est un ensemble de valeurs ranges sur un disque. Sous UNIX, les priphriques de/s peuvent tre considrs comme des fichiers. Les e/s consoles apparaissent ainsi comme des cas particulier de la gestion de fichiers. Bien quil soit possible daccder aux priphriques par des primitives de bas niveau, cest un usage qui est dconseill car dune part cest complexe (transfert par blocs, interruptions...), dautre part cest dlicat car une erreur de programmation peut tre catastrophique (par exemple dgts sur la disque...).

47

Cest pour cela que des librairies de fonctions de plus haut niveau sont fournies. On distingue deux niveaux de fonctions: - les librairies de/s dUNIX - les librairies de/s du C ANSI Les librairies de/s dUNIX fournissent un bon niveau de scurit, mais leur maniement est encore assez complexe. Et surtout, leur usage dans un programme ne permet pas de porter ce programme sur un ordinateur non-UNIX. Les librairies de/s du C ANSI sont indpendantes du systme dexploitation: un programme qui les utilise peut tre port sur tout ordinateur qui possde un compilateur C ANSI. De plus ces librairies sont de plus haut niveau que les librairies UNIX donc encore plus simples utiliser. Nous nous limitons dans cet expos la librairie de/s du C ANSI. Son utilisation ncessite dinclure <stdio.h>. Pour plus de dtail sur les fonctions de/s voir leurs descriptions dans la librairie ANSI.

Introduction la manipulation dun fichier


Le programme suivant calcule la somme des valeurs entires ou flottantes contenues dans un fichier:
#include <stdio.h> void main(void) { char nom[21]; float valeur, somme; int r; FILE *df; printf(nom du fichier: ); scanf(%20s,nom); df = fopen(nom,r); somme=0; do { r = fscanf(df,%f,&valeur); somme = somme+valeur; } while ( r==1 ); if ( r==0 ) printf(erreur dans le fichier\n); else printf(somme=%f\n,somme); fclose(df); }

On constate dans cet exemple plusieurs tapes: 1) La variable df est dclare: Cette variable est un pointeur sur un objet de type FILE. Un objet de ce type est un descripteur de fichier. Un descripteur de fichier contient des informations relatives un fichier en cours dutilisation: nom, tat, buffers, ... Pour linstant df ne pointe vers rien. 2) Ouverture du fichier: Lopration douverture fopen consiste demander laccs un fichier. En rponse cette demande le systme prpare laccs au fichier, remplit un descripteur et en renvoie ladresse en retour de fonction. Dans notre cas, louverture est effectue en vue de lecture (paramtre r). Maintenant df pointe vers le descripteur du fichier nom et servira pour dsigner ce fichier dans les oprations suivantes.

48

3) Lecture du fichier: Cette lecture est ici ralise par fscanf. Cette fonction est analogue la fonction scanf mais opre sur un fichier ouvert dont on indique ladresse dun descripteur en premier paramtre. 4) Fermeture du fichier: La fermeture, ralise par fclose, dsalloue le descripteur de fichier et les structures de donnes qui y sont lies (buffers...).

Le type FILE
Le type FILE, dfini dans <stdio.h>, est le modle pour une structure de donnes appele descripteur de fichier. Cette structure de donnes contient des informations relatives au fichier manipul: nom, tat, buffers, ... Lors de louverture dun fichier, le descripteur de fichier est cr. Il est alors possible doprer sur le fichier (lecture, criture, etc...). A chacune de ces oprations le descripteur est indiqu en paramtre pour identifier le fichier sur lequel lopration doit sappliquer. Les descripteurs de fichiers sont manipuls via des pointeurs car ce sont des structures volumineuses qui seraient lourdes passer en paramtre. Lutilisateur na pas se soucier du contenu du descripteur de fichier: cest une structure de donne prive gre par la librairie de/s.

Ouverture et fermeture
fopen fclose Usage de fopen: pointeur = fopen ( nom, mode_douverture ) On indique en paramtre le nom du fichier ouvrir et le mode douverture qui peut tre: r : lecture. Le fichier doit exister. w: criture. Si le fichier existe il est perdu; si il nexiste pas il est cr. a: criture. Si le fichier existe, criture la suite (append); sinon il est cr. et ladresse du descripteur est renvoye. Usage de fclose: fclose ( pointeur ) ouverture dun fichier fermeture dun fichier

Lecture et criture
Les oprations de lecture/criture sont analogues celles sur console. On peut distinguer diffrents types doprations: - e/s formates - e/s ligne - e/s caractre

49

fprintf fscanf fputs fgets fputc fgetc

criture formate lecture formate criture ligne lecture ligne criture caractre lecture caractre

De manire gnrales ces fonctions sutilisent comme les fonctions de/s console. Un paramtre indique simplement ladresse du descripteur du fichier sur lequel il faut oprer.

Les fichiers prdfinis


Trois fichiers sont prdfinis: - stdin - stdout - stderr par dfaut le clavier par dfaut lcran par dfaut aussi lcran

Les e/s console ne sont finalement pas autre chose que les e/s fichiers appliques ces fichiers prdfinis. Exemple: fprintf(stdout,Salut\n) quivaut printf(Salut\n)

Allocation et visibilit des variables


Lallocation dune variable concerne la manire dont elle est range dans la mmoire. Lallocation dtermine : - son existence: permanente ou non, - la possibilit de lui appliquer loprateur & (adresse de), - sa rapidit daccs. La visibilit dune variable concerne son accessibilit en un point dun programme. En un point du programme, une variable peut tre accessible (visible), en un autre point non. La dclaration dune variable dtermine son allocation et sa visibilit. Par dfaut, on peut schmatiser ainsi: allocation \ visibilit registre mmoire statique mmoire dynamique
Dclaration lintrieur dune fonction Dclaration lextrieur dune fonction

local

global

Variables globales, variables locales


s
Variables globales
Un objet global est dclar en dehors de toute fonction. Il est alors visible depuis sa dclaration jusqu la fin du fichier.

50

Si la dclaration est ralise comme cest lusage en dbut fichier, lobjet est visible depuis toutes les fonctions du fichier. Do le nom global. Les objets globaux ont une allocation statique, donc existent pendant toute la dure du programme. Note: Les fonctions sont considres comme des objets globaux.

Variables locales

Un objet local est dclar dans une fonction. Il est visible depuis sa dclaration jusqu la fin de la fonction. Si la dclaration est effectue comme cest lusage en dbut de fonction, lobjet est visible de tous les points de celle-ci, mais seulement delle. Do le nom local. Les objets locaux ont par dfaut une allocation automatique, donc nexistent que pendant la dure de vie de la fonction qui les contient. Une variable locale peut tre dclare statique. Dans ce cas, sa visibilit est toujours restreinte la fonction mais sa dure de vie est celle du programme. Ceci permet pour des usages particuliers de disposer de variables locales persistantes.

Exportation et importation
Tous les objets globaux, y compris les fonctions, sont exports par dfaut et peuvent tre imports dans un autre fichier pour y tre utiliss. Lexportation dun objet global est supprime si sa dclaration spcifie lattribut static. Pour importer un objet global dfini dans un autre fichier, on doit le dclarer en prcisant lattribut extern.

Allocation statique, automatique et dynamique


s
Allocation statique
Un objet statique est implant en mmoire dite permanente. Dans cette partie de la mmoire, une variable possde un emplacement pout toute la dure du programme. Cest lallocation par dfaut des objets globaux. Pour une variable locale lallocation statique est force par lattribut static. Exemple:
static int x ;

Allocation automatique

Les variables locales dune fonction sont par dfaut alloues dans la pile au dbut de lexcution de la fonction et sont dsallous la fin. Cette allocation est prise en charge par le compilateur, do le mot automatique. Si une fonction est appele plusieurs fois au cours dun programme, ses variables locales automatiques ont donc peu de chances de se trouver la mme adresse. Bien des compilateurs optimisent lutilisation des variables automatiques en leur allouant un registre. Ceci nest possible que si ce sont des variables simples et si lon ne leur applique pas loprateur & pour prendre leur adresse. Il est possible de forcer lallocation dune variable automatique dans un registre en spcifiant lattribut register lors de sa dclaration. Exemple:
register int v ;

51

Allocation dynamique

Un objet dynamique est implant dans une autre partie de la mmoire: le tas. Cest l que lon dpose les objets pour une dure quelconque. Ce type dallocation est dynamique car il est ralis par la volont du programmeur laide des fonctions de librairie malloc et free: - malloc ralise lallocation dun objet dans le tas, - free le dsalloue, cest dire libre la place.

Initialisation des objets


Les objets peuvent gnralement recevoir une valeur initiale. Cette valeur est donne au moment de la dclaration. Il y a lieu de distinguer objets statiques et objets automatiques:

Objets statiques
- soit par dfaut 0 - soit par une valeur constante donne la dclaration.

Les objets statiques sont toujours initialiss:

Objets automatiques

Les objets automatiques ne sont jamais initialiss par dfaut. Il peuvent recevoir une valeur, constante ou non, au moment de la dclaration. Cependant cette possibilit est interdite aux agrgats (tableaux, structures, unions). Note: Ces diffrences concernant les objets automatiques sexpliquent car ces objets sont crs donc initialiss en cours dexcution, alors que les objets statiques sont crs et initialiss ds la compilation.

Objet volatile
Les compilateurs ralisent frquemment des optimisations qui visent simplifier les boucles. Exemple:
while (1) { v = port; if (v==0) ... ... }

Cette boucle est optimise en:


v = port; while (1) { if (v==0) ... ... }

juste titre car la variable port semble ne pas changer de valeur pendant le cours de la boucle. Dans la plupart des cas cette optimisation ne pose pas de problme, mais le programmeur peut ne pas la souhaiter car il a prvu que la variable port sera modifie par un autre processus ou une interruption. Dans un tel cas on dit que la variable est volatile et il est ncessaire de prciser cela la dclaration en spcifiant lattribut volatile.

52

Exemple:
volatile short port ;

Objet constant
Lattibut de dclaration constant, spcifie que lobjet ne doit pas tre modifi. Ceci est vrifi par le compilateur. Exemple:
constant pi = 3.14 ;

Dfinition dune fonction


La dfinition dune fonction cest sa description complte: - la manire de lutiliser: le prototype, - la manire dont elle opre: le corps. La dclaration dune fonction cest lindication de son prototype. La dclaration est indispensable AVANT toute utilisation de la fonction car le compilateur doit pouvoir vrifier si lutilisation est conforme au prototype. La connaissance du corps nest pas ncessaire au compilateur: il peut tre spcifi ailleurs. Que peut-on faire de cette possibilit ?

Programme mono-fichier classique

Les fonctions sont dfinies (et donc du coup dclares) AVANT toute utilisation.
int somme (int a, int b) { return a+b; } void main(void) { ... z = somme(x,y); .... }

Programme mono-fichier avec fonctions dfinies aprs:

Pour une obscure raison, le programmeur souhaite spcifier le corps des fonctions en fin de fichier... Dans ce cas, cest possible, mais pour pouvoir appeler les fonctions il faut au moins que le prototype soit indiqu AVANT appel. Cest cette indication de prototype qui dclare la fonction et la rend utilisable.
int somme (int a, int b); void main(void) { ... z = somme(x,y); .... } int somme (int a, int b) { return a+b; }

Programme multifichier

Cest l vritablement lintrt de distinguer dfinition et dclaration.

53

Dans une programmation multifichiers, on programme souvent des fonctions dans des fichiers spars qui peuvent constituer une librairie, tandis que le programme qui les utilise se trouve dans un autre. Ici le fichier somme.c dfinit un fonction qui est utilise par test.c... Fichier test.c
extern int somme (int a, int b); void main(void) { ... z = somme(x,y); .... } int somme (int a, int b) { return a+b; }

Fichier somme.c

La librairie ANSI
La librairie (ou bibliothque) ANSI est un ensemble de fonctions utilitaires que lon retrouve dans tout compilateur C ANSI. Elle assure donc un certaine portabilit des programmes qui lutilisent. Pour utiliser une fonction de librairie, il est ncessaire dinclure le fichier header qui en contient la dfinition. Pour des raisons de commodit, la librairie C ANSI, a rparti les dfinitions des fonctions dans plusieurs headers.

stdio.h
s
FOPEN FFLUSH FCLOSE FEOF REMOVE RENAME

Gestion
FILE *fopen ( const char * nomfichier, const char * mode ) int fflush ( FILE * pointeur ) int fclose ( FILE * pointeur ) int feof ( FILE * pointeur ) int remove ( const char * nomfichier ) int rename ( const char * anciennom, const char * nouveaunom )

s
PRINTF

E/S formattes
int printf ( const char * guide, ... ) int fprintt ( FILE * pointeur, const char * guide, ... ) int sprintt ( char * chaine, const char * guide, ... ) int scanf ( const char * guide, ... ) int fscanf ( FILE * pointeur, const char * guide, ... ) int sscanf ( char * chaine, const char * guide, ... )

FPRINTF SPRINTF SCANF FSCANF SSCANF

E/S caractres
int putchar ( int c )

PUTCHAR

54

FPUTC GETCHAR FGETC UNGETC

int fputc ( int c, FILE * pointeur ) int getchar ( void ) int fgetc ( FILE * pointeur ) int ungetc ( int c, FILE * pointeur )

s
PUTS FPUTS GETS FGETS

E/S ligne
int puts ( const char * chaine ) int fputs ( const char * chaine, FILE * pointeur ) char * gets ( char * chaine ) char * fgets ( char * chaine, FILE * pointeur )

s
FSEEK

Oprations lies au positionnement


int fseek ( FILE * pointeur, long nboctets, int mode ) void rewind ( FILE * pointeur ) long ftell ( FILE * pointeur )

REWIND FTELL

ctype.h
ISALNUM ISALPHA ISCNTRL ISDIGIT ISLOWER ISPRINT ISPUNCT ISSPACE ISUPPER ISXDIGIT int isalnul ( char c ) int isalpha ( char c ) int iscntrl ( char c ) int isdigit ( char c ) int islower ( char c ) int isprint ( char c ) int ispunct ( char c ) int isspace ( char c ) int isupper ( char c ) int isxdigit ( char c )

string.h
STRCPY STRNCPY STRCAT STRNCAT STRCMP STRNCMP STRLEN char * strcpy ( char * destination, const char * source ) char * strncpy ( char * destination, const char * source, int size ) char * strcat ( char * destination, const char * source ) char * strncat ( char * destination, const char * source, int size ) int strcmp ( char * destination, const char * source ) int strncmp ( const char * destination, const char * source, int size ) size_t strlen ( const char * chaine )

55

math.h
SIN COS TAN ASIN ACOS ATAN ATAN2 SINH COSH TANH EXP LOG LOG10 POW SQRT CEIL FLOOR FABS double sin (double x) double cos (double x) double tan (double x) double asin (double x) double acos (double x) double atan (double x) double atan2 (double y, double x) double sinh (double x) double cosh (double x) double tanh (double x) double exp (double x) double log (double x) double log10 (double x) double pow (double x, double y) double sqrt (double x) double ceil (double x) double floor (double x) double fabs (double x)

stdlib.h
RAND int rand ( void ) SRAND void srand ( unsigned int graine ) MALLOC FREE EXIT SYSTEM GETENV ABS LABS DIV LDIV void malloc ( size_t taille ) void free ( void * adresse ) void exit ( int etat ) int system ( const char * commande_unix ) char * getenv ( const char * nom ) int abs ( int n ) long labs ( long n ) div_t ldiv ( int numerateur, int denominateur ) ldiv_t ldiv ( long numerateur, long denominateur )

assert.h
ASSERT void assert ( int expression )

56

time.h
CLOCK clock_t clock ( void ) TIME time_t time ( time_t adresse )

57