Você está na página 1de 168

Techniques De Programmation

Langage C

1ère Année GC
Ecole Hassania des Travaux Publics

Mohammed Karim GUENNOUN

1
Un algorithme

 Origine étymologique:
– Vient du nom du mathématicien Al Khawarizmi latinisé en
« algoritmi »
 Définitions:
– Académie Française: Méthode de calcul qui indique la
démarche à suivre pour résoudre une série de problèmes
équivalents en appliquant dans un ordre précis une suite
finie de règles.
– Wikipedia: Un algorithme est un moyen pour un humain de
présenter la résolution par calcul d’un problème à une autre
personne physique (un autre humain) ou virtuelle (un
2 calculateur)
Qualités d’un bon concepteur
d’algorithmes

 L’intuition:
– Pas de méthode universelle absolue pour résoudre
un problème
 La rigueur et la logique
– La méthodologie, la capacité à se mettre à la place
de la machine et d’être aussi idiot qu’elle!
 L’expérience
– Renforce les deux premiers points
– Plus on écrit d’algorithmes, plus on est performant à
3 le faire
Algorithmique Vs programmation

 Un langage de programmation n’est que


l’outil de réalisation de l’algorithme
 L’algorithme permet de concevoir la solution
alors que la programmation permet de la
mettre en œuvre
 L’algorithme est généralement à un niveau
d’abstraction plus haut que la programmation

4
Le Langage C

5
Origine

 Le langage C a été développé par Ritchie et Thompson


en 1972 dans les laboratoires de AT&T Bell
– Destiné initialement pour l’implémentation du système
d’exploitation UNIX
– Prend le nom C parce qu’il était considéré par ses développeurs
comme une suite du langage B
 C est un langage
– Procédural
– Compilé
 Une évolution vers l’orienté objet avec C++ (1998)

6
Etapes de développement d’un
programme C

 Edition du code source:


– Via un éditeur, codage du programme avec la
syntaxe du langage
 Compilation du code source
– En utilisant un compilateur C, générer
l’exécutable en code binaire
 Exécution de l’exécutable
– Lancement du fichier exécutable pour effectuer
les traitements spécifiées dans le code source.
7
Syntaxe

8
Un fichier code source C…

 Un programme C est constitué de


– Une partie directive du pré-processeur
 e.g. les include
 Commence par « # » et termine par un retour à la ligne
– Une partie déclarations globales
 E.g. la déclaration des constantes et des variables
 Termine par un « ; »
– Une dernière partie contenant les fonctions et les
procédure définies par le programme
 E.g. la fonction « main »
9 – Chaque instruction est terminée par le caractère « ; »
Les variables

10
L’intérêt des variables

 Parfois, il est nécessaire de stocker de l’information


au cours du calcul effectué par un programme
– Pour additionner deux valeurs, j’ai besoin de stocker ces
deux valeurs
– Pour additionner trois valeurs, j’ai besoin de stocker, en
plus, le résultat intermédiaire
 Une variable est donc une case où on peut stocker
de l’information repérée par une étiquette permettant
d’écrire dedans et de lire depuis.
 Physiquement, c’est une case de la mémoire vive de
l’ordinateur repérée par une adresse binaire
11
Cycle de vie d’une variable

 La déclaration

 L’instruction d’affectation

 La lecture/écriture

 La libération

12
Les variables en C

 Une variable correspond à une ou plusieurs


cases mémoire auxquelles le programme
attribut un nom
 Déclarée en spécifiant, entre autres, son
identifiant et son type
 Pour l’instruction: int a;
– On déclare une variable entière identifiée par la
chaîne de caractères « a »
– Un espace mémoire de 4 octets (compilateur gcc) est
13 réservé à cette variable
L’identificateur d’une variable

 L’identificateur d’une variable est choisi par le programmeur


– Une combinaison de lettres a-Z, de chiffres et de « _ »
– Le premier caractère ne peut être un chiffre
 Variable1 et Variable_1 sont OK
 1_Variable ne l’est pas
– Les espaces sont bannis
– Les caractères accentués sont aussi bannis
– Les compilateurs acceptent n’importe quelle longueur pour les
identifiants
 Seuls les 32 premiers caractères sont pris en compte
 Pour plus de lisibilité,
– Toujours privilégier les étiquettes qui ont du sens
– Éviter des étiquettes à rallonge
14
Expressions et opérateurs

 Une expression est un calcul qui renvoie une valeur, e.g.


5*5
 Elle comporte des variables, des appels de fonction et
des constantes combinés entre eux par des opérateurs,
e.g. sin(VarAngle*PI/180) )
 Plusieurs types d’opérateurs sont considérés dans C:
– Arithmétiques
– Relationnels
– Affectation
– Opérateurs d’adresses
15 – …
Opérateurs arithmétiques (1/2)

 S’appliquent à des valeurs entières ou réelles


 Unaires: un seul argument
– Le « + » et le « - », Le résultat est du même type que
l'argument
 Binaires: deux arguments placés de part et
d’autre de l’opérateur
– + (addition), - (soustraction), * (produit), / (division), %
(reste de la division)
– % nécessite obligatoirement deux arguments entiers,
16 les autres utilisent soit des entiers, soit des réels
Opérateurs arithmétiques (2/2)

 Deux cas
– Les opérandes sont du même type:
 Le résultat est du même type que ces opérandes
– Les opérandes sont de deux types (numériques) différents
 le compilateur prévoit une conversion implicite suivant l'ordre :
{ char -> int -> long -> float -> double } et { unsigned -> signed}
 Le compilateur considère aussi les valeurs de type
« char » comme des entiers, les opérations sont en fait
faites sur les valeurs de code (ASCII)
 Les calculs arithmétiques sont faits uniquement soit en
long soit en double, pour éviter des dépassements de
17 capacité
Exemples (1/2)

 int a=1,b=2,c=34000;
float x=1,y=2.0;

 a=(c*2)/1000;
– Même si on suppose qu’un entier est codé sur 2 octets, le
résultat est 68, même si l'on est passé par un résultat
intermédiaire (68000) qui dépassait la capacité des entiers (mais
pas celle des long)
 b=7/b;
