Vous êtes sur la page 1sur 9

L'essence de C

J. Azema, P. Ezequel
A consulter : { Methodologie de la programmation en langage C, J.P. Braquelaire, Masson { Le langage C, B.W. Kernighan, D.M. Ritchie, Masson

Cree par Kernighan, Ritchie, Thompson pour ecrire UNIX ( n des annees 60). Langage de haut niveau: fonctions, structures de contr^le, modules. o Langage de bas niveau: proche du langage machine.

1.1 Exemple de programme


1 2 3 4 4 5 6 7 5 /* Table de convertion Fahrenheit Celsius */ #include <stdio.h> main()

1 Elements du langage

int f; float c; printf(" Fahrenheit Celsius \n\n"); for (f=0; f<=300; f+=20)

c=(5.0/9.0)*(f-32.0); printf(" %4d %6.1f \n", f, c);

1. Commentaire 5. Ecriture sur la sortie standard 2. Inclusion de chier 6. Structure de boucle 3. Fonction principale 7. A ectation 4. Declarations de variables . Avec un editeur de texte (emacs par exemple) on cree un chier table.c :
$ emacs table.c & $ gcc -o table table.c table $ table

puis on compile ce chier source pour obtenir un chier executable de nom table : :

en n on execute

C'est autour d'elles que repose la construction de tout programme C. Pour eviter d'avoir a tout rede nir, on dispose de bibliotheques de fonctions prede nies (lecture, ecriture, appels systeme, mathematiques, 1

1.2 Les fonctions

manipulation de cha^nes de caracteres,. . .). Il y en a une centaine pour la norme C ANSI. Une fonction est { declaree : on precise son nom, le nombre et le type de ses parametres, le type de son resultat (pro l, signature ou prototype). Exemple: int max( int, int); { de nie : on decrit (par un algorithme ecrit en C) comment on calcule la valeur de la fonction a partir de la valeur de ses parametres. Exemple:
int max( int a, int b) { if (a>b) return a; else return b; }

{ appelee : on calcule e ectivement une valeur a partir des valeurs des parametres. Exemple: k=max(2*i,5) Un programme C peut ^tre ecrit dans un ou plusieurs chiers. Chaque chier contient la de nition e d'une ou plusieurs fonctions (on ne peut de nir une fonction a l'interieur d'une fonction). On doit toujours de nir une (et une seule) fonction main qui sera appelee a l'execution. Les chiers sources ont pour su xe .c. Les declarations des fonctions sont souvent regroupees dans des chiers d'en-t^te de su xe .h que l'on inclut par la directive #include. e Apres compilation on obtient un chier objet de su xe .o. L'editeur de liens regroupe tous les chiers objets (y compris eventuellement les bibliotheques) en un seul chier executable. Son nom est traditionellement celui (sans le su xe) du chier source contenant la de nition de la fonction main (par defaut c'est a.out).

1.3 Fichier, programme, compilation

1.4 Vocabulaire de C

{ Commentaires: /* et */ { Caracteres autorises : les 26 lettres minuscules, les 26 lettres majuscules, les 10 chi res, l'espace, les caracteres ! " # % & ' ( ) * + , . - / : ; < = > ? \ ] ^ f g | { identi cateurs : formes d'une lettre suivie d'au plus 31 (C ANSI) lettres, chi res ou . ATTENTION !! C fait la di erence entre majuscules et minuscules. { mots reserves (a ne PAS utiliser comme identi cateur) : int char float double void long short
signed unsigned enum struct union typedef sizeof const auto extern register static volatile goto return break continue if else for do while switch case default asm

Pas de regles obligatoires, mais il est fortement conseille de rendre le programme le plus lisible possible : { en mettant des commentaires { en ne mettant sur une ligne { qu'une seule declaration, ou { qu'une seule instruction, ou { que le debut d'une structure de contr^le, ou o { qu'une seule accolade (ouvrante ou fermante). { en separant les declarations des instructions { en indentant les di erents blocs Si vous utilisez emacs, sachez qu'il conna^t la syntaxe de C, et donc qu'il peut automatiquement indenter les blocs, ou vous indiquer les parentheses manquantes,... 2

1.5 Regles de presentation

2 Declarations
Forme generale : <type><liste Exemple: int i,j,k;
de noms d'objets>;

2.1 Types de base


{ { { { {
int signed

: entier; la taille depend de la machine (16, 32 ou 64 bits). On peut preciser short ou long, (defaut) ou unsigned. char : caractere; sa valeur est le code ASCII. float : reel virgule ottante; depend de la machine. double : reel ( double precision) ; depend de la machine. ( ) void : ensemble vide.

2.2 Types elementaires

Ce sont les types de base, ainsi que les pointeurs (un pointeur est une adresse, cf infra).

A partir des types elementaires on peut construire des objets (ou des types d'objets) plus complexes a l'aide des constructeurs { * : constructeur de pointeur { ] : constructeur de tableau { ( ) : constructeur de fonction { struct : constructeur de structure (agregats) On peut bien s^r combiner ces constructeurs. .. u

2.3 Construction d'objets ou de types

2.4 Les tableaux


La taille du tableau est une valeur entiere constante. Exemples : int T 10]; char ligne 80];
<type de reference><nom du tableau> <taille du tableau>];

2.5 Les structures


de champs> ;

La declaration struct

Exemple:

<nom>

f<liste

de champs> ;

g rend struct

<nom>

synonyme de f<liste

struct complexe double reelle; double imaginaire;

g;

struct complexe z; z.reelle=5.8; z.imaginaire=1;

3 Expressions
Formee d'operandes et d'operateurs unaires ou binaires (il y en a 44 en tout), elle fournit une valeur. Si les deux operandes ne sont pas de m^me type, on convertit le plus ((faible)) en le plus ((fort)). L'ordre e est le suivant :
char short int long pointeur

et

<

<

long

<

float

<

double

<

long double

3.1 Operateurs arithmetiques


+ - * / %

Entre deux entiers, / est le quotient et % le reste de la division euclidienne.

3.2 Operateurs de relation


Il n'y a pas de type booleen. 0 represente faux, toute valeur non nulle vrai (cf la liste vide en SCHEME ou en LISP)
== != > >= < <=

3.3 Operateurs logiques

{ ! (unaire) negation { && (et) : si l'operande gauche est faux retourne faux sinon retourne la valeur de l'operande de droite. { || (ou) : si l'operande gauche est vrai retourne vrai sinon retourne la valeur de l'operande de droite.

3.4 Operateurs d'a ectation


=

ou <op>=, ou <op> est l'un de + - * / % L'operande gauche est une variable; l'operande droit est une expression. A ecte la valeur de l'operande droit a l'operande gauche, et retourne cette valeur. ATTENTION !! if (x=5) ... ne teste pas l'egalite de x et de 5, mais a ecte a x la valeur 5, et renvoie 5, c'est-a-dire vrai !
<expression test>? <expression si vrai>: <expression si faux>

3.5 Operateur conditionnel 3.6 Operateurs increment et decrement


++ est l'increment, -- le decrement. Si un objet (entier) i vaut 5, l'expression ++i retourne 6 et i vaut 6, et l'expression i++ retourne 5 mais i vaut 6.

3.7 Operateurs sur les pointeurs et les structures


{ * : indirection { & : adresse { . : Acces a un champ { -> : Acces indirect a un champ { sizeof(...) : Taille (d'un objet ou d'un type).

3.8 Conversions
int a,b; float c; a=5; b=8; c= (float) a/b;

Une conversion peut ^tre implicite (operandes de types di erents, a ectation) ou explicite (on dit qu'il e y a coercition, ou cast). Exemple :

3.9 Priorite des operateurs


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (...) [...] -> . ! ~ ++ -- - * & (<type>) sizeof(...) * / % + << >> < <= > >= == != & ^ | && || ...?...:... = += -= *= /= %= &= ^= |= <<= >>= ,

4 Instruction

4.1 Instructions elementaires


scanf("%d %f",&i,&x);

{ instruction nulle : ; { instruction expression : <expression>; En general l'instruction est une a ectation, ou un appel de fonction a valeur void, par exemple en lecture : ou en ecriture :
printf("i = %d x = %f \n",i,x);

{ instruction composee (dite aussi bloc) :

{ dclaration

instruction

Exemple:
{ int a,b,s; printf("Entrer deux entiers\n"); scanf("%d%d",&a,&b); s=a+b; printf("La somme de %d et %d est %d\n",a,b,s); }

4.2.1 Instruction if

4.2 Instruction conditionnelle

if <expression> <instruction si vrai> else <instruction si faux>

On evalue expression; si la valeur est non nulle (donc vrai), on execute instruction si vrai. Si la valeur est nulle, et si la clause \else" est presente, on execute instruction si faux. Le else est associe au if le plus proche, en cas d'imbrication.

4.2.2 Instruction switch


switch (<expression>)

case <expression constante 1> : <instruction> . . . case <expression constante > : <instruction> default : <instruction> g case <expression constante > : <instruction> . . . case <expression constante > : <instruction>

k fFacultatif k+1 n

On evalue <expression>; soit e la valeur retournee. De trois choses l'une : { e est l'une des <expression constante> : l'execution reprend a cet endroit la. { e n'est pas l'une des <expression constante> et la clause default est presente : l'execution reprend a la clause default. { e n'est pas l'une des <expression constante> et la clause default est absente : l'execution du switch est terminee. Les <expression constante> doivent ^tre distinctes. ATTENTION !! L'execution de e l'<instruction> associee a une <expression constante> ne provoque pas de rupture de sequence : si c'est la i-eme clause qui est selectionnee, toutes les instructions de i a n seront executees. La rupture de sequence doit ^tre forcee par une instruction break, cf gure 1. e 6

switch (c)

switch (c) :; :printf("c vaut a \n"); :; :printf("c vaut b \n"); :printf("c vaut autre chose \n");

case 'a' case 'A' case 'b' case 'B' default

g
Fig.

case 'a' :; case 'A' :printf("c vaut a \n"); break; case 'b' :; case 'B' :printf("c vaut b \n"); break; default :printf("c vaut autre chose \n"); break;

1 { switch avec et sans break

4.3 Boucles

4.3.1 Boucle tant que


( (

) )

while (<expression>) <instruction> instruction

On execute repetitivement. Avant chacune des executions, on teste expression. L'execution s'arr^te des qu'une des evaluations d'expression renvoie faux. e Exemple: recherche d'une valeur v dans un tableau T :
i=0; while ((i<n) && (T i] != v)) i++;

4.3.2 Boucle pour


( (

) )

for (<exp. initiale>;<exp. test>;<exp. increment>) <instruction>

C'est equivalent a
<exp. initiale>; while (<exp. test>)

Exemple: calcul dans s de la somme des n premiers entiers.


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

<instruction> <exp. increment>;

5 Tout (ou presque . . . ) sur les pointeurs


La memoire est divisee en emplacements (appeles mots), reperes par un numero (une adresse). Les di erents objets (variables, tableau, structure, fonction,. .. ) manipules par un programme sont ranges en memoire, et ont une adresse qui est celle du premier mot memoire qu'ils occupent (car tous les objets n'ont pas la m^me taille en memoire). ATTENTION !! Certaines parties de la memoire sont reservees e 7

5.1 Adresses

a certaines t^ches (zones systemes, memoire video, tampons d'entrees-sortie,. .. ), et ne peuvent pas ^tre a e utilisees par un programme C. De m^me, chaque utilisateur (physique ou logiciel) se voit allouer une partie e de la memoire destinee a son seul usage. Il existe donc des regions entieres de la memoire a l'interieur desquelles un utilisateur ne peut pas lire et/ou ecrire. Nous dirons alors d'une adresse qu'elle est valide pour un utilisateur si celui-ci peut y lire et y ecrire. En C, il existe deux operateurs qui permettent d'obtenir l'adresse d'un objet et sa taille : { & : unaire, pre xe, donne l'adresse de son argument. Par exemple, &toto est l'adresse de la variable toto. { sizeof(...) : unaire, in xe, donne la taille (en octets) de son argument (qui est soit une variable, soit un type). Par exemple, sizeof(int) = 4 et sizeof(char *) = 8. Noter que la taille d'un type (ou d'un objet) depend de la machine sur laquelle tourne le compilateur, ainsi que du compilateur lui-m^me: les valeurs donnees ici sont celles de gcc-2.95.2 sur une DECe Alpha (moneta en l'occurrence), et seraient sans doute di erentes sur un autre materiel (m^me avec e gcc-2.95.2). Une variable de type pointeur est une variable dont la valeur est une adresse valide. C'est l'adresse d'un objet dont on conna^t le type (donc la taille .. .). A la declaration, on donne le type vers lequel on pourra pointer, suivi de *. Ainsi, int *p declare un pointeur p, c'est-a-dire une variable dont la valeur sera l'adresse du premier mot d'une zone memoire contenant un entier (on dira plus simplement que p pointe sur un entier). Par exemple, si p vaut 536869440, on doit trouver en 536869440 un entier.

5.2 Pointeurs

5.3 Operations sur les pointeurs


{ A ectation :
int i,j,*p,*q; p=&i; q=p; p=(int *)536869440; q=NULL; int i,*p,*q; *p=7; i=*p; *q=*p+5; (*p)++;

{ Indirection (on dit aussi dereferencement) : *p est l'objet de type int pointe par p.

{ Comparaisons:
int *p; while (p!=NULL) ... if (p<(int *) 536869440) ...

Dans certains cas on peut utiliser les operations additives (le compilateur prend en compte la taille du type pointe). Par exemple, si la memoire est comme represente sur la gure 2, l'a ectation q=p+3 va la transformer comme indique dans la gure 3. C'est d'ailleurs ainsi que sont implantes les tableaux en C : une variable de type tableau est un pointeur sur le premier element du tableau. Du coup, l'expression t i] est en fait interpretee comme *(t+i). 8

p q (nimporte o)
Fig.

p q
Fig.

2 { Avant l'addition . . .

3 { . . . Apres l'addition

La memoire est allouee a un programme C de trois facons di erentes : { a la compilation (pour les variables statiques, en general declarees en dehors des fonctions) { lors de l'appel d'une fonction (dans la pile) { a la demande du programme, pour faire pointer les pointeurs sur ((quelque chose)) ! Cette demande se fait a l'aide de la fonction void *malloc(int Taille) 1 de la librairie stdlib.h (qu'il faut donc inclure dans le source du programme avant d'utiliser la fonction), qui renvoie un pointeur sur un bloc de Taille octets. Il faut ensuite forcer le bon type pour le pointeur, au moyen d'une coercition (ou cast) : En n on peut liberer la memoire non utilisee, avec la fonction void
free(p); p=(int *)malloc(sizeof(int)); free(void *adresse)

5.4 Allocation dynamique de memoire

1. en fait le prototype exact est

void *malloc(size t Taille)

Vous aimerez peut-être aussi