– signe = donc en premier calcul de l'argument à droite : 7 (entier) /
2 (entier) donne 3 (entier, reste 1, que l'on obtient par 7%b). donc
18 b=3
Exemples (2/2)

 int a=1,b=2,c=34000;
float x=1,y=2.0;

 x=7/b;
– 7 et b entiers => passage en réel inutile, calcul de 7/2 donne 3
(entier, reste 1) puis opérateur = (transformation du 3 en 3.0 puis
transfert dans X qui vaut donc 3.0) */
 x=7/y;
– un int et un float autour de / : transformation implicite de 7 en réel
(7.0), division des deux réel (3.5), puis transfert dans x
 x=((float)(a+2))/b;
– calcul (entier) de a+1, puis transformation explicite en float, et
donc implicite de b en float, division 65.0/3.0 -> 21.666...
19
Opérateurs relationnels

 Comparaisons
– == égalité,
– != différent,
– <, >, <=, >=
 Des deux côtés du signe opératoire, il faut deux opérandes
de même type (sinon, transformation implicite) mais
numériques (les caractères sont classés suivant leur code
ASCII).
 Le résultat de l'opération est de type int :0 si faux, 1 si vrai
 Exemple : (5<7)+10*((1+1)= =2) donne 11

20
Logique booléenne (1/2)

 Le résultat d’une expression booléenne est


toujours
– 0 (faux) ou
– 1 (vrai)
 les opérandes devant être de type entier (si
char conversion implicite),
– 0 symbolisant faux
– toute autre valeur étant considérée vraie.

21
Logique booléenne (2/2)

 Opérateur unaire :
– ! (non).
 !arg vaut 1 si arg vaut 0, et 0 sinon.
 Opérateurs binaires :
– && correspond à l’opérateur ET
 vaut 1 si les 2 opérandes sont non nuls, 0 sinon
– || correspond à l’opérateur OU
 vaut 0 si les deux opérandes sont nuls, 1 sinon
 Le deuxième opérande n'est évalué que si le premier n'a pas
suffi pour conclure au résultat
 exemple: (a= =0)&&((x=x+1)<0) incrémente x si a est nul, le
laisse intact sinon.
22
opérations bit à bit

 S’appliquent à des entiers


 Contrairement aux opérateurs relationnels, les
résultats ne se limitent pas à 0 et 1.
 ~ :complément, unaire
 & :et, bit à bit
 | :ou bit à bit,
 ^ :ou exclusif bit à bit,
 >> :décalage à droite, le 2ème opérande est le
nombre de décalages
23
 << :décalage à gauche
Exemples

 7&12 donne 4
– 0111&1100 donne 0100
 5<<2 donne 20.
– 00..00101 décalé de deux positions à gauche
donne 00..10100

24
Opération d’affectation

 Affectation simple: le signe =


 Deux opérandes
– L’opérande de droite doit être une expression renvoyant un
résultat d’un type donné
– L’opérande de gauche est une variable
 Les deux opérandes doivent être du même type
– Si types numériques, elle peuvent être de deux types différents
 Le type de gauche doit être plus fort que celui de droite
 le compilateur fait une conversion implicite
 L’opération d’affectation renvoie une valeur
correspondant à la valeur transférée
25
Exemples

 a=5
– met la valeur 5 dans la variable a. Si a est float, il y a conversion
implicite en float
 b=(a*5)/2
– Effectue le calcul d'abord, puis met le résultat dans b
 a=5+(b=2)
– Le compilateur lit l'expression de gauche à droite. la première
affectation nécessite le calcul 5+(b=2). Celle ci comporte une
addition, dont il évalue le premier opérande (5) puis le second
(b=2). Il met donc 2 dans b, le résultat de l'opération est 2, qui
sera donc ajouté à 5 pour être mis dans a. La variable a vaut
26 donc 7 et b, 2. Le résultat de l'expression est 7 (si l'on veut s'en
servir).
Big Warning!!!

 il ne faut pas confondre = et = =


– Le compilateur ne relève pas d’erreur
– L’affectation d’une variable entière renvoie un
entier!!!
– Exemple : if (a=0) est toujours faux car quelle que
soit la valeur initiale de a, on l'écrase par la valeur
0, le résultat de l'opération vaut 0 et est donc
interprété par if comme faux.

27
Mathématique Vs Informatique

 Quelques pièges à éviter pour les grands mathématiciens


que vous êtes:
– La notion de variable n’est pas vraiment la même dans les deux
cas:
 x=2*y; en mathématique est une équation à résoudre ou une
assertion liant deux variables
 En informatique,
– cette expression n’a de sens que si "y" possède une valeur fixe
– Le rapport entre x et y n’est plus maintenu après l’exécution de cette
affectation
– L’opérateur = n’est pas symétrique:
 x=y et y=x sont deux expressions identiques en mathématiques
28  x=y et y=x ne le sont absolument pas en informatique
Incrémentation et décrémentation

 Incrémentation préfixée :++a


– Ajoute 1 à la variable a
– Le résultat de l'expression est la valeur finale de a
 l'incrémentation postfixée : a++
– ajoute 1 à la variable a
– Le résultat de l'expression est la valeur initiale de a
 de même, la décrémentation --a et a-- soustrait 1 à la
variable a.
 exemples :
– j=++i est équivalent à : j=(i=i+1)
29 – j=i++ est équivalent à : ?
affectation élargie

 + =,
 -=,
 *=,
 /=,
 %=,
 &=,…
 Exemples
– a+=5 est équivalent à a=(a+5).
30 – a&=1 est équivalent à a=(a&&1)
Opérateurs d’adresses

 Ces opérateurs sont utilisées avec des


pointeurs. On utilise
– &variable : donne l'adresse d'une variable
– *pointeur : réfère à la variable pointée (opérateur
d'indirection)
– . : champ d'une structure
– -> : champ pointé

31
L’operateur conditionnel ?

 C'est le seul opérateur ternaire.

 L'expression a?b:c vaut


– la valeur de b si a est vrai
– La valeur de c si a est faux.

 Exemple :
– val=a>b?a:b
32
opérateurs description

! ~ ++ -- - + & * (cast) unaires (* pointeurs)

*/% multiplicatifs

+- addition

>> << décalages

< <= > >= relations d'ordre

= = != égalité

& binaire
 Priorités par ordre
^ binaire

| binaire
décroissant
&& logique

|| logique

?: conditionnel (ternaire)

= += -= *= etc. affectation
33
Exo1

 Quelles seront les valeurs des variables A et B après


exécution des instructions suivantes ?
 int A,B;
A=5;
B=2;
A=B;
B=A;

 Les deux dernières instructions permettent-elles d’échanger


les deux valeurs de B et A ?
 Si l’on inverse les deux dernières instructions, cela
34 change-t-il quelque chose ?
Exo2

 On dispose de trois variables entières A, B et


C. Ecrire un programme transférant à B la
valeur de A, à C la valeur de B et à A la
valeur de C (toujours quels que soient les
contenus préalables de ces variables).

35
La Lecture/Ecriture

36
Motivation

 Un programme effectue des calculs mais, au


final, il serait intéressant de voir le résultat
quelque part:
– Un programme qui additionne 2 et 3 puis l’affiche à
l’écran est un peu plus intéressant qu’un programme
qui garde le résultat pour lui-même!
 Il est parfois intéressant de pouvoir lui fournir des
entrées pendant son exécution
– Un programme qui additionne deux valeurs que vous
lui donnez en entrée est un peu plus intéressant qu’un
37 programme qui additionne toujours 2 et 3!
Les fonctions de lecture/ecriture

 Prises en compte dans les bibliothèques du


compilateur

 La plus utilisée: stdio.h

 Normalisée, donc reste portable

38
Lecture/écriture des caractères

 char putchar(char) :
– affiche sur l'écran (ou du moins stdout) le
caractère fourni en argument
– stdout est l'écran, ou un fichier si on a redirigé
l'écran (en rajoutant ">nomfichier" derrière l'appel
du programme, sous DOS ou UNIX).
– cette fonction retourne le caractère affiché ou
EOF en cas d'erreur.
 char getchar(void) :
– attend le prochain appui sur le clavier, et rend le
39
caractère qui a été saisi
Lecture/écriture des chaînes de
caractères

 puts(chaîne)
– affiche, sur stdout, la chaîne de caractères puis positionne le
curseur en début de ligne suivante.
– retourne EOF en cas d'erreur
 gets(chaîne)
– lecture d'une chaîne sur stdin. Tous les caractères peuvent être
entrés, y compris les blancs.
– La saisie est terminée par un retour chariot. gets retourne un
pointeur sur le premier caractère entré (donc égal à son
paramètre d'entrée, ou le pointeur NULL en cas d'erreur).

40
écriture des chaînes de
caractères: printf

 printf(format,listevaleurs)
– affiche la liste de valeurs (variables ou expressions)
dans le format choisi.
– format est
 une chaîne de caractères entre guillemets (double quote "),
dans laquelle se trouve un texte qui sera écrit tel quel,
 des spécifications de format (débutant par %) qui seront
remplacées par la valeur effective des variables, dans l'ordre
donné dans listevaleurs,
– retourne le nombre de caractères écrits, ou EOF en
cas de problème.
41
Spécification du format
 % [largeur][.précision]type, avec
– La largeur est le nombre minimal de caractères à écrire (des blancs sont
rajoutés si nécessaire).
– Si le texte à écrire est plus long, il est néanmoins écrit en totalité.
– En donnant le signe * comme largeur, le prochain argument de la liste de
valeurs donnera la largeur
– ex printf("%*f",largeur,val)

 La précision définit,
– Pour les réels, le nombre de chiffres après la virgule (doit être inférieur à
la largeur).
– Dans le cas d'entiers, indique le nombre minimal de chiffes désirés (ajout
de 0 sinon)
– Pour une chaîne (%s), elle indique la longueur maximale imprimée
(tronqué si trop long).
42  La précision peut, comme la largeur, être variable en préfixant par: .*
Les caractères spéciaux pour
l’affichage

 \t (tabulation),

 \n (retour à la ligne),

 \\ (signe \),

43
Les types pour les formats

 Le type dans le format est :


– c: (char),
– s (chaîne de caractères),
– d (int),
– u (entier non signé),
– x (entier affiché en hexadécimal),
– oXo (entier affiché en octal),
– f (réel en virgule fixe),
– lf (réel double)
– e ou E (réel en notation exponentielle),
– g ou G (réel en f si possible, e sinon),
44 – p (pointeur),
Lecture des chaînes de
caractères: scanf

 scanf(format,listeadresse)
– lecture au clavier de valeurs, dans le format
spécifié.
– les arguments sont des pointeurs sur les
variables résultats (dans le cas de variables
scalaires, les précéder par l'opérateur &).
– retourne le nombre de valeurs effectivement lues
et mémorisées.

45
Le format pour scanf

 Le format peut contenir entre « " » :


– du texte (il devra être tapé exactement ainsi par
l'utilisateur, et ne sera pas stocké), des spécifications
de format, sous la forme %[*][largeur]type.
 * signifie que la valeur sera lue mais ne sera pas stockée
– scanf("%d%*c%d",&i,&j) : lecture de deux entiers séparés par
n'importe quel caractère
 la largeur est la largeur maximale lue, scanf s'arrête avant s'il
trouve un séparateur (blanc, tab, RC).
 scanf ne peut lire une chaîne avec des blancs: utiliser
« gets »
46
Bonne pratique

 Il est fortement conseillé, avant une


instruction de lecture, de prévenir l’utilisateur
et de lui indiquer ce qu’il doit faire
– Exemple:
int age;
printf("Veuillez renseigner votre age en annees, svp!");
scanf("%d",&age);
printf("Merci de votre collaboration");

47
Exo3

 Ecrire un programme qui demande un


nombre à l’utilisateur, puis qui calcule
et affiche le carré de ce nombre.

48
Exo4

 Ecrire un programme qui lit le prix HT d’un


article, le nombre d’articles et le taux de TVA,
et qui fournit le prix total TTC correspondant.

49
Les structures de
contrôle

50
Les instructions

 Une instruction peut être :


– soit une expression (pouvant comprendre une
affectation, un appel de fonction...), terminé par un
«;»
– soit une structure de contrôle (boucle, branchement...),
– soit un bloc d'instructions : ensemble de déclarations et
instructions délimités par des accolades { }. Un bloc
sera utilisé à chaque fois que l'on désire mettre
plusieurs instructions là où on ne peut en mettre
qu'une.
51
Tromper le séquentielle

 Normalement, les instructions s'effectuent


séquentiellement, c'est à dire l'une après
l'autre dans l’ordre de leur apparition dans le
code source.
 Pour accéder à une autre instruction que la
suivante, on a trois solutions :
– le branchement conditionnel
– le branchement inconditionnel,
– la boucle.
52
Les branchements conditionnels

 Deux types de branchement conditionnels


sont disponibles:

– Le branchement if

– Le branchement switch

53
Le branchement if

 structure :
– if (expression) instruction1
– Ou: if (expression) instruction1 else instruction2
 Même sémantique que le concept général
« Si Alors Sinon » en Algorithmique
– Si l'expression est vraie on effectue l'instruction1,
puis on passe à la suite.
– Sinon, on effectue l'instruction 2 puis on passe à
la suite.
54
Exo5

 Ecrire un programme C qui demande deux


entiers a et b à l’utilisateur et l’informe
ensuite si le produit est strictement négatif,
strictement positif ou nul sans calculer ce
produit

55
Tests imbriqués: motivation

 Prenons, un autre exemple:


– Un programme qui détermine l’état de l’eau selon la température:
– Une solution:
int Temp;
printf( "Entrez la température de l’eau :" );
scanf("%d ",&Temp);
if( Temp <= 0)
{printf( "C’est de la glace" );}
if( Temp > 0 && Temp =< 100)
{printf( "C’est du liquide");}
if( Temp > 100)
{printf( "C’est de la vapeur" );}

56
Tests imbriqués

 Pour notre exemple, en imbriquant les tests, il est plus élégant


d’écrire:
int Temp;
printf( "Entrez la température de l’eau :" );
scanf("%d ",&Temp);
if( Temp <= 0)
{printf( "C’est de la glace" );}
else{
if( Temp =< 100)
{printf( "C’est du liquide");}
else
{
printf( "C’est de la vapeur" );
}
}
57
Utilité

 L’utilisation des tests imbriqués n’est pas


indispensable, cependant elle permet d’avoir:
– des programmes plus lisibles
– des conditions moins complexes
– des programmes "plus performants"

58
Exo6

 Un magasin de reprographie facture 1DH les


dix premières photocopies, 0,75 DH les vingt
suivantes et 0,50 DH au-delà. Ecrivez un
programme C qui demande à l’utilisateur le
nombre de photocopies effectuées et qui
affiche la facture correspondante.

59
Exo7

 Les habitants de Mars paient l’impôt selon les règles


suivantes :
– les hommes de plus de 20 ans paient l’impôt
– les femmes paient l’impôt si elles ont entre 18 et 35 ans
– les autres ne paient pas d’impôt
 Le programme C demandera donc l’âge et le sexe du
Martien, et se prononcera donc ensuite sur le fait que
l’habitant est imposable.
 On utilisera, comme on le verra plus tard, la fonction
– int strcmp(char *str1,char*str2)
60 – rend 0 si str1= =str2, <0 si str1<str2, >0 si str1>str2
Exo8

 Les élections législatives sur Jupiter obéissent à la règle


suivante :
– lorsque l'un des candidats obtient plus de 50% des suffrages, il est
élu dès le premier tour.
– en cas de deuxième tour, peuvent participer uniquement les
candidats ayant obtenu au moins 12,5% des voix au premier tour.
 Ecrire un programme C qui permette la saisie des scores de
quatre candidats au premier tour. Cet algorithme traitera
ensuite le candidat numéro 1 (et uniquement lui) : il dira s'il est
élu, battu, s'il se trouve en ballottage favorable (il participe au
second tour en étant arrivé en tête à l'issue du premier tour) ou
défavorable (il participe au second tour sans avoir été en tête
61
au premier tour).
Le branchement switch case (1/2)
 structure :
– switch (expression_entière) {
case cste1:instructions
........
case csteN:instructions
default :instructions }
 L'expression ne peut être qu'entière :char, int, long
 L'expression est évaluée, puis on passe directement au
"case" correspondant à la valeur trouvée. L’exécution qui
s’en suit est séquentielle à partir de la première instruction
 Le cas default est facultatif, mais s’il est prévu il doit être
62
le dernier cas
Exemple

int voyelle(char c)
switch(c) {
case 'a': return(1);
case 'e': return(1);
case 'i': return(1);
case 'o': return(1);
case 'u': return(1);
case 'y':return(1);
default :return(0);
63 }
L’instruction break

 l'instruction break permet de passer directement


à la fin d'un switch
 Dans le cas de switch imbriqués on ne peut sortir
que du switch intérieur
 Exemple:
– switch (a) {
case 1:inst1;inst2;....;break;
case 2:....;break;
default:..... }
64
instn
Exo 9

 Ecrire un programme C demandant deux


réels et un opérateur sur les réels. Le
programme simule une calculatrice avec les
4 opérations classiques. Les résultat du
calcul est affiché pour l’utilisateur

65
Branchements inconditionnels

 Sémantique:
– Quand on arrive sur une telle instruction, on se branche
obligatoirement sur une autre partie du programme

 En C, 5 types de branchement inconditionnels


– break
– continue
– goto
– return
– exit

66
continue

 Cette instruction provoque le passage à la prochaine


itération d'une boucle.
– Dans le cas d'un while ou do while, saut vers l'évaluation du test
de sortie de boucle.
– Dans le cas d'un for on passe à l'expression d'incrémentation
puis seulement au test de bouclage.
– En cas de boucles imbriquées, permet uniquement de continuer
la boucle la plus interne.
 exemple :
for (i=0;i<10;i++) {if (i==j) continue; ...... }
– peut être remplacé par : for (i=0;i<10;i++) if (i!=j) { ...... }
67
break

 Il provoque la sortie immédiate de la boucle


ou du switch en cours.
 Il est limité à un seul niveau d'imbrication.
 exemples :
– do {if(i==0)break;....}
while (i!=0);
 À remplacer par un while
– for (i=0;i<10;i++){....;if (erreur) break;}
 A remplacer par for(i=0;(i<10)&&(!erreur);i++){...}
68
goto

 structure : goto label;


 Label est un identificateur
– non déclaré, mais non utilisé pour autre chose, suivi de
deux points (:),
– indiquant la destination du saut.
 Un goto permet de sortir d'un bloc depuis n'importe
quel endroit.
– ....
goto endroit;
.....
endroit: .....
69
Exo 10

 Ecrire un programme C, en utilisant « goto »,


qui demande deux entiers à l’utilisateur et lui
renvoie le résultat de leur division. Le
programme continuera de demander la
deuxième valeur jusqu’à l’obtention d’une
valeur non nulle.

70
return

 Permet de
– sortir de la fonction actuelle
– rendre la valeur résultat de la fonction.
 structure :
– return; ou return(expression);
 exemple :
int max(int a, int b) {
if (a>b) return(a);
71 else return(b);}
exit

 Ceci n'est pas un mot clé du C mais une fonction


disponible dans la plupart des compilateurs
 structure :
– exit(); ou
– exit(valeur);
 Elle permet de
– quitter directement le programme
– on peut lui donner comme argument le code de sortie
– libère la mémoire utilisée par le programme (variables + alloc) et
ferme les fichiers ouverts.

72
Les boucles

73
Justification, un exemple (1/2)

 Posons le problème suivant:


– Nous voulons réaliser un programme qui renvoie l’inverse d’un chiffre:
pour N, renvoyer 1/N.
– En bon concepteur, le programmeur veut prendre en compte le fait que
l’utilisateur ne doit pas rentrer un 0.
 En utilisant une structure conditionnelle, on peut écrire:
– int UnChiffre;
printf( "donner un chiffre et je vous renvoie son inverse");
scanf("%d" ,&UnChiffre);
if (UnChiffre ==0)
{
printf( "Saisie erronée. Donner un chiffre non nul !" );
scanf("%d" ,&UnChiffre);
}
74 printf( 1/UnChiffre);
Justification, un exemple (2/2)

 Qu’est-ce qui se passe si l’utilisateur donne


encore un zéro?
 Combien de Tests il faut inclure pour
résoudre le problème d’un utilisateur têtu?
 Conceptuellement, quelle serait la solution?

75
La Boucle while

 structure : while (expression) instruction


 Correspond sémantiquement à la boucle TantQue
– Tant que l'expression est vraie (!=0), on effectue l'instruction, qui
peut être
 simple (terminée par ;),
 bloc (entre {})
 vide (; seul).

 L'expression est au moins évaluée une fois. Tant qu'elle


est vraie, on effectue l'instruction, dès qu'elle est fausse,
on passe à l'instruction suivante (si elle est fausse dès le
début, l'instruction n'est jamais effectuée).
76
La boucle do while

 structure : do instruction while (expression);


(attention au ; final)
 La même sémantique que la boucle while,
mais :
– l'instruction est exécutée une fois avant la
première évaluation de l'expression

77
Exemple

 int a;
do {
puts("veuillez entrer le nombre 482");
scanf("%d",&a);
}
while (a!=482);
puts("Merci!");

78
Exo11

 Ecrire un programme qui affiche toutes les


puissances de 2, jusqu'à une valeur
maximale donnée par l'utilisateur. On
calculera la puissance par multiplications
successives par 2

79
Exo12

 Ecrire un algorithme qui demande à


l’utilisateur un nombre compris entre 1 et 3
jusqu’à ce que la réponse convienne.

80
Exo13

 Ecrire un algorithme qui demande un nombre


compris entre 10 et 20, jusqu’à ce que la
réponse convienne. En cas de réponse
supérieure à 20, on fera apparaître un
message : « Plus petit ! », et inversement,
« Plus grand ! » si le nombre est inférieur à
10.

81
Exo14

 Ecrivez un programme de jeu demandant de deviner


un nombre entre 0 et 100 choisi par l'ordinateur. On
donnera l'indication plus grand ou plus petit à
chaque saisie avant la découverte de la solution, où
l'on indiquera le nombre d'essais. La solution sera
choisie par l'ordinateur par la fonction rand() qui rend
un entier aléatoire (déclarée dans stdlib.h).

82
Boucle for (1/2)

 structure :
– for ( expr_initiale;expr_condition;expr_incrémentation) instruction
 Equivalente sémantiquement à la boucle « Pour »
 Cette boucle est surtout utilisée lorsque l'on connaît à
l'avance le nombre d'itérations à effectuer.
– L'expr_initiale est effectuée une fois, en premier.
– Puis on teste la condition.
– On effectue l'instruction puis l'incrémentation tant que la condition
est vraie.
– L'instruction et l'incrémentation peuvent ne jamais être
effectuées.
83
Boucle for (2/2)

 La boucle est équivalente à :


– expr_initiale;
while (expr_condition)
{ instruction;
expr_incrémentation;
}
 Une ou plusieurs des trois expressions
peuvent être omises,
 l'instruction peut être vide.
84
– for(;;); est donc une boucle infinie.
Exemple

 { char c;
for(c='Z';c>='A';c--) putchar(c);
}

85
Exo15

 Ecrire un algorithme qui demande un nombre


de départ, et qui ensuite écrit la table de
multiplication de ce nombre, présentée
comme suit :
Table de n :
nx1=7
n x 2 = 14
n x 3 = 21

86 n x 10 = 70
Exo16

 Ecrire un algorithme qui demande un nombre


de départ, et qui calcule la somme des
entiers jusqu’à ce nombre. Par exemple, si
l’on entre 5, le programme doit calculer :
– 1 + 2 + 3 + 4 + 5 = 15

87
Exo17

 Ecrire un algorithme qui demande successivement 20


nombres à l’utilisateur, et qui lui dit ensuite quel était le
plus grand parmi ces 20 nombres ainsi que sa position,
e.g. :
– Entrez le nombre numéro 1 : 12
Entrez le nombre numéro 2 : 14
etc.
Entrez le nombre numéro 20 : 6
Le plus grand de ces nombres est : 14, saisi a la position 2

88
Exo 18

 Réécrire le programme précédent, mais cette


fois-ci on ne connaît pas d’avance combien
l’utilisateur souhaite saisir de nombres. La
saisie des nombres s’arrête lorsque
l’utilisateur entre un zéro.

89
Exo19

 Écrire un programme qui permet de connaître ses


chances de gagner au tiercé, quarté, quinté:
– On demande à l’utilisateur le nombre de chevaux partants,
et le nombre de chevaux joués. Les deux messages
affichés devront être :
– Dans l’ordre : une chance sur X de gagner
Dans le désordre : une chance sur Y de gagner
– X et Y nous sont donnés par la formule suivante, si n est le
nombre de chevaux partants et p le nombre de chevaux
joués :
 X = n ! / (n - p) !
Y = n ! / (p ! * (n – p) !)
90
Déclaration et stockage
des variables

91
Gestion des variables

 Une variable doit être définie par le


programmeur dans une déclaration, en
indiquant
– Son nom permettant de faire référence à elle
– Son type permettant de
 Réserver l’espace mémoire adéquat
– Comment elle doit être gérée
– Visibilité
– Durée de vie

92
Gestion des variables lors de
l’exécution

 Les variables peuvent être déclarées


– Dans tout bloc d’instructions
 Les variables sont locales au bloc où elles
sont déclarée
 Lors de l’exécution
– Elles sont gérées en mémoire en utilisant une pile
 Quand on arrive au début du bloc « { », on réserve la
place mémoire au sommet de la pile
 Quand on atteint la fin du bloc « } », on retire la variable
de la mémoire
93
Exemple: appel de fonctions

 int double(int b)
{int c;
c=2*b;
b=0;
return(c); }
 int main()
{ int c=5;
printf("%d %d\n",double(c),c); }
94
Durée de vie et visibilité

 Une variable locale est créée à l'entrée du bloc, et


libérée à la sortie.
– Cette période est appelée sa durée de vie.
 Pendant sa durée de vie, une variable peut être visible
ou non
– Elle est visible : dans le texte source du bloc d'instruction à
partir de sa déclaration jusqu'au },
– Si une autre variable locale de même nom est déclarée dans un
sous bloc elle la cache
– Par contre elle n'est pas visible dans une fonction appelée par
le bloc (puisque son code source est hors du bloc).
95
Exemple

 int main(void)  [1] a=1


{int a=1; [1] [2] a=1 | b=2
{int b=2; [2] [3] a=1 | b=2 | a=3 :
[4a] a=1 | b=2 | a=3 | b=3 :
{int a=3; [3] [4b] a=1 | b=2 | a=3 | b=3 | c=0
fonction(a); [4] [4c] a=1 | b=2 | a=3 | b=3 | c=11
} [5] [4d] a=1 | b=2 | a=3
fonction(a); [6] [5] a=1 | b=2
} [7] [6a] a=1 | b=2 | b=1 :
[6b] a=1 | b=2 | b=1 | c=0
} [8] [6c] a=1 | b=2 | b=1 | c=9
void fonction (int b) [a] [6d] a=1 | b=2
{int c=0; [b] [7] a=1
c=b+8; [c] [8] la pile est vide, on quitte le
programme
96 } [d]
Déclaration des variables

 Une déclaration a toujours la structure suivante :


– [classe] type liste_variables [initialisateur];
 Le type peut être simple (char, int, float,...) ou
composé (tableaux, structures...,
 La liste_variables est la liste des noms des
variables désirées, séparées par des virgules s'il
y en a plusieurs
 L'initialisateur est un signe =, suivi de la valeur
à donner à la variable lors de sa création
97
Les classes de variables (1/3)

 auto :
– la variable est créée à l'entrée du bloc (dans la
pile) et libérée automatiquement à sa sortie.

– c'est la classe par défaut pour les variables


locales

98
Les classes des variables (2/3)

 register :
– la variable est créée, possède la même durée de vie et visibilité
qu'une classe auto,
– mais sera placée dans un registre du (micro)processeur.
– Si tous les registres sont déjà utilisés, la variable sera de classe
auto.
– Avantages
 Accès très rapide
– Inconvénients
 le compilateur peut avoir besoin des registres pour ses besoins
internes ou pour les fonctions des bibliothèques
 les compilateurs optimisés choisissent de mettre en registre des
99 variables auto, et souvent de manière plus pertinente que vous!
Les classes des variables (3/3)

 static :
– La variable ne sera pas dans la pile mais dans la
même zone que le code machine du programme.
– Sa durée de vie sera donc celle du programme.
– Elle ne sera initialisée qu'une fois, au début du
programme, et restera toujours réservée.
– Sa visibilité reste la même (limitée au bloc).
– Une variable statique permet en général un gain en
temps d'exécution contre une perte en place mémoire.

100
Déclaration globales

 Une déclaration faite à l'extérieur de tout bloc


d'instructions (en général en début du fichier)
est dite globale.
 La variable est stockée en mémoire statique, sa
durée de vie est celle du programme.
 Elle est visible de sa déclaration jusqu'à la fin
du fichier.
 Elle sera initialisée une fois, à l'entrée du
programme.
101
Classes des déclarations globales
(1/2)

 Le format d'une déclaration globale est


identique à une déclaration locale, seules les
classes varient.
 Par défaut, la variable est publique, c'est à dire
qu'elle pourra même être visible dans des
fichiers compilés séparément.

102
Autres classes des déclarations
globales

 La classe static
– rend la visibilité de la variable limitée au fichier
actuel.
 La classe extern
– permet de déclarer une variable d'un autre fichier
(et donc ne pas lui réserver de mémoire mais la
rendre visible).
– Elle ne doit pas être initialisée à cet endroit.
– Une variable commune à plusieurs fichiers devra
103
donc être déclarée sans classe dans un fichier et
Exemples

 int i,j; /* publiques */


 static int k=1; /* privée, initialisée à 1 */
 extern int z; /* déclarée (et initialisée) dans
un autre fichier */
 float produit(float,float); /* prototype d'une
fonction définie plus loin dans ce fichier */
 extern void echange(int *a, int *b); /*
prototype d'une fonction définie dans un
104
autre fichier */
Déclaration de types

 La norme ANSI permet de définir de nouveaux types de


variables par typedef.
 structure :
– typedef type_de_base nouveau_nom;
 Ceci permet de donner un nom à un type donné, mais ne
crée aucune variable. Une déclaration typedef est
normalement globale et publique.
 Utilité: définir un type complexe, où l'on définira petit à
petit les composants
– Exemple tableau de pointeurs de structures:
 on définit d'abord la structure, puis le pointeur de structure, puis le
105
tableau de tout cela).
Exemples

 typedef long int entierlong; /* définition d'un


nouveau type */
 entierlong i; /* création d'une variable i de
type entierlong */
 typedef entierlong *pointeur; /* nouveau
type : pointeur = pointeur d'entierlong */
 pointeur p; /* création de p (qui contiendra
une adresse d’un long int */
106
Les fonctions

107
Définitions générales

 Une fonction est définie par son entête, suivie


d'un bloc d'instructions
 L’entête:
– [classe] type_retourné nom_fonction(liste_arguments)
 Si la fonction ne retourne rien, le type_returné est « void »
 La liste_arguments doit être typée
 Si la fonction n'utilise pas d'arguments il faut la déclarer:
nom()

108
Gestion des arguments d’une
fonction

 Les arguments sont gérés comme des variables


locales à la fonction.
 Les valeurs fournies à l'appel de la fonction
(arguments instanciés) y sont recopiés à l'entrée
dans la fonction.
 Les instructions de la fonction s'exécutent du
début du bloc ({) jusqu'à return(valeur) ou la
sortie du bloc (}).
 La valeur retournée par la fonction est indiquée
109
en argument de return.
Exemple: elle ne prends pas
d’argument et ne retourne rien

 void hello( )
{
int i;
for(i=0;i<10;i++)
printf("bonjour les gars!");
}
 Appel: hello();

110
Exemple: elle ne retourne rien

 void AffichageMultiple(int nbr,char * compliment)


{
int i;
printf( "je vous le dit %d fois:\n" ,nbr);
for(i=0;i<nbr;i++)
printf("%s \n ",compliment);
}
 Appel: AffichageMultiples(5, "vous êtes très beau");

111
Exemple: elle prend et retourne

 float produit(float a,float b)


{
float z;
z=a*b;
return(z);
}
 Appel: float f=produit(1.2,4.5);

112
Un seul type de retour pour les
fonctions

 Il est impossible de retourner plusieurs


valeurs directement
 Solution:
– Passage d’argument par adresse
– Utilisation des variables globales
– Retourner un tableau ou d’une structure

113
déclaration des fonctions

 Toute fonction, pour pouvoir être utilisée, doit


auparavant être soit déclarée, soit définie.
 Une déclaration de fonction est généralement
globale, et alors connue des autres fonctions.
 Une déclaration de fonction est appelée
"prototype". Le prototype est de la forme :
– [classe] type_retourné nom_fonction(liste_arguments);
– Identique à l’entête mais terminée par un « ; »

114
Classes des fonctions

 Par défaut, une fonction est publique


 Elle peut aussi être déclarée
– extern: définie dans un autre fichier
– static: locale au fichier où elle est déclarée
 Exemples
– float produit(float,float);
 prototype d'une fonction définie plus loin dans ce fichier
– extern void echange(int *a, int *b);
 prototype d'une fonction définie dans un autre fichier
115
Récursivité et gestion de la pile

 Une fonction peut s’appeler elle-même


– Faire attention au problème de terminaison
 Exemple:
– int factorielle(int i)
{
if (i>1) return(i*factorielle(i-1));
else return(1);
}

116
Analysons la gestion de la pile

 (a) appel de factorielle(3), création de i, à qui on


affecte la valeur 3. comme i>1 on calcule
i*factorielle(i-1) : i-1=2 on appelle factorielle(2) i=1
 (b) création i, affecté de la valeur 2, i>1 donc on
appelle factorielle(1) i=2 i=2 i=2
 (c) création de i, i=1 donc on quitte la fonction, on
libère le pile de son sommet, on retourne où la i=3 i=3 i=3 i=3 i=3
fonction factorielle(1) a été appelée en rendant 1.
 (d) on peut maintenant calculer i*factorielle(1), i (a) (b) (c) (d) (e)
(sommet de la pile) vaut 2, factorielle(1) vaut 1, on
peut rendre 2, puis on "dépile" i
 (e) on peut calculer i*factorielle(2), i vaut 3
(sommet de la pile), factorielle(2) vaut 2, 3*2=6,
on retourne 6, la pile est vidée et retrouve son état
117 initial.
Arguments passés par adresse

 Regardons la fonction suivante:


– void echange(int i;int j)
{int k;
k=i;
i=j;
j=k;}
 Qu’est-ce qui se passe si on fait l’appel: echange(x,y)?
– les variables locales i,j,k sont créées sur la pile,
– i vaut la valeur de x, j celle de y.
– Les contenus de i et j sont échangés puis la pile est libérée,
– x et y ne sont pas modifiées
118 – Pour le faire, il faut passer par des pointeurs.
Arguments passés par adresse

 Regardons maintenant la fonction suivante:


– void echange(int *i;int *j)
{int k; k=*i;*i=*j; *j=k;}
 Qu’est-ce qui se passe si on fait l’appel: echange(&x,&y)?
– les deux arguments de la fonction (i et j) sont des pointeurs sur des int
– dans i on recopie l'argument réel qui est l'adresse de x, et l'adresse de y
dans j.
– On crée une variable locale k pouvant contenir un entier.
– On met dans k non pas la valeur de i mais le contenu pointé par i, donc le
contenu de x.
– On place la valeur pointée par j (donc y) à l'adresse pointée par j (donc x).
– On place la valeur de k à l'adresse pointée par j (y).
– On a donc échangé x et y.
119
La fonction main
 Signature:
– int main(int argc,char *argv[])
 argc indique le nombre de mots de la ligne de commande du système
d'exploitation
 argv est un tableau de pointeurs sur chaque mot de la ligne de
commande,
 env pointe sur les variables de l'environnement (e.g. Set Sous MSDOS)
 Exemple: Add 10 50
– argc vaut 3
– argv[0] pointe sur "Add",
– argv[1] sur "10"
– argv[2] sur "50".
120 – argv[3] vaut le pointeur NULL
Exercice

 Ecrire un programme qui simule une


calculatrice en définissant
– Une fonction pour chaque opération
– La fonction main qui permet de
 Lire l’opérateur et les opérandes sur la ligne de
commande (utiliser la fonction de conversion atoi qui
prend un char* et renvoie un entier)
 Afficher le résultat

121
Les types de données
du langage C

122
Les types de variables

 Deux sortes types


– Types Scalaires
 Ne contenant qu’une seule valeur
 entiers, caractères
– Types agrégés
 Contenant plusieurs valeurs
 Tableaux, structures…

123
Le type scalaire: char

 Valeurs mises entre « ' »


– char c='a';
 Stocké sur 8 bits
 Des caractères spéciaux sont définis
– \n : nouvelle ligne
– \t : tabulation
– \b : backspace
– \' : apostrophe

124
Le type scalaire: int

 Si l'on désire une taille précise, utiliser


– short int (16 bits) ou
– long int (32 bits).
 Sans précision, int donnera les programmes les plus
rapides pour une machine donnée.
 Par défaut, les int sont signés, mais on peut préciser
unsigned int.
 Désormais la plupart des compilateurs considèrent
short comme 16 bits, int comme 32 bits et long
comme 64 bits.
125
Le type scalaire: float

 Les réels sont toujours signés.


 On peut utiliser le spécificateur long pour des
réels avec une précision accrue.
 On peut également utiliser le nom double au
lieu de long float.
 Certains compilateurs acceptent même des
long double (quadruple précision).

126
Taille et plages des types scalaires
type taille (en bits) plage de valeurs

char 8 -128 à +127

unsigned char 8 0 à 255

short (short int) 16 -32768 à 32767

unsigned short 16 0 à 65535

long (long int) 32 -2.147.483.648 à 2.147.483.647

unsigned long 32 0 à 4.294.967.295

float 32 -3.4e38 à 3.4e38 (7 chiffres significatifs)

double (long float) 64 -1.7e308 à 1.7e308 (15 chiffres )


127
Conversion de type

 Dans les calculs, les char sont automatiquement


transformés en int.
 Quand un opérateur possède des arguments de
type différent, une transformation de type est
effectuée automatiquement, suivant l'ordre :
– char -> int -> long -> float -> double
 Attention! la transformation n'est effectuée que le
plus tard possible, si nécessaire.
– 5/2+3.5 donnera donc 5.5
128

Le cast

 On peut forcer une transformation en utilisant le cast, qui est un


opérateur unaire.
– La syntaxe est :
 (type_résultat) valeur_à_transformer
 exemple : {float x;int a=5; x=(float)a;}
 Un cast transformant un réel en entier prendra la partie entière
 Si on souhaite l’entier le plus proche, utiliser
– a= (int) (x+0.5);
 Le cast n'est une opération de transformation que pour les types
scalaires,
 pour tous les autres types, le cast ne permet que de faire croire au
compilateur que la variable est d'un autre type que ce qu'il attendait,
129 pour qu'il n'émette pas de message d'erreur.
Les Tableaux

130
Concept
 Un tableau est un regroupement, dans une même variable,
de plusieurs variables simples, toutes de même type.
 déclaration :
– [classe] type nom [nombre_d'éléments];
 exemple :
– int tab[10];
– réserve en mémoire un espace contigu pouvant contenir 10
entiers
– Le premier élément est tab[0] et le dernier c’est tab[9]
– Si vous utilisez tab[10] ou plus,
 pas forcément d’erreur
131  Vous allez récupérer ce qui a dans la mémoire correspondante
(probablement réservée pour autre chose!)
Les tableaux

 Il est possible de définir un tableau de n'importe


quel type de composantes (scalaires, pointeurs,
structures, tableaux…)
 On peut aussi initialiser un tableau.
– Dans ce cas la dimension n'est pas nécessaire.
– Si elle est donnée, et est supérieure au nombre de
valeurs données, les suivantes seront initialisées à 0
 vecteur vect0={0,0,0};
 int chiffres[]={0,1,2,3,4,5,6,7,8,9};
 int tableau[20]={1,2,3};
132
Exercice

 Ecrire le programme qui


– lit une ensemble de Nb notes et les mets dans un
tableau
– calcule et affiche la moyenne
– Dans un autre tableau, stock puis affiche l'écart
entre chaque note et cette moyenne.
– On définira pour cela les fonctions
 Saisie
 Moyenne
 Ecart
133
Les chaînes de caractères

 Le type chaîne de caractères n’est pas un type


scalaire en C
 Il est défini comme un tableau de caractères se
terminant par le caractère '\0'
 Une constante chaîne de caractères est mise
entre guillemets
– printf("salut");
 Est déclarée et initialisée comme suit:
– char mess[6]="salut";
134
– Ou mess ={'s','a',..,'t','\0'}
Bibliothèque pour les chaînes de
caractères(1/2)

 La bibliothèque string.h introduit des fonctions sur les


chaînes de caractères:
 int strlen(chaîne): donne la longueur de la chaîne (\0
non compris)
 char *strcpy(char *destination,char *source) recopie la
source dans la destination, rend un pointeur sur la
destination (dest=source quand à lui ne recopie pas la
chaîne mais uniquement son adresse !).
 char *strncpy(char *destination,char *source, int lg_max)
idem strcpy mais s'arrête au \0 ou longmax (qui doit
135 comprendre le \0)
Bibliothèque pour les chaînes de
caractères(2/2)

– char *strcat(char *destination,char *source) recopie la


source à la suite de la destination, rend un pointeur
sur la destination
– char *strncat(char *destination,char *source,int
lg_max) idem, avec une longueur max
– int strcmp(char *str1,char*str2) rend 0 si str1= =str2,
<0 si str1<str2, >0 si str1>str2 (classé par ordre ascii,
donc test du premier caractère, si différents test du
second, etc...) Les majuscules sont différentes des
minuscules (inférieures).
136
Les allocations dynamiques

 En C, le lien entre les tableaux et pointeurs permet


la notion de tableaux dynamiques :
– On peut définir la dimension d'un tableau lors de l'exécution
– Il faut d'abord réserver une zone de mémoire contiguë, de
la taille désirée
– Il faut avoir déclaré une variable pointeur qui contiendra
l'adresse du début du tableau
– A l'exécution, on peut réserver une zone mémoire par les
fonctions :
 void *malloc(int taille) : réserve une zone mémoire contiguë
de taille octets, et retourne un pointeur sur le début du bloc
réservé. Retourne le pointeur NULL en cas d'erreur .
137  void *calloc(int nb, int taille) : équivalent à malloc(nb*taille).
Exemple

#include <stdlib.h>

char *tab;
int nb;
puts("donner la taille");
scanf("%d",&nb);
tab=(char*) calloc(nb,sizeof(char));

138
D’autres fonctions
stdlib.h ou alloc.h (selon le compilateur)

 void free(void *pointeur): libère la place réservée


auparavant par malloc ou calloc. pointeur est
l'adresse retournée lors de l'allocation.
 void *realloc(void * pointeur,int taille): essaie, si
possible, de réajuster la taille d'un bloc de
mémoire déjà alloué (augmentation ou diminution
de taille). Si nécessaire, le bloc est déplacé et son
contenu recopié. En retour, l'adresse du bloc
modifié (pas nécessairement la même qu'avant) ou
139 le pointeur NULL en cas d'erreur.
Ecriture tableaux/ écriture pointeurs

 Une fois la zone de mémoire réservée, et


son adresse de début connue, on peut y
accéder soit par "l'écriture pointeurs" soit par
"l'écriture tableaux", puisqu'elles sont
équivalentes :
– premier élément : *tab ou tab[0]
– iiéme élément : *(tab+i) ou tab[i]
– adresse du iiéme élément (pour scanf par
exemple) : tab+i ou &(tab[i])
140
Tableaux multidimensionnels

 Le langage C permet de définir des tableaux


multidimensionnels
 Exemple: int tab[2][2] déclare une matrice
carrée d’entiers de dimension 2
 intialisation:
– tab= {{1,2},{2,1}}
– tab={1,2,2,1}

141
Petit exo:
 Ecrire un programme C avec les fonctions suivantes:
– Une fonction « affichage » qui affiche une matrice d’entiers
sachant sa dimension
– Une fonction « saisie » qui prend en paramètre deux entiers et
permet de remplir les éléments d’une matrice avec la dimension
correspondante. Elle retourne la matrice ainsi instanciée
– Une fonction qui prend en paramètres deux matrices ainsi que
leurs dimensions et retourne dans un troisième paramètre (quand
cela est consistant) la matrice résultant de leur multiplication
– La fonction main qui utilisera les fonctions précédente et
permettant de
 Saisir deux matrices ainsi que leurs dimensions
142  Afficher les deux matrices saisies ainsi que la matrice résultant de leur
multiplication
Les structures

143
Concept

 Dans les tableaux, les éléments doivent être


du même type.
 Parfois, on a besoin de définir des structures
de données de types hétérogènes
 Les structures permettent de répondre à ce
genre de problématiques
 Les champs ne sont pas désignés par leurs
positions, mais par un identificateur.
144
Déclaration des structures

 La déclaration
– struct nom_type {déclaration champs} liste_variables;
 Exemple:
– struct client { char* nom;
char* prenom;
char* mail;
int tel;
}
Toto,Titi,MesClients[20];
 Déclaration d’une variable:
145 – struct client tutu;
Traitements sur les structures

 Pour accéder aux champs des structures, on utilise


l’opérateur « . » ou l’opérateur « -> » pour les pointeurs
sur les structures
– printf("telephone du client %s : %d", Toto.nom, Toto.tel);
– scanf("%d",&Titi.tel);
 Généralement pour éviter de répéter le mot clé struct, on
définit un nouveau type en utilisant typedef:
– typedef struct client { char * nom;char * prenom;
char * mail; int tel;
} * Client;
– Utilisation: Client toto=(Client) malloc(sizeof(struct client));
146
– toto->nom="OK";
Les fichiers de données

147
Motivation

 Lors de l’exécution d’un programme, les


variables sont conservées dans la pile
d’exécution
 À la fin de l’exécution, les variables sont dépilées
et les données perdues
– Impossibilité de conserver les données d’entrée (e.g.
saisies par un utilisateur) ou les données résultants de
leur traitement
– Impossibilité de partager des données persistantes
148 entre différentes exécutions de programmes
Les fichiers pour la persistance

 Pour offrir la persistance de données, il faut passer par


un support de stockage persistant
– Fichiers, bases de données, …
 En C, on peut distinguer
– du point de vue accès
 Fichier séquentiels: accès au contenu dans l'ordre du stockage
 Fichiers à accès direct: accès à n'importe quel endroit du fichier
– Du point de vue format de stockage
 Les fichiers sont soit binaires: e.g. un float sera stocké comme il est
codé en mémoire , d'où gain de place mais incompatibilité entre
logiciels
 Les fichiers formaté ASCII: e.g. un float binaire sera transformé en
149
décimal puis on écrira le caractère correspondant à chaque chiffre)
Fichiers bruts

 L’utilisations des fichiers bruts:


– La méthode la plus efficace et rapide pour stocker et récupérer
des données sur fichier
– La plus lourde et la moins pratique
 Accès au fichier par lecture ou écriture de blocs (groupe
d'octets de taille définie par le programmeur)
 On traite les fichiers par l'intermédiaire de fonctions,
prototypées
– Ouverture et fermeture de fichier (librairie stdio.h)
– Les autres (librairie fcntl.h )

150
Ouverture et fermeture de fichiers
bruts
 L’ouverture d’un fichier est la première action à réaliser:
– int open(char *nomfic, int mode);
– Nomfic: le nom fichier à ouvrir
– Mode correspond au mode des interactions avec le fichier:
 O_RDONLY lecture seule,
 O_WRONLY écriture seule,
 O_RDWR lecture et écriture
 O_APPEND positionnement en fin de fichier (permet d'augmenter le fichier),
 O_CREAT crée le fichier s'il n'existe pas, au lieu de donner une erreur, sans
effet s'il existe
 O_TRUNC vide le fichier s'il existait
 On peut combiner cet accès avec d'autres spécifications, par une opération
OU (|)
– Retourne -1 si erreur, un nombre positif si l’ouverture a été réalisée
151 correspondant au handle attribué au fichier ouvert
Lecture

 Après ouverture d’un fichier, on peut:


– Écrire dans le fichier:
 int read(int handle, void *bloc, unsigned taille);
 lit dans le fichier désigné par son handle,
 met ce qui est lu dans le bloc dont on donne l'adresse et
la taille
 Retourne -1 si erreur, et le nombre d’octets lus dans le
cas contraire
– La fin du fichier
 int eof(int handle)
 Renvoie un booléen si on se trouve à la fin (1) ou non
152
(0) du fichier
Lecture séquentielle

 Le fichier peut être utilisé séquentiellement


 le "pointeur de fichier" est toujours placé derrière le bloc
que l'on vient de traiter, pour pouvoir traiter le suivant.
 Pour déplacer le pointeur de fichier en n'importe que
autre endroit, on appelle la fonction :
– long lseek(int handle, long combien, int code);
– déplace le pointeur de fichier de combien octets, à partir de :
 début du fichier si code=0,
 position actuelle si 1,
 fin du fichier si 2.
– La fonction retourne la position atteinte (en nb d'octets), -1L si
153 erreur.
Ecriture

 Pour l’écriture dans un fichier:


– On doit avoir ouvert le fichier avec un mode
permettant d’écrire
– int write(int handle, void *bloc, unsigned taille);
– handle correspond au handle du fichier obtenu
lors de l’ouverture
– bloc correspond aux données à écrire
– taille correspond à la taille des données (en
octets)
154
– Retourne -1 si erreur, et le nombre d’octets écrits
Taille et fermeture

 Lorsque l'on ne se sert plus du fichier, il faut


le fermer (obligatoire pour que le fichier soit
utilisable par le système d'exploitation, entre
autre mise à jour de sa taille) :
– int close(int handle)
– fermeture, rend 0 si ok, -1 si erreur
 la taille d'un fichier (sans déplacer le pointeur
de fichier) peut être obtenue par:
– long filelength(int handle);
155
Exemple (1/2)
 #include <stdio.h>
 #include <fcntl.h>
 int main(int argc,char *argv[]) {
 int source, destination;
 char buffer[1024];
 int taillebloc=1024;
 int nb_lus,nb_ecrits;
 if (argc!=3) {puts("erreur arguments");return(1);}

156
Exemple (2/2)
 if((source=open(argv[1],O_RDONLY|O_BINARY))<0) {
 puts("erreur ouverture");return(2);}
 if((destination=open(argv[2], O_WRONLY| O_CREAT|
O_TRUNC| O_BINARY))<0)
 {puts("erreur ouverture");return(2);}
 do {
 nb_lus=read(source,buffer,taillebloc);
 if (nb_lus>0) nb_ecrits= write(destination,buffer, nb_lus); }
 while ((nb_lus==taillebloc)&&(nb_ecrits>0));
 close(source);
 close(destination);
 return(0); }
157
Fichiers bufférisés

 Les opérations d'entrée / sortie sur ces fichiers se font


par l'intermédiaire d'un "buffer" (bloc en mémoire) géré
automatiquement.
 L’instruction d'écriture n'impliquera pas une écriture
physique sur le disque mais dans le buffer,
 L’écriture sur disque est réalisée uniquement quand le
buffer est plein.
 Les fichiers sont identifiés non par un entier mais par un
pointeur sur une structure FILE (définie par un typedef
dans stdio.h)
158
Ouverture

 L’ouverture d’un fichier bufférisé:


– FILE *fopen(char *nomfic, char *mode)
– mode peut correspondre à:
 r :lecture seule,
 w :écriture, si le fichier existe il est d'abord vidé,
 a :append, écriture à la suite du contenu actuel, création si inexistant,
 r+ :lecture et écriture, le fichier doit exister,
 w+ :lecture et écriture mais effacement au départ du fichier si existant,
 a+ :lecture et écriture, positionnement en fin de fichier si existant,
création sinon
– fopen rend un identificateur (ID) qui nous servira pour accéder au
fichier.
159 – En cas d'erreur, le pointeur NULL est retourné
Lecture / écriture
 Lecture
– int fread(void *bloc, int taille, int nb, FILE *id) :
– lit nb éléments dont on donne la taille unitaire en
octets, dans le fichier désigné par id,
– le résultat est stocké à l'adresse bloc.
– La fonction rend le nombre d'éléments lus (<nb si fin
de fichier), 0 si erreur.
 Ecriture
– int fwrite(void *bloc, int taille, int nb, FILE *id) :
– écriture du bloc sur fichier,
160
Fermeture et transfert

 int fclose(FILE *id) :


– ferme le fichier, en y recopiant le reste du buffer si
nécessaire.
– Cette fonction est obligatoire pour être sûr d'avoir
l'intégralité des données effectivement transférées sur le
disque.
– Retourne 0 si tout s'est bien passé.
 int fflush(FILE *id) :
– transfère effectivement le reste du buffer sur disque, sans
fermer le fichier
– à appeler par exemple avant une instruction qui risque de
161
planter le programme.
Pointeur et fin de fichier

 int fseek(FILE *id, long combien, int mode) :


– déplace le pointeur de fichier de combien octets, à partir de :
 début du fichier (mode=0),
 position actuelle (mode=1) ou
 fin du fichier (mode=2).
– Retourne 0 si tout c'est bien passé.
– Cette fonction n'est utilisable que si l'on connaît la taille des
données dans le fichier (impossible d'aller directement à une
ligne donnée d'un texte si on ne connaît pas la longueur de
chaque ligne).
 int feof(FILE *id) dit si on est en fin de fichier (1) ou
162
non (0).
Données formatées (caractères)

 Niveau caractères:
– char fgetc(FILE *id),
 lit un caractère
– char fputc(char c, FILE *id),
 écrit un caractère
– char ungetc(char c, FILE *id),
 permet de "reculer" d'un caractère,
 équivalent à {fseek(id,-1,1);c=fgetc(id)}

163
Données formatées (chaînes de
caractères)

 char *fgets(char *s, int max, FILE *id) :


– lit une chaîne en s'arrêtant au \n ou à max-1
caractères lus,
– résultat dans la zone pointée par s, et
– retour du pointeur s ou NULL si erreur.
 char fputs(char *s, FILE *id) :
– écrit la chaîne dans le fichier sans ajouter de \n,
– rend le dernier caractère écrit ou EOF si erreur

164
Données formatées (chaînes de
caractères)

 int fprintf(FILE *id, char *format,


listearguments) :
– rend le nb d'octets écrits, ou EOF si erreur.
– Format: O_BINARY ou O_TEXT

 int fscanf(FILE *id, char *format, listeadresses)


– rend le nombre de variables lues et stockées, 0 si
erreur.

165
Exemple avec les structures (1/2)

 Client C[100];
 int nbr=0;
 …
 FILE * fic=fopen("toto.txt", "a");
 for(i=0;i<nbr;i++)
 {
 fwrite(C[i], sizeof(struct client), 1, fic);
 }
 fclose(fic);
166
Exemple avec les structures (1/2)

 fic=fopen("toto.txt", "r");
 Client c1=(Client) malloc(sizeof(struct client));
 j=fread(c1,sizeof(struct client), 1, fic);
 while(j>0)
 {
 printf("Nom: %s, prenom: %s, solde: %d\n",c1->nom,
c1->prenom,c1->solde);
 j=fread(c1,sizeof(struct client), 1, fic);
 }
167  fclose(fic);
MERCI DE VOTRE ATTENTION

168

Você também pode gostar