Telechargé par Youssouph Sadio

LangageC BR

publicité
B. Rouzeyre, Polytech'ERII
Langage C
Types de données et représentation
Représentation des algorithmes
Organigramme : façon graphique de représenter le
déroulement d'un calcul
1. Action de base
Action 1
2. Séquencement
Action 1
Action 2
3. Bloc et niveau de détail
Action 1
Action 2
Action 11
Action 12
Organigramme
4. Sélection
Σ Evi = 1 et Evi.Evj = 0
Non structuré
Structuré
Action 1
Critère ?
Action 1
Ev.1
Ev.2
Ev.3
Action
21
Critère ?
Action 22
Ev.1
Action 23
Ev.2
Action
21
Action 22
Ev.3
Action 23
Action 3
Action 3
Se lit de haut en bas et de gauche à droite
Se lit en suivant les fils
Organigramme
4. Cas particulier : alternative
Non structuré
Structuré
Action 1
Action 1
Vrai
Action
21
Critère ?
Critère ?
Vrai
Faux
Action 22
Faux
Action
21
Action 22
Action 3
Action 3
Organigramme
4. Cas particulier : alternative sans action
Non structuré
Structuré
Action 1
Action 1
Vrai
Action
21
Critère ?
Critère ?
Vrai
Faux
Faux
Action
21
Action 3
Action 3
Organigramme
5. Itérations : exprime le fait que l'on répète une action
5.1 "Tant que"
Action 1
Action 1
Vrai
Tant que
critère vrai
Action 2
Critère
Action 2
Action 3
1 – On évalue le critère
2 – s'il est vrai on effectue l'action 2, retour en 1
2 bis – s'il est faux on passe à la suite
Remarque : éventuellement l'action 2 n'est pas exécutée
Faux
Action 3
Organigramme
5. Itérations :
5.2 "Jusqu'à ce que"
Action 1
Action 1
Jusqu'à ce que
critère soit vrai
Action 2
Action 2
Faux
Critère
Vrai
Action 3
Action 3
1 – On exécute l'action 2
2 – s'il est faux, retour en 1
2 bis – s'il est vrai, on passe à la suite
Remarque : l'action 2 est exécutée au moins une fois
Organigramme
5. Itérations :
5.3 "Pour tout e ε E"
Action 1
Action 1
Vrai
Pour chaque e
de E
Action 2
eεE?
Action 2
Action 3
e <- valeur
suivante de E
l'action 2 est exécutée autant de fois qu'il y a d'éléments dans E.
Remarque : si E est vide, Action 2 n'est pas exécutée.
Faux
Action 3
Organigramme : conseil
-
utiliser que les primitives précédentes
intérêt : traduction (presque) directe en C
ne pas faire de spaghetti :
- traduction difficile
- debug impossible
- résultat incompréhensible
- => 0 à l'examen
Action 1
Vrai
Action 2
e <- valeur
suivante de E
eεE?
Faux
Action 3
Organigramme : exemple
x = premier
S=0
x = premier
x pair ?
fin = faux
S = S+ x
x dernier ?
Tant que x pair
et fin = faux
S = S+ x
x dernier ?
Imprimer S
Imprimer S
fin = v
Vrai
Faux
x= suivant(x)
x=
suivant(x)
KO
S=0
OK
Codage d'un algorithme en C
• Les actions ou "instructions" en langage C ne sont en fait que des
opérations élémentaires (+,-, * etc…) sur des données unitaires.
• Chaque instruction est suivie d'un ;
• Le début d'un bloc est signalé par un { , la fin d'un bloc par }. Un bloc
devient l'équivalent syntaxique d'une instruction.
• Ex :
x=1
Calcul
y=2
z = x+y
imprimer z
{
x=1;
y=2;
z=x+y;
printf("%d",z);
} ;
optionnel
Codage d'un algorithme en C
Alternative
if (expression) action1 ;
else action2 ;
fin de l'instruction if
calcul
Vrai
"calcul juste"
z = z+10
z=3 ?
Faux
Imprimer z
"problème"
…
if (z == 3) {
printf("calcul juste");
z=z+10;
} ;
else printf ("problème");
printf ("%d",z);
…
Codage d'un algorithme en C
Cas particuliers de alternative
calcul
…
if (z == 3) ;
else printf("problème);
printf ("%d",z);
…
Vrai
z=3 ?
Faux
Imprimer z
"problème"
Codage d'un algorithme en C
Cas particuliers de alternative
calcul
Vrai
"calcul juste"
…
if (z == 3) {
printf("calcul juste");
z=z+10;
} ;
else;
printf ("%d",z);
…
z = z+10
z=3 ?
ou bien
Faux
Imprimer z
…
if (z == 3) {
printf("calcul juste");
z=z+10;
};
printf ("%d",z);
…
Codage d'un algorithme en C
Sélection multiple : en C, la sélection ne peut être qu'une comparaison à des
valeurs de constante entière ou caractère (si besoin passer à plusieurs if imbriqués)
Action 1
z?
z=3
z=4
autres
"ok"
"PB1"
"PB2"
Action 3
…
switch (z) {
case 3 : printf("ok"); break;
case 4 : printf("PB1"); break;
default : "printf ("PB2");
};
…
Codage d'un algorithme en C
Structures itératives. Exemple, on veut imprimer tous les nombres de 1 à 10
TANT QUE : while (condition) action;
x=1
imprimer x
Tant que x ≤ 10
x=x+1
x=1;
while (x <=10) {
printf("%d\n",x);
x=x+1;
};
…
JUSQU'À CE QUE : do action while (condition);
x=1;
do {
x=1
imprimer x
Jusqu'à ce que x >11
x=x+1
printf("%d\n",x);
x=x+1;
}
while (x <= 11);
…
Codage d'un algorithme en C
POUR
Pour x E {1,2,…,10}
imprimer x
for(x=1; x≤10; x=x+1)
printf("%d\n",x);
équivalent TANT QUE
x=1
imprimer x
Tant que x ≤ 10
x=x+1
x=1;
while (x <=10) {
printf("%d\n",x);
x=x+1;
};
…
Compilateurs
• Sur le web
– ideone.com (pour le
début)
• En TP
– Visual C++
Types de données
• 3 types de base
caractères
ex : 'a', '1', '\n'
entier relatifs
ex : 0 , -12, 328
réel
3.14, 2.3 e+4
• Remarques :
Pas de booléen (vrai, faux)
Pas de type chaînes de caractères prédéfini
Type caractère
•
Caractère : Symboles alphanumériques (a,z,!,1,9) + caractères spéciaux (retour à la
ligne, beep, etc..)
•
Un caractère est représenté sur un octet (8 bits) suivant la table ASCII (American
Standard Code for Information Interchange)
•
ex : 'a' = 9710 = 6116 = 0110 00012
•
Table ASCII
ex : code ASCII du 'A' = 65
'A'<'B'<……..< 'Z'
'0'<'1'<'2'<…..<'9'
'a'<'b'<……….<'z'
•
Déclaration de variable de type caractère
char c;
c = 'a';
•
Constante de type caractère
#define caractère_a 'a'
Table ASCII
• Remarques :
les chiffres sont codés suivant un ordre croissant (48 à 57)
idem pour les lettres (65 à 90, 97 à 122)
code des majuscules est inférieur au code des majuscules
(différence constante = 32)
les codes supérieurs à 128 dépendent de la langue :
é, ö , ä, æ, œ etc…
• Déclaration d'une variable caractère :
char c;
c='a';
…..
Type caractère
• Caractères spéciaux (retour à la ligne, tabulation etc..)
• Exemple :
retour à la ligne = CR = code ASCII 13
char retour;
retour = 13; ou bien retour = '\n';
• Conventions
\n : retour à la ligne
\t : tabulation
\f : nouvelle page
\' : apostrophe
\0 : caractère nul (indique la fin d'une chaîne de caractères)
Les entiers : entiers naturels
•
Codage sur 2 ou 4 octets suivant le calculateur
n 1
x   xi 2i , xi  0,1, n  16 ou 32
i 0
•
Sur deux octets on peut coder les nombres de 0 à 216-1 (0 à 65535)
•
Nombre représenté en base 2, les bits sont rangés dans des cellules
correspondant à leur poids, on complète à gauche par des 0
•
Exemple :
13 = 8 + 4 +1 = 1*23+1*22+0*21+1*20
000000000000001101
•
Déclaration d'une variable entier naturel x
unsigned int x;
short unsigned int x; (on force sur 16 bits)
long unsigned int x; (on force sur 32 bits)
Type entier relatif
• Implantation sur 2 ou 4 octets suivant le compilateur
• Représentation sur n bits : codage complément à 2
x   xn 1 2
• Déclarations
int a,b;
….
a = 1;
….
b= 3;
n 1
n2
  xi 2 , xi  0,1
i
i 0
Type entier relatif
• Codage complément à 2
n2
x   xn 1 2 n 1   xi 2i , xi  0,1
i 0
• Si xn-1 = 0 : nombre positif, xn-1 = 1 : nombre négatif
• Exemple sur 16 bits
+5 = 1 * 22 + 0 * 21 + 1 * 20 = 0000 0000 0000 0101
-3 = -32768 + 32765
= -215+ 214 + 213 + 212 + 211 + 210 + 29 + 28 + 27 + 26 + 25 + 24 + 23 + 1.22 +
0. 21 + 1.20
= 1111 1111 1111 1101
• Sur 16 bits (2 octets)
-32768  x  32767
• Sur 32 bits
-2147483648  x  2147483647
Complément à 2
• Représentation non symétrique : le plus petit nombre n'a
pas d'opposé : sur n bits
• le plus grand entier positif est 2n-1-1
• le plus petit entier négatif est -2n-1
• exemple sur 3 bits :
000
111
-1
110
1
-2
2
010
3
-3
101
001
0
-4
100
011
Codage complément à 2
• Remarques
1/ Complément à 2 de x = Complément à 1 de x + 1
représentation de –3 ?
3
= 0000 0000 0000 0011
c1(3) = 1111 1111 1111 1100
+1
= 0000 0000 0000 0001
c1(3) +1= 1111 1111 1111 1101
2/ Représentation 16 bits = > 32 bits
x >0 = 0000 0000 0000 0011 => x = 0000 0000 0000 0000 0000 0000 0000 0011
x <0 = 1111 1111 1111 1101 => x = 1111 1111 1111 1111 1111 1111 1111 1101
• Extensions
int 2 ou 4 octets ? problème de portabilité
short int x; 2 octets
long int x ; 4 octets
unsigned int x ; bit de signe utilisé pour la valeur
unsigned short int x ;
unsigned long int x ;
Type réel
• Déclaration
float x,y;
x = 3.14;
y = 2.0 e+3;
• Implantation sur 4 octets
• Représentation suivant le compilateur
en général mantisse exposant (norme IEEE)
• 10-38 < x < 1038
• Extension :
double x; x est codé sur 8 octets
Nombre réels
• Codage des réels : virgule flottante, Norme IEEE
• flottant stocké sous la forme M * BE
M : Mantisse ; B : Base ; E : Exposant
•
•
exemple : 123 . 103 = 123 000
Représentation IEEE 754 (signe 1 bit, mantisse et
exposant sur 32 ou 64 bits pour simple et double
précision)
• SM : signe de la mantisse : 1 bit
• Eb : exposant biaisé : 8 ou 11 bits
• M : Mantisse : 23 ou 52 bits
SM
Eb
M
Mantisse et exposant
• Signe : bit de poids fort (0 = + ; 1 = -)
• Exposant
placé avant la mantisse pour simplifier les comparaisons (pour ceci il
ne doit pas être représenté en complément à deux : -1 > 2)
sur 8 bits : 0..256
sans signe mais biaisé de 127 (simple précision) :
Eb = 1 ⇒ E = 1 – 127 = -126
Eb = 254 ⇒ E = 254 – 127 = 127
les exposants 255 (erreur) et 0 (nb dénormalisé) sont interdits
•
Mantisse
normalisée : bit de poids fort n’est pas 0 et un seul chiffre avant la
virgule
•
ex : 3,2510 = 11,012 = 1,101 * 21
Virgule Flottante
SM
Eb
M
f1,f2,……fn
• Comme le bit de poids fort de la mantisse est nécessairement 1 : on
ne l’indique pas (gaspillage de place), il est implicite
• Mantisse
partie fractionnaire = f1f2 …fn ⇒ m = 1,f1f2…fn
nombre x = (-1)SM * 1,M * 2Eb-127
•
Exemple
x = (-2,5)10 = -1,01*212
SM = 1 ;
E= 1 => Eb= 128 = 1000 0000 ;
m=1,01 => M =010…….0
• Déclaration de variables réelles :
float x;
double y;
Variables
• Variable : élément de mémorisation élémentaire
• Toutes les variables doivent être déclarées suivant un type
int a, b;
ou bien
int a;
int b;
float x;
char caractere;
• Identificateur de variables (noms)
Le premier caractères doit être une lettre
Les autres caractères sont les lettres (majuscules et minuscules), les chiffres
et le caractère _
Majuscule / minuscule significatifs
• Exemples :
Pi, pi3_14, a2B3 : corrects
2x, i-E
: incorrects
A1  a1
Variables
• Exemple :
char a;
int un;
a = 'a';
un =1;
a = '1';
• Initialisation des variables
à l'exécution :
int i;
….
i = 0;
à la compilation
int i = 0;
Conversion de type
• Conversion explicite : ( type ) expression
Exemple :
int a; float x; char c;
a= 2;
x= (float) a;
x= 2.3;
a= (int) (x+1);
a = 98;
c = (char) a; -> c='b'
• Conversion implicite
Exemple :
int a; float x; char c;
a= 2;
x= a;
x= 2.3;
a= x+1;
a = 98;
c = a; -> c='b'
Conversion de types
• Exemples :
char c; int i; float f;
// conversion entier vers char.
c= 98; // implicite : c prend le code ASCII 98 c-à-d ’b'
c = (char) 98; // explicite plus propre
// char vers entier
i= 'a' ; // i prend la valeur 97
i= (int) 'a' ; //plus propre
// entier vers réel
f= 3; // f prend la valeur 3.0;
f=(float) 3; //+ propre
//réel vers entier, attention : troncature
i = 3.4; // i prend la valeur 3
i= -3.7; // i prend la valeur -3
i = (int) 3.4; // + propre
Conversion de types : application
• Passer au caractère suivant
char c;
c ='a';
c = c+1; // calcul fait en entier puis résultat converti en char
c = (char) ((int) c+1) ; //+ propre
• Conversions majuscule<-> minuscule
char c;
c='t';
// conversion en Majuscule
c=c-32; // c contient 'T'
ou mieux
c=c-('a'-'A');
c=c+1; // c contient 'U'
// conversion en minuscule
c=c+32;
ou c=c+('a'-'A')
// conversion en Majuscule
if ((c >= 'a') && (c <= 'z')) c = c-('a'-'A');
Tableaux
• Lorsque on veut mémoriser plusieurs données de même
type, on peut utiliser un tableau c-à-d on regroupe sous un
même nom plusieurs informations
• Exemple de déclaration d'un tableau d'entiers
int tab[100];
int : type des éléments du tableau
tab : identificateur (nom du tableau)
100 : nombre d'éléments du tableau (dimension)
tab peut recevoir 100 entiers indicés de 0 à 99 (attention !)
le premier est tab[0]
le second tab[1]
..
le dernier tab [99]
Tableaux
• Utilisation
chaque élément du tableau est accessible par un indice qui doit être
de type entier, quelque soit le type des éléments du tableau
exemples :
int i ;
tab[2] 3eme élément du tableau
tab[2+3] 6eme élément du tableau
tab[i]
i+1eme élément du tableau
• Exemples :
stocker les 100 premiers nombres pairs : 0,2,4,...,196,198
int i, t[100];
for (i=0; i < 100; i=i+1)
t[i]= 2*i;
Tableaux
• Remarques:
1/ chaque élément du tableau s'utilise comme une variable
tab[3] = 2;
2/ le nombre maximum d'éléments du tableau (dimension)
1/ doit être fixé à la compilation
2/ ne peut être modifié pendant l'exécution
3/ Pas d'opérations sur les tableaux en tant que tels
Parcours des éléments d’un tableau
Parcours du premier au dernier
int i; /* l’indice de balayage doit être un entier */
float t[100]; /* le type du tableau est quelconque, ici réel */
for (i=0; i < 100; i=i+1) // ou bien for (i=0; i <= 99; i=i+1)
t[i]= …….;
Parcours du dernier au premier
int i;
float t[100];
for (i=99; i >= 0; i=i-1)
t[i]= …….;
La dimension
Bonne pratique de programmation
int i;
int t[100];
for (i=0; i < 100; i=i+1)
t[i]= 100;
Pb : modification du pgm, changement de la taille du tableau
malaisée
#define TAILLE 100
int i;
int t[TAILLE];
for (i=0; i < TAILLE; i=i+1)
t[i]= 100;
Il suffit de changer TAILLE
Exemples
Point de l'espace
1ere solution :
float x,y,z;
2eme solution
float pt[3];
pt[0] pour x, pt[1] pour y, pt[2] pour z
Mémorisation des 100 premiers nombres pairs et impairs:
int pairs[100], impairs[100];
int i;
for (i=0; i<100;i=i+1) {
pairs[i]=2*i;
impairs[i]=2*i+1;
}
Chaînes de caractères
• En c, pas de type prédéfini chaîne de caractères. En
pratique on utilise des tableaux de caractères.
• Convention : le dernier caractère utile est suivi du
caractère \0 (de code ascii 0)
• Exemples :
char t[10]; (9 caractères max, puisque une case réservée pour \0;
'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 0
strcpy(t,"abcdefghi")
chaque lettre est accessible par l'indice
char t[12];
strcpy(t,"abcdefghi"); 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 0 ? ?
• Initialisation char t[10]= "abcdefghi"; ou char t[]= "abcdefghi";
• Constante chaîne de caractères
#define ceciestuntexte "azssddqsdqsd"
Définitions de type utilisateur
typedef int entier; (entier est maintenant un type)
entier i;
typedef int tableaude20[20];
tableaude20 t;
 int t[20];
Les expressions en langage C
Expressions : introduction
• Remarque : expression  instruction
• une instruction indique à l'ordinateur de mener une action
• expression = élément syntaxique
• Expressions :
variable ou constante, ex : x, 3
constituées à l'aide d'opérateurs : x+ y
conversion de type, opérateurs arithmétiques, de taille,
relationnels et logiques, affectation, bit-à-bit, conditionnels,
adresse
Expressions
• Une expression représente une donnée élémentaire :
constante, variable, un élément de tableau, la référence à
une fonction ou à une valeur, le résultat d'un calcul etc ….
• Exemples
3
a+b
x=y
c = a+b
x <= y
x == y
i++
sin(3.14)
• Toute expression a une valeur
Opérateurs arithmétiques
• Opérateurs bi-opérandes
+,* , / , % (modulo)
Les opérandes doivent être des valeurs numériques.
entier opérateur entier -> résultat entier
réel opérateur réel -> résultat réel
entier opérateur réel -> résultat réel
• Exemples
int a,b;
a=10; b= 3
a+b
13
a-b
7
a*b
30
a/b
3 (division euclidienne)
a%b
1
float a,b;
a=12.6; b= 3.0
a+b
13.6
a-b
9.6
a*b
37.8
a/b
4.2 (division réelle)
a%b
erreur de syntaxe
Opérateurs arithmétiques
• Opérateur % :
- int a; flloat x;
(a+x) % 4 incorrect. ((int) (a+x))%4 correct
- si l'un des opérandes est négatif, le résultat est négatif.
• Si l'un des opérandes est de type caractère, c'est la valeur
du code ASCII qui est prise (conversion implicite char vers
Exemple :
int ou float)
char c = 'a';
c = c+1; => c = 'b'
mécanisme :
c+1 = 98 + 1 =99
c = code ascii 99 = 'c'
• Conversion majuscule  minuscule
Exemple :
char c ;
if (c >= 'a' && c <='z') c = c-32 (ou bien c = c + 'A' –'a')
if (c >= 'A' && c <='Z') c = c+32 (ou bien c = c - 'A' +'a')
Opérateurs arithmétiques
• Opérateurs unaires (un opérande)
a/ signe : + , exemple : a = -a;
b/ incrémentation, décrémentation : ++ (+1) , -- (-1)
exemple :
int i =1;
++i;
printf("%d",i) ; -> 2;
Syntaxes : ++i ou i++
++i : la valeur de i est d'abord incrémenté, la valeur résultat est
utilisée dans l'expression courante
i++ : la valeur courante de i est utilisée dans l'expression courante,
puis i est incrémenté
++ et -• Exemples
i=1;
printf("i= %d\n",i); -> i=1
printf("i= %d\n",++i); -> i=2
printf("i= %d\n",i); -> i=2
i=1;
printf("i= %d\n",i); -> i=1
printf("i= %d\n",i++); -> i=1
printf("i= %d\n",i); -> i=2
• Conclusions :
1/ appendre la règle (pour comprendre des programmes)
2/ à n'utiliser que lorsque il n'y a pas d'ambiguïté :
x=y+z++; // à éviter
x++; // pas de risque
Opérateurs d'affectation
• Affectation simple
syntaxe : variable = expression
la valeur de l'expression est stockée dans la mémoire à l'endroit
réservé pour la variable
Exemples :
a = 2; b=1; c=0;
a = b+c;
a = b && c;
la valeur de l'expression vaut la valeur affectée
Attention : affectation et test d'égalité
if (a =1) instruction1; else instruction2;
L'instruction1 est toujours déclenchée.
a = b = 3;
(évaluation de droite à gauche)
Opérateurs d'affectation
• Affectation et opération : +=, -=, *=, /=, %=,<<= , >>=, &=,
|=, ^=
Syntaxe :
équivalent à :
variable opérateur expression
variable = variable opérateur expression
Exemple :
int i;
i= 3;
i+=2;
// même chose que i=i+2;
printf("%d\n",i); -> 5
Opérateurs relationnels et logiques
•
Valeur logique :
0 : faux
 0 : vrai
exemple : if (3) traitement1 ; else traitement 2;
équivalent à : traitement1;
•
•
Relationnels : >= , > , == , <, <= , !=
La valeur de l'expression est 1 si l'expression est vraie , 0 si elle est fausse
Exemple : 2 < 3 vaut 1 , 2 > 3 vaut 0
•
Attention à la confusion : test d'égalité == et l'affectation =
ex : if (x=0) traitement 1; // au lieu de x==0
else traitement 2;
Conséquence: En cas d'erreur, non seulement le traitement 1 ne sera jamais exécuté mais en plus
x vaudra 0 quelle que soit sa valeur initiale
•
Logiques : && "et" logique , || "ou" logique, ! "non" logique
Dans l'évaluation de l'expression, 0 est considéré comme la valeur logique "faux", toute valeur  0
comme la valeur logique "vraie"
La valeur de l'expression est 1 ou 0
Exemples:
2 && 0 vaut 0 et donc est faux
2 || 0 vaut 1 et donc est vrai
!0 vaut 1
!4 vaut 0
Opérateurs bit à bit
•
•
•
•
•
Opèrent sur les représentations des valeurs
& et , | ou, ^ ou-exclusif, ~ complément à 1 ,
<< décalage à gauche, >> décalage à droite,
Attention : &  &&
Exemples
5
20
5 & 20
5 | 20
5 ^ 20
~5
0000 0000 0000 0101
0000 0000 0001 0100
0000 0000 0000 0100 => 5 & 20 => 4
0000 0000 0001 0101 => 5 | 20 => 21
0000 0000 0001 0001 => 5 ^ 20 => 17
1111 1111 1111 1010 => -6
• Affectation/bit-à-bit : &=, |=, ^=, ~=
Décalages
• Décalages
• à gauche a << b : a est décalé à gauche de b bits (les bits ajoutés
valent 0)
5 << 2
0000 0000 0001 0100
20
un décalage d'une position à gauche correspond à une multiplication
par 2
• à droite a >>b : a est décalé à droite de b bits (les bits insérés
valent le bit de poids fort)
14
0000 0000 0000 1110
14 >> 2
0000 0000 0000 0011
3
-6
1111 1111 1111 1010
-6 >> 1
1111 1111 1111 1101
-3
un décalage d'une position à droite correspond à une division par 2
(en respectant le signe)
Opérateur conditionnel
• Syntaxe
expression1 ? expression2 : expression3
à peu près équivalent à :
if (expression1) expression2; else expression3;
• Exemple :
maximum = (x>y) ? x : y;
if (x>y) maximum =x ; else maximum = y;
• Conseil : ne pas utiliser (peu clair)
Opérateurs d'adressage
• Adresse de : &
Syntaxe : &variable , donne l'adresse mémoire de la variable
Exemple :
int i,adr;
adr = &i;
ne pas confondre avec le "et" bit à bit
• Dont l'adresse est : *
Syntaxe *expression : donne le mot mémoire dont l'adresse est
donnée par l'expression
Exemple :
int i,adr;
i=1;
adr = &i;
printf("%d", *adr); -> 1
Opérateur de taille : sizeof
• Donne la taille de l'implantation
• 2 syntaxes
1/ sizeof expression
exemple :
int i,j ;
j= sizeof i; -> 2 ou 4 (octets)
2/ sizeof (type)
exmples :
typedef char tab[100];
tab t;
int n;
n = sizeof(int), -> 2 ou 4 (octets)
n = sizeof(tab) -> 100 (char)
Opérateurs divers
• ( ) : force l'ordre des calculs
ex : 1 + 2 * 3 -> 7
(1+2) * 3 -> 9
• [ ] pour les tableaux
t[2] équivalent à *(t+2)
• -> et . (opérateurs sur structures, + tard)
Priorité des opérateurs
Priorité
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
Opérateurs
() [ ] -> .
++ -~
!
&*
(type)
+*/%
+<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += -= *= /= %=
>>= <<= &= ^= |=
,
Description
opérateurs d'adressage
incrément/décrément
complément à un (bit à bit)
non unaire
adresse et valeur (pointeurs)
conversion de type (cast)
plus/moins unaire (signe)
opérations arithmétiques
""
décalage bit à bit
opérateur relationnels
""
et bit à bit
ou exclusif bit à bit
ou bit à bit
et logique
ou logique
conditionnel
Associativité
->
assignations
<-
séparateur
->
<-
->
->
->
->
->
->
->
->
->
->
<-
Priorité des opérateurs
a – b /c *d
(a-b) / (c-d)
i = j = k = 0;
a=1; b=4;
! --a == ++ !b
!0 == ++0
1 == 1
1
Priorité des opérateurs (exercices)
main(){
int x, y , z;
x = 2;
x += 3 + 2; printf("%d\n",x);
x -= y = z = 4; printf("%d%d%d\n",x,y,z);
x = y == z; printf("%d%d%d\n",x,y,z);
x == (y = z); printf("%d%d%d\n",x,y,z);
x = 3; y =2 ; z = 1;
x = x && y || z ; printf("%d\n", x);
printf ("%d\n", x || ! y && z);
x = y = 0;
z = x ++ -1; printf ("%d, %d\n", x, z);
z += -x ++ + ++ y; printf ("%d, %d\n", x, z);
x =1 ; y =1;
printf("%d\n", ! x | x);
printf("%d\n", ~ x | x);
printf("%d\n", x ^ x);
x <<= 3 ; printf("%d\n", x);
y <<= 3 ; printf("%d\n", y);
y >>= 3 ; printf("%d\n", y);
Priorité des opérateurs (exercices)
x =0 ; y =0; z=0;
x+=y+=z;
printf("%d\n", x < y ? y : x) ;
printf("%d\n", x < y ? x++ : y++) ;
printf("%d, %d\n", x , y);
printf("%d\n", z += x < y ? x++ : y++) ;
printf("%d, %d\n", y , z);
x = 3; y = z = 4;
printf("%d\n",( z >= y >= x) ? 1 : 0) ;
printf("%d\n", z >= y && y >= x ) ;
x = y = z = 0;
}
ENTREES / SORTIES
Les entrées/sorties (lecture/écriture)
• Lecture clavier
2 fonctions de base : getchar () et scanf()
Elles peuvent être appelées soit indépendamment soit au sein
d'expressions
Exemples
getchar();
while (c==getchar()) {…..};
getchar()
• getchar() : sert à la lecture de caractères isolés
• la valeur de getchar() est le code ascii du caractère lu
• utilisation
char c;
c = getchar();
• Exemple : on veut lire et mémoriser 2 caractères donnés
sur 2 lignes différentes
char c1,c2;
c1 = getchar()
getchar ();
c2 = getchar ()
// acquiert le 1er caractère
// filtre le <cr>, on ne mémorise pas la valeur lue
// acquiert le 2ème caractère
scanf ()
• Sert à la lecture de données et convertit la succession de
caractères donnés en entiers, flottants, caractères, chaîne
de caractères
• Syntaxe :
 scanf (format,arg1,arg2,……,argn)
 le nombre d'arguments est quelconque
 arg1, arg2,……, argn sont les adresses des variables dans
lesquelles on stocke les valeurs lues
 variable simple (entier, caractère, flottant) : &v
 chaîne de caractères = tableau : v
 le format est une chaîne de caractères précisant le type des
arguments afin de convertir la suite de caractères lus dans les
arguments
Scanf : format
• Format
chaîne de caractères composée de caractères % suivis d'une lettre et
éventuellement séparés par des blancs
la lettre indique le type de conversion à effectuer
exemple :
int i; float x;
scanf("%d %f", &i,&x);
le %d indique que le premier argument est un entier
le %f indique que le second est un réel
réponse : 23☐12.6
23 est converti en entier et stocké dans i
12.6 est stocké en flottant et stocké dans x
Scanf : format
• caractères de conversion
c : donnée de type caractère simple
d : donnée de type entier relatif
f : donnée de type flottant
e : donnée de type flottant en notation exponentielle
x : donnée de type entier hexadécimal
s : donnée de type chaîne de caractères (tabl. de char terminé par \0)
• Exemple
char t[20];
int i ; float x;
scanf ("%s %d %f", t,&i,&x);
réponses :
1/ abcde 123 0.05
2/ abcde 123 
0.05
3/ abcde
123 
0.05
Scanf : rôle des caractères ☐, , tabulation,
dans les réponses
• Dans les réponses
☐, , tabulation servent de délimiteurs pour les valeurs numériques et les
chaînes de caractères (pas pour les caractères)
• Exemples
scanf ("%d%f",&i,&x);
rep1 : 123 ☐☐☐☐456 
rep2 : 123456 
i = 123 , x = 456.0
i = 123456 , x : pas encore lu (en attente)
scanf("%s%d",ch,&i);
rep : abc 12
ch = "abc" , i=12
scanf ("%c%c",&c1,&c2);
rep1 : ab
rep2 : a☐b
c1= 'a' , c2 = 'b'
c1= 'a' , c2 = ☐
scanf ("%c%c%c",&c1,&c2,&c3);
rep1 : ab
c1= 'a' , c2 = 'b', c3= 
rep2 : ab
c1= 'a' , c2 = 'b'
c
c3 = 
Scanf : rôle des caractères ☐ et tabulation,
dans la chaîne de format
• Lecture de valeurs numériques : aucun rôle
scanf ("%d%f",&i,&x)  scanf ("%d☐%f",&i,&x)
• Lecture de caractères : indique de sauter les ☐, tab et 
• Exemples
scanf ("%c%c%c",&c1,&c2,&c3);
rep1 : abc
c1= 'a' , c2 = 'b', c3= 'c'
rep2 : a☐b☐c
c1= 'a' , c2 = '☐', c3= 'b'
scanf ("%c☐%c☐%c",&c1,&c2,&c3);
rep1 : abc
c1= 'a' , c2 = 'b', c3= 'c'
rep2 : a☐b☐c
c1= 'a' , c2 = 'b', c3= 'c'
rep2 : a☐b 
c
c1= 'a' , c2 = 'b', c3= 'c'
Scanf : compléments
• Nombre de caractères lus
faire précéder le caractère de format du nombre de caractères (max)
désiré
Exemples
int i,j,k;
scanf("%3d %3d %3d",&i,&j,&k);
rep1 : 1☐2☐3
i=1 j=2 k=3
rep2 : 123☐456☐789
i=123 j=456 k=789
rep3 : 123456789
i=123 j=456 k=789
rep4 :1234☐5678☐9
i=123 j=4 k=567
int i; float x;char c;
scanf("%3d☐%5f☐%c,&i,&x,&c);
rep : 10☐234.567☐t
i=10 x=234.5 c='6'
Scanf : compléments
• Lecture d'une chaîne de caractères
char ch[50];
scanf("%s",ch)
rep : abcdefghi
// pas de &
'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 0
?
Le caractère 0 de fin de chaîne est ajouté automatiquement
Exercice : faire l'équivalent de scanf("%s",ch) à l'aide de getchar()
• Saut conditionnel de caractères : %*d, %*f
permet de sauter des données correspondantes dans la réponse
exemple :
int i,j; char c;
scanf("%d ☐ %*d ☐ %c",&i,&c);
rep1 : 12☐34x
i=12
c='x'
rep2 : 12☐x
i=12
c='x'
?
scanf : compléments
• Filtre sur chaînes de caractères
[ caractères admissibles] ou [^caractères non admissibles]
exemples
char ch[100];
scanf("%[0123456789]",ch);
rep : 32ab48
ch="32"
scanf("%[^0123456789]",ch);
rep : 32ab48
ch="ab"
raccourcis : [0123456789] ou [0-9]
[abcdefg]
ou [a-g]
Ecriture
• 2 fonctions de base : putchar() et printf()
• putchar(caractère)
• Exemple
char c;
c='a';
putchar ( c );
putchar ('\n');
putchar('b');
Affichage :
a
b
Printf()
• Format
printf(format,arg1,arg2,…..,argn);
les arguments sont des valeurs d'expression à imprimer
le format donne le texte mort et le mode de décodage des arguments
le format est une chaîne de caractères
• Exemples :
printf("bon"); printf("jour");printf("\n")
i=j=1;
printf("i=%d\n",i)
printf("%d%d%d\n",i,j,i+j);
printf("%d☐%d☐%d☐%d\n",i,j,i+j,sqrt(i));
x=3.0;
printf("%f☐%d\n",x,i);
printf("%d\n%d\n",i,i+j);
bonjour
i=1
112
1☐1☐2☐1
3.000000☐1
1
2
Printf
• Caractères spéciaux de format
%d : imprime les entiers sur le nombre de caractères nécessaires
%f : imprime les réels avec 6 chiffres de partie décimale
%e : imprime les réels en notation exponentielle
%c : imprime un caractère
%s : imprime une chaîne de caractères jusqu'à rencontrer le
caractère de fin de chaine 0 (erreur si absent)
…..
\n : saut à la ligne
\t : tabulation
\p : saut à la page
….
Printf : mises en forme
• Forçage du nombre de caractères
entiers :
%5d
%-5d
réels :
%10f
chiffres
%-10f
l'entier est imprimé sur 5 caractères au moins (blancs)
avec cadrage à droite
l'entier est imprimé sur 5 caractères au moins (blancs)
avec cadrage à gauche
le réel est imprimé sur 10 caractères (en tout) avec 6
en partie décimale (cadrage à droite)
idem + cadrage à gauche
limitation de la partie décimale
%20.3f le réel est imprimé sur 20 caractères (en tout) avec 3
chiffres en partie décimale
Printf : format variable
• ("%*d",n,i)
n donne le nombre de caractères pour i
• ("%*.3f,n,x)
n donne le nombre total de caractères pour x
• ("%*.*f,n,m,x)
n donne le nombre de total caractères
m donne le nombre de caractères pour la partie décimale
• Exemples
Printf
• La valeur de retour du printf est le nombre de caractères
écrits et une valeur négative si il y a eu un pb.
• Exemple :
int a,x;
a=32;
x = printf ("%d\n",a);
printf ("%d\n",x); -> 3
Autres fonctions d'E/S
• Beaucoup d'autre fonctions d'E/S
voir "stdio.h"
gets, puts permettent de lire et d'écrire des chaînes de caractères
contenant des espaces (rappel : scanf ("%s",….) les espaces sont
des délimiteurs)
exemple :
#include "stdioh"
main()
{
char ligne[80];
gets(ligne);
puts(ligne);
}
Lecture/Ecriture dans fichiers
• Principe : identique aux lecture/écriture sur clavier/écran
En fait, le clavier et l'écran sont des fichiers particuliers
• Il faut simplement en plus "ouvrir" le fichier c-à-d
- l'associer à un fichier physique (sur disque)
- l'associer à un variable interne du pgm
• Un fichier peut être
- soit lu (read)
- soit (ré-)écrit (write)
- soit écrit à la fin (append)
bande magnétique
• Déclaration :
FILE * variable-interne
ex : FILE * f;
// f est une variable spéciale de type "fichier"
Ouverture de fichier : fopen
• variable = fopen("nom du fichier sur disque",mode d'ouverture)
mode d'ouverture :
syntaxe : chaîne de caractères
1er caractère :
'r' = read = lecture
'w' = write = écriture
'a' = append = écriture à la fin
fopen renvoie la valeur NULL (=0) si le pb sur le fichier physique
• Exemple
variable interne
FILE * f;
f = fopen ("c:\texte.txt","r1234");
fichier
if (f==NULL) printf ("le fichier est absent\n");
else printf ("ok\n");
physique
Fermeture du fichier : fclose
• Supprime l'association fichier physique-variable interne
• La variable interne peut être associée à un autre fichier
physique
• Syntaxe
fclose (variable interne)
• Exemple :
FILE * f;
f= fopen (fichier1, "r");
….
fclose (f);
…
f= fopen (fichier2,"w");
…
Lecture dans fichier
• Lecture :
fgetc() et getc() ↔ getchar()
exemple
c=fgetc(f);
fscanf() ↔ scanf()
exemple
fscanf(f,"%d",i) le 1er argument est le variable interne fichier
• Le caractère EOF indique la fin de fichier
• Ecriture
fputc () ↔ putchar() // c= fgetc (f)
fprintf() ↔ printf()
// fprintf(f,"……",…..);
Exercice
• Afficher à l'écran le contenu d'un fichier (idem commande
unix ou MSDOS type)
main() {
FILE * monfichier;
char sur_disque[100]; char c;
/* acquisition du nom */
scanf("%s",sur_disque);
/*ouverture*/
monfichier=fopen(sur_disque,"r");
if (monfichier==NULL) printf("erreur\n");
else
{ // lecture affichage
while ((c=getc(monfichier))!=EOF) printf("%c",c);
fclose (monfichier);
}
}
Lecture/ecriture dans chaines de caractères
•
sprintf (char * s, format, paramètres) = écriture dans la chaine s
• sscanf (char * s, format, paramètres) = lecture dans la chaine s
exemple
char tokenstring[] = "15 12 14... " ;
char s[81]; char c; int i; float fp;
/*lecture de différentes valeurs: */
sscanf( tokenstring, "%s", s );
sscanf( tokenstring, "%c", &c );
sscanf( tokenstring, "%d", &i );
sscanf( tokenstring, "%f", &fp );
/* Sortie*/
printf( "String = %s\n", s );
printf( "Character = %c\n", c );
printf( "Integer: = %d\n", i );
printf( "Real: = %f\n", fp ); }
•
Sortie : String = 15 Character = 1 Integer: = 15 Real: = 15.000000
Lecture/écriture dans chaines de caractères
• Exercice :
Convertir un entier en une chaîne de caractères :
exemple :
int i = 135,;
char s[1000];
/* à faire */
……..
printf("%s",s);  135;
Compléments sur les instructions de contrôle
• Instruction if : imbrication
ex1 : if(e1) if(e2) s1;
else s2;
else if (e3) s3;
else s4;
s1
e2
s2
e1
s3
e3
s4
s1
ex2 : if(e1) s1;
else if (e2) s2;
e1
e2
s1
ex3 : if(e1) s1;
else if (e2) s2;
else s3;
e1
s2
s2
e2
s3
s1
ex4 : if(e1) if(e2) s1;
else s2;
s1
e2
e2
s2
e1
e1
s2
Compléments sur les instructions de contrôle
• Règle : le else se rapporte au if le + imbriqué
s1
if(e1) {if(e2) s1;}
else s2;
e2
ou bien
e1
s2
if(e1)
if(e2) s1;
else ;
else s2;
Compléments sur les instructions de contrôle :
continue
• Dans une structure itérative : l'instruction continue permet
d'arrêter l'itération courante sans sortie de la boucle
• Exemple: Calculer la moyenne des valeurs positives d'un
tableau d'entiers relatifs.
nb_valeurs=0;
somme = 0;
for (i=0;i<dim;i++) {
if (T[i]<0) continue;
somme = somme + T[i];
nb_valeurs ++;
}
moyenne = somme / nb_valeurs
Instruction break
L'instruction break fait sortir de la structure de contrôle dans laquelle elle
est imbriquée
Utilisation dans les boucles : permet de faire une boucle avec une
condition de type "et" logique
ex: while (c1 && c2) {traitement;}
while (c1)
if (!c2) break;
else {traitement;}
Application typique : gestion d'exception
différence entre continue et break
for (i = 0 ; i < 10 ; i++) {
if (i == 5) break ;
printf("%d,",i) ;
}
0,1,2,3,4,
for (i = 0 ; i < 10 ; i++) {
if (i == 5) continue ;
printf("%d,",i) ;
}
0,1,2,3,4,6,7,8,9
break
On veut afficher tous les éléments d'un tableau d'entiers jusqu'à rencontrer
un nombre <0 (si il y en a un)
for (i=0;t[i]>=0 && i<dim;i++)
printf("%d", t[i]);
ou bien
i=0;
while(t[i]>=0 && i<dim)
{printf("%d", t[i]);i++};
Pb : lorsque i=dim, il y a évaluation de t[dim] qui n'existe pas => erreur
Solution 1 : avec un "drapeau »
positif=1;
// positif indique que l’on a eu que des valeurs positives jusqu'à maintenant
for (i=0; (positif==1) && (i<dim) ; i++) {
if(t[i]<0) positif=0;
else printf("%d", t[i]);
}
Solution 2 :
for (i=0; i<dim;i++) {
if(t[i]<0) break;
else printf("%d", t[i]);
}
Instruction switch
• syntaxe : switch (expression) instructions où expression a
une valeur entière ou caractère
• L'instruction est une expression composée d'alternatives.
Chaque alternative commence par une énumération de
cas
• switch (expression) {
case valeur 1 : instruction1; instruction 2; ….;
case valeur 2 : instruction1; instruction 2; ….;
…
case valeur n : instruction1; instruction 2; ….;
default : instruction1; instruction 2; ….; // optionnel
};
switch
Exemple :
char c;
printf("donner un e couleur\n");
c=getchar();if (c>='a' && c<='z') c=c+'A'-'a'
switch (c) {
case 'R' : printf("Rouge \n");
case 'V' : printf("Vert\n");
case 'B' : printf("Bleu\n");
default : printf ("Autre");
}
Rouge
Vert
Bleu
Autre
R
Vert
Bleu
V
Autre
Bleu
B
Autre
Autre
switch + break
Exemple :
char c;
printf("donner un e couleur\n");
c=getchar();if (c>='a' && c<='z') c=c+'A'-'a'
switch (c) {
case 'R' : printf("Rouge \n");break;
case 'V' : printf("Vert\n");break;
case 'B' : printf("Bleu\n");break;
default : printf ("Autre");
}
Rouge
R
Vert
V
Bleu
B
Autre
switch + break
Exemple :
char c;
printf("donner une couleur\n");
c=getchar();
switch (c) {
case 'r','R' : printf("rouge \n");break;
case 'v', 'V' : printf("Vert\n");break;
case 'b','B' : printf("Bleu\n");break;
default : printf ("Autre");
}
Rouge
r ou R
Vert
v ou V
Bleu
b ou B
Autre
Les tableaux
Rappel : tableau =regroupement de données de même type sous un même nom,
accessibles par un indice (0,..,dim-1)
Déclaration et implantation mémoire :
int t[50]; => réservation dans la mémoire de 50 cases contiguës d'entiers.
L'adresse de la première case est t
t[0]
t[1]
t[2]
&t[0]  t
*t  t[0]
t[48]
t[49]
t
Tableaux
•
Initialisation à la compilation
int t[10] = {1,2,3,4,5,6,7,8,9,10};
float x[4] = {0.,0.25,3.14,2.57};
char couleur[4]= {'r','v','b','j'};
char texte[10]="abcd";
int t1[10] = {1,2,3};
1 2 3 4 5 6 7 8 9 10
0. 0.25 3.14 2.57
r v b j
a b c d \0 ? ? ? ? ?
1 2 3 ? ? ? ? ? ? ?
• Dimension par défaut:
int t[ ]={0,0,0} => dimension =3
char t [ ]={'r','v','b','j'}; => dimension=4
char t[ ]="abcd" => dimension=5
par contre int t[ ] sans initialisation est incorrect
Tableaux
• Accès aux éléments d'un tableau
int t[50];
syntaxe 1
// accès à la (i+1)ème case avec i compris entre 0 et 49
t[i];
syntaxe 2
puisque t est l'adresse de la première case :
t[0]  *t
// mot d'adresse t, * : opérateur mot dont l'adresse est)
t[1]  *(t+1) // rem : priorité des opérateurs)
…
t[i]  *(t+i) //
*t+i  t[0]+i
Tableaux à plusieurs dimensions
•
Tableau dont chaque case est elle-même un tableau
ex : typedef int t[100] // t est un type
t matrice [20];
matrice est un tableau de 20 cases, chacune est un tableau de 100 entiers =>
matrice est un tableau de 20*100 entiers
autre déclaration :
int matrice [20][100]; // tableau de 20 "lignes" et 100 "colonnes"
• Accès aux éléments
par un 1er indice allant de 0 à 19 et par un 2eme indice allant de 0 à 99
matrice[3] est la 4eme case de tableau. C'est un tableau de 100 cases
(entiers)
matrice[3][48] est un entier.
matrice [i][j] avec i de 0 à 19 et j de 0 à 99
matrice est un tableau à 2 dimensions
Tableaux à plusieurs dimensions
• Pas de limitations sur le nombre de dimensions
Ex à 3 dimensions : tableau de coord. de pts de l'espace
typedef float point[3] ; // x: indice 0, y : indice 1, z : indice 2
point tab[100][100]; // tab = matrice de 100 points
ou bien
tab[100][100][3];
tab[2][5][1] représente le "y" du point rangé en ligne 2 et colonne 5
• Implantation mémoire
int t[3][2];
t[0][0]
t[0][1]
t[1][0]
t[1][1]
t[2][0]
t[2][1]
t[0]
t[1]
t[2]
Tableaux à plusieurs dimensions
• Initialisation (à la compilation)
int t[3][2] = {1,2,3,4,5,6};
ou bien (+ clair)
int t[3][2] = {{1,2},{3,4},{5,6}};
int t[3][2] = {{1,2},4,{5,6}}; => t[1][1] non initialisé
• Initialisation grâce à des boucles
for (i=0;i<3;i++)
for (j=0;j<2;j++)
t[i][j]=0;
t[0][0]
t[0][1]
t[1][0]
t[1][1]
t[2][0]
t[2][1]
=1
=2
=3
=4
=5
=6
t[0]
t[1]
t[2]
Tableaux à plusieurs dimensions
• Accés aux éléments
int t[dim1][dim2] ;
t[i][j]  * (t+i*dim2+j)
int t[dim1][dim2][dim3];
t[i][j][k]  * (t+i*dim2*dim3+j*dim3+k)
int t[dim1][dim2]….[dimn] ;
t[i1][i2]….[in]  * (t
+i1*dim2*dim3*dim4….. *dimn
+i2*
dim3*dim4….. *dimn
+…..
+in-1
*dimn
+in)
)
=> la première dimension n'est pas utilisée dans le calcul
Les fonctions
•
•
•
Une fonction permet de :
– Remplacer une partie qui se répète
– Découper un programme en parties isolées -> débogage, lisibilité, etc..
Exemples : fonctions d'E/S (scanf, printf, …), mathématiques (sin, cos, …)
Organisation d'un programme :
Déclarations de variables et de types globaux
type fonction1 (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}
type fonction2 (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}
...
void main (arguments) {
Déclarations de variables et de types locaux à la fonction
Instructions
}
Exemple
Type de la
valeur de
retour
Argument
char minus_majus (char c1) {
char c2; /* déclarations locales */
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
Instructions
else c2=c1;
return (c2);
}
Valeur renvoyée
void main() {
char c,majuscule;
printf("Donner un caractere\n");
c = getchar(); getchar();
Appel de la fonction
majuscule = minus_majus(c);
printf ("La majuscule de %c est %c\n",c,majuscule);
}
Définition de fonction : syntaxe
type_fonction nom_fonction (type_arg1 arg1, …, type_argn argn) {
…
return (valeur retournée);
}
Dans l'exemple précédent : char minus_majus (char c1)
type_fonction : char,
c'est le type de la valeur renvoyée par return
nom_fonction : minus_majus
type_arg1 : char
arg1 : c1
Le nombre d'arguments est quelconque, éventuellement aucun, les parenthèses
doivent toujours figurer (ex: main () )
Type de la fonction
• Une fonction peut ne pas renvoyer de valeur.
• Exemple
void print_majus (char c1) {
char c2;
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
else c2=c1;
printf("la majuscule de % est %c, c1, c2);
return; /* ou bien return (); ou bien ;*/
}
• Dans ce cas, le type de la fonction est : void
• Le type de la fonction ne peut être que :
• int, float, char void, ou adresse_de
• ni tableau, ni autre type complexe
Instruction return
1/ Indique la valeur de retour de la fonction.
2/ Arrête l'exécution de la fonction
char minus_majus (char c1) {
if (c1 >= 'a' && c1 <= 'z')
return (c1+'A'-'a');
else return (c1);
printf("%c",c1); // jamais executée
}
Pour les fonction de type void, return est optionnel
void print_majus (char c1) {
char c2;
if (c1 >= 'a' && c1 <= 'z')
c2 = c1+'A'-'a';
else c2=c1;
printf("la majuscule de % est %c, c1, c2);
}
Appel des fonctions
• L'appel d'une fonction se fait en donnant son nom, suivi de la liste des
paramètres entre parenthèses. L'ordre des paramètres correspond à
celui des arguments.
• Exemple
float puiss (float x, int n) {
float y=1.0;
if (n>0) for (i=1;i<=n;i++) y = y*x;
else for (i=1;i<=n;i++) y = y/x;
return (y);
}
void main () {
float z,t;
z = puiss(10.7,2);
t = puiss (z,-6);
...
}
Appel des fonctions
•
Un appel de fonction peut se faire comme opérande d'une expression, soit comme
paramètre d'un autre appel de fonction.
• Exemple
int maximum (int x, int y) {
return((x>y)?x,y));
}
void main () {
int v1,v2,v3,m1;
scanf("%d %d %d , &v1,&v2,&v3);
m1 = maximum(v1,v2);
m1 = maximum(m1,v3);
printf("valeur maximale %d\n", m1);
}
ou bien
m1 =maximum(v1,v2);
printf("valeur maximale %d\n", maximum(m1,v3));
ou bien
printf("valeur maximale %d\n", maximum(maximum(v1,v2),v3));
Règles de déclaration et d'appel
•
•
Toute fonction ne peut appeler que des fonctions déclarées avant elle ou elle-même (
exception : la fonction main ne peut pas s'appeler).
... f1 (..) {
...
}
... f2 (...) {
...
}
... f3 (...) {
...
}
void main (...) {
...
}
la fonction main peut appeler f1,f2,f3
la fonction f3 peut appeler f1,f2,f3
la fonction f2 peut appeler f1, f2
la fonction f1 peut appeler f1
Lorsqu'une fonction s'appelle elle-même, on dit qu'elle est "récursive".
Déclarations en "avance"
• Règle précédente contraignante
• Solution : Prototype
En début de programme on donne le type de chaque fonction , son
nom, le nombre et les types des arguments : prototype
• Information suffisante pour le compilateur.
float puiss (float,int); /*Prototype de la fonction puiss*/
void main(){
puiss (10.2, 5);
/*Appel avant déclaration*/
...}
/*Déclaration de la fonction */
float puiss (float x, int n){
float y=1.0;
if (n>0) for (i=1;i<=n;i++) y = y*x;
else for (i=1;i<=n;i++) y = y/x;
return (y);
}
Fichier "header"
• Conseil de programmation :
Dans un fichier ".h" déclarer les prototypes de toutes les fonctions,
par exemple malib.h
Dans le ou les fichiers ".c", insérer la directive
#include "malib.h"
Passage des paramètres
•
•
Rappel : les paramètres sont associés aux arguments suivant l'ordre de
déclaration.
En c, cette association se fait par COPIE de la valeur du paramètre dans
l'argument. Chaque argument est en fait une variable locale de la fonction. La
fonction travaille sur l'argument.
• Conséquence : Une fonction ne modifie pas les paramètres d'appels
void f (int a){
a=a+1;
}
void main(){
int b;
b=0;
f(b);
printf("%d\n",b);
->0
}
Détail
void f (int a){
a=a+1; /*t3*/
}
void main(){
int b;
b=0; /*t1*/
f(b); /*t2*/
printf("%d\n",b); /*4*/
}
b
0
/*t1*/
b
0
a
0
/*t2*/
Copie
b
0
a
1
/*t3*/
b
0
/*t4*/
Inchangé
Modification des paramètres
• Si l'on veut qu'une fonction modifie un paramètre, on ne
passe pas la variable mais l'adresse de la variable. Il y a
copie de l'adresse de la variable. Dans la fonction on va
chercher la variable par son adresse.
• Rappels :
opérateur & : &variable -> adresse de la variable
opérateur * : *adresse -> valeur qui se trouve à cette adresse
int i;
int * adresse_i; /* déclaration d'une adresse d'entier */
i=0;
adresse_i=&i;
printf("%d\n",i); -> 0;
printf("%d\n",*adresse_i); -> 0;
...
Modification des paramètres
void f2 (int * a){ // a est l'adresse, *a est l'entier
*a=*a+1;
/*t3 on incrémente le mot d'adresse a*/
}
void main(){
int b;
b=0;
/*t1*/
f2(&b);
/*t2 &b est l'adresse de b */
printf("%d\n",b);
/*t4*/ -> 1
}
b
0
728
b
&b
a
/*t1*/
0
b
728
728
Copie
/*t2*/
Exemple : scanf ("%d",&v);
1
&b
728
a
728
/*t3*/
*a
b
1
/*t4*/
Passage d'un tableau à une dimension en
paramètre
• Rappels:
– Lorsqu'on déclare un tableau, par ex int t[10], t est l'adresse du 1er
élément du tableau
– Chaque élément du tableau peut être accédé par t[i] ou *(t+i)
– La première dimension d'un tableau n'est pas utilisée dans le
calcul de l'adresse d'une case
• Exemple
void printtab (int t1[50]) {
int i;
for (i=0;i<50;i++)
printf("%d",t1[i]);
}
void main () {
int t[50];
.....
printtab(t);
}
Passage d'un tableau à une dimension en
paramètre
• Puisque la dimension n'est pas utilisée, on peut ne pas la
donner
void printtab (int t1[50]){
int i;
for (i=0;i<50;i++)
printf("%d",t1[i]);
}
ou bien
void printtab (int t1[])
int i;
for (i=0;i<50;i++)
printf("%d",t1[i]);
}
{
Syntaxes équivalentes
Passage d'un tableau à une dimension en
paramètre
Conséquence : on peut donc appeler cette fonction avec tout tableau d'entiers quelle
que soit sa dimension. C’est au programmeur à gérer les débordements de
tableau => donner le nombre de cases sur lequel travaille la fonction
void printtab (int t1[], int n){
int i;
for (i=0;i<n;i++)
printf("%d",t1[i]);
}
void main () {
int t[50],t2[100];
...
printtab(t,50);
/*affiche
printtab(t2,100); /*affiche
printtab(t+20,30);/*affiche
printtab(t+20,10);/*affiche
}
toutes
toutes
toutes
toutes
les
les
les
les
cases
cases
cases
cases
de
de
de
de
t de 0 à 49*/
t2 de 0 à 99*/
t de 20 à 49*/
t de 20 à 30*/
Passage d'un tableau à une dimension en
paramètre
Puisqu'en fait t1 est une adresse, on peut le déclarer comme tel
void printtab (int * t1,int n) {
int i;
for (i=0;i<n;i++)
printf("%d",t1[i]);
}
Passage d'un tableau à une dimension en
paramètre
Conséquence :
Si un argument est de type tableau (càd une adresse), la
fonction peut modifier les cases du tableau
void misea0 (int t1[], int n){
int i;
for (i=0;i<n;i++)
t1[i]=0;
}
void main () {
int t[10]={1,2,3,4,5,6,7,8,9,10};
printtab(t,10); -> 1 2 3 4 5 6 7 8 9 10
misea0(t,10);
printtab(t,10); -> 0 0 0 0 0 0 0 0 0 0
}
Fonctions de manipulations de chaînes de
caractères
•
Rappel : une chaîne de caractères est un tableau de caractères. Le caractère
\0 marque la fin de la chaine.
#include "string.h"
....
char S1[]="abcd";
char S2[20];
S2 = S1 ; incorrect
strcpy(S2,S1);
void strcpy( char * dest, char * source){
int i;
i=0;
while(source[i]!=\0) {
dest[i]=source[i]
i++;
}
dest[i]=\0
}
Fonctions de manipulations de chaînes de
caractères
• Toutes les fonctions de manipulation de chaines de caractères suivent
ce gabarit.
• strlen(char * s) : donne la longueur de s, le 0 final non compris
• strcat (char * s1, char * s2): ajoute s2 à la fin de s1
• strcmp(char * s1, char * s2): compare les chaines suivant l'ordre
lexicographique. Valeur de retour :
>0 si s1 > s2
=0 si s1 = s2
<0 si s1 < s2
"abc" < "abcd"
"aa"< "ab"
"aa" < "b"
et bien d'autres fonctions (voir string.h)
Fonctions et tableaux à plusieurs dimensions
Rappel: Pour des tableaux à plusieurs dimensions, la première dimension n'est
pas utilisée pour le calcul d'adresse, mais la seconde , la troisième etc.. sont
nécessaires .
int t[dim1][dim2]….[dimn] ;
t[i1][i2]….[in]  * (t
+i1*dim2*dim3*dim4….. *dimn
+i2*
dim3*dim4….. *dimn
+…..
+in-1
*dimn
+in)
Conséquence
Lorsqu'un argument est un tableau à plusieurs dimensions il faut donner explicitement
sa deuxième, troisième etc...
Fonctions et tableaux à plusieurs dimensions
Exemple
void zero_mat (float t[30][40]) {
int i, j;
for (i=0;i<30;i++)
for (j=0;j<40;j++)
t[i][j]=0;
}
void main() {
float t1[30][40];
zero_mat(t1);
}
Remarques :
Dans void zero_mat (float t[30][40]),
1/le 30 est inutile
2/le 30 n'a pas de rapport avec le 30 de la boucle for
Fonctions et tableaux à plusieurs dimensions
Autre déclaration
void zero_mat (float t[][40]) {
int i, j;
for (i=0;i<30;i++)
for (j=0;j<40;j++)
t[i][j]=0;
}
void main() {
float t1[30][40];
zero_mat(t1);
}
Mais
void main() {
float t1[30][40];
float t2[40][40];
// zero_mat peut être utilisée pour t2, mais seules les 30 premières "lignes" seront mises à
zero
}
Fonctions et tableaux à plusieurs dimensions
Autre déclaration (mieux)
void zero_mat (float t[][40], int lignes) {
int i, j;
for (i=0;i<lignes;i++)
for (j=0;j<40;j++)
t[i][j]=0; // pas de pb dans les calculs d'adresse
}
void main() {
float t1[30][40];
float t2[40][40];
zero_mat(t1,30);
zero_mat(t2,40);
}
Fonctions et tableau à plusieurs dimensions
Cette fonction n'est utilisable qu'avec des tableaux dont la deuxième
dimension est 40
float t1[30][40];
float t2[30][50]; /* on ne peut pas utiliser la fonction sur t2 */
Ennuyeux ! si l'on manipule des matrices avec une deuxième dimension
différente.
Comment contourner la difficulté ?
Faire soit-même l'adressage des cases dans la fonction en gérant les
dimensions
Fonctions et tableau à plusieurs dimensions
void zero_mat (float * t, int l, int c) {
int i,j;
/* rappel t[i][j] = *(t+i*dim2+j) */
for (i=0;i<l;i++)
for (j=0;j<c;j++)
*(t+i*c+j) = 0.0;
}
void main(){
float t1[20][40],t2[30][10];
zero_mat
zero_mat
zero_mat
zero_mat
}
(t1,20,40);
(t2,30,10);
(t1,20,5); /* correct ou incorrect ? effet ? */
(t1,10,40); /* correct ou incorrect ? effet ?*/
Fonctions et tableau à plusieurs dimensions
Pour que le calcul soit correct il faut que c soit égal à la deuxième
dimension vraie du tableau
void zero_mat (float * t, int l , int c) {
int i,j;
/* rappel t[i][j] = *(t+i*dim2+j) */
for (i=0;i<l;i++)
for (j=0;j<c;j++)
*(t+i*c+j) = 0.0;
}
void main(){
float t1[20][40],t2[30][10];
zero_mat (t1,20,40); /* correct */
zero_mat (t2,30,10); /* correct */
zero_mat (t1,20,5); /* incorrect */
zero_mat (t1,10,40); /* correct */
}
Fonctions et tableau à plusieurs dimensions
Solution : passer la deuxième dimension en paramètre
void mat0 (float * t, int l , int c, int dim2) {
int i,j;
/* rappel t[i][j] = *(t+i*dim2+j) */
for (i=0;i<l;i++)
for (j=0;j<c;j++)
*(t+i*dim2+j) = 0.0;
}
void main(){
float t1[20][40],t2[30][10];
zero_mat (t1,20,40,40); // correct mieux: mat0 (&t1[0][0],…)
zero_mat (t2,30,10,10); // correct
zero_mat (t1,20,5,40); // correct
zero_mat (t1,10,20,40); // correct
}
Visibilité des variables
• On appelle visibilité ou portée des variables les règles qui régissent
l'utilisation des variables. Les mêmes règles régissent les types définis
par l'utilisateur.
• Règle 1 : variables globales
Les variables déclarées avant la 1ere fonction peuvent être utilisées dans
toutes les fonctions. Ces variables sont dites globales.
#include "stdio.h"
int i;
void f1 () {
i = i+1;
}
void main(){
i=0;
f1();
printf("%d\n",i) -> 1
}
Visibilité des variables
• Règle 2 : variables locales
Les variables déclarées dans une fonction ne peuvent être utilisées
que dans cette fonction. Ces variables sont dites locales.
void f1 () {
int i;
i = i+1;
}
void main(){
i=0;
-> ERREUR : i n'existe pas pour main
...
}
Visibilité des variables
•
Règle 3 : arguments = variables locales
Les arguments d'une fonction sont aussi des variables locales de la fonction.
void f1 (int i) {
i = i+1; /* i est une variable locale de la fonction */
}
void main(){
int j=1;
f1(j);
printf("%d\n",j)
}
•
Règle 4 : Au sein d'une fonction, toutes les variables doivent avoir des noms
distincts
void f1 () {
int i;
char i; -> ERREUR : i existe déjà
i = i+1;
}
Visibilité des variables
• Règle 5 : Des variables déclarées dans des fonctions différentes
peuvent porter le même nom sans ambiguïté.
void f1 () {
int i;
...
}
void f2 () {
char i;
...
}
void main(){
int i;
....
}
sous-entendu i_f1
sous-entendu i_f2
sous-entendu i_main
Ces 3 variables n'ont rien de commun
Visibilité des variables
• Règle 6 : Si une variable globale et une variable locale ont le même
nom, on accède à la variable locale dans la fonction où elle est déclarée.. Si il
n'y a pas de déclaration locale, on accède à la variable globale.
int i;
void f1 () {
int i;
i=2;
/* i de f1 */
}
void main(){
i=0;
/* i global */
f1();
printf (%"d\n",i); -> 0
}
Conseils
• Evitez autant que possible l'usage des variables globales => limitation
des effets de bord indésirables
int i;
void f1 () {
...
i=i+1;
}
void main(){
i=0;
f1();
printf (%"d\n",i); -> 1
}
• Dans f1, on travaille sur i global :
– Est-ce bien ce que l'on désirait (oubli de déclaration d'une nouvelle
variable locale ?)
– Débogage difficile : il faut inspecter le code en détail pour voir où
sont modifiées les variables.
Conseils
• Si l'on ne peut éviter les variables globales, respecter un code pour
différencier les variables globales des variables locales.
• Par exemple :
si l'initiale de la variable est une majuscule -> globale : Vglob
minuscule -> locale : vloc
ou bien
le nom de chaque variable globale commence par G_ : G_variable
etc...
• Pas de confusion entre variables locales et globales.
• Mêmes règles pour les déclarations de type que pour les variabes
Compléments : static
• Static : une telle variable maintient sa valeur à travers les
appels de la fonction
void inc( )
{
int i=0;
i++;
printf ("%d", i);
}
1, 1, 1, 1, …
void inc( )
{
static int i=0;
i++;
printf ("%d", i);
}
1, 2, 3, 4, …
Compléments : register
• Une déclaration "registre" indique au compilateur qu'une
variable sera utilisée fréquemment.
• Si c'est possible, le compilateur utilisera un registre pour
implanter la variable plutot qu'un emplacement mémoire
(vitesse d'exécution)
• register int i;
Structures
• Une structure permet de rassembler sous un même nom
des données de types différents
• Une structure peut contenir des donnés entières,
flottantes, tableaux , caractères, pointeurs, etc... Ces
données sont appelés les membres de la structure.
• Exemple : fiche d'indentification d'un personne
– nom, prénom, âge, liste des diplômes, etc...
Définition d'une structure
• Déclaration d'une structure : syntaxe
struct nomdelastructure {
typemembre1 nommembre1 ;
typemembre2 nommembre2 ;
…
typemembren nommembren ;
}
• Exemple : compte bancaire
struct compte {
int no_compte ;
char etat ;
char nom[80];
float solde;
};
struct compte a,b,c; /*déclaration de 3 variables de ce type*/
Déclarations de variables
• Autres façons de déclarer des variables structure
struct compte {
int no_compte ;
char etat ;
char nom[80];
float solde;
} a, b;
/*déclaration de 2 variables de ce type*/
struct compte c; /*déclaration de 1 variable de ce type*/
struct
{ /* le nom de la structure est facultatif */
int no_compte ;
char etat ;
char nom[80];
float solde;
} a,b,c;
/*déclaration de variables de ce type ici */
/* mais plus de possibilité de déclarer d'autres variables de
ce type*/
Déconseillé
Déclarations de variables
• Autres façons de déclarer des variables structure
typedef struct compte {
int no_compte ;
char etat ;
char nom[80];
float solde;
} cpt ;
/* cpt est alors un type équivalent à struct
compte*/
cpt a,b,c; /*déclaration de variables de ce type*/
Recommandé
• Dans ce cas puisque on ne se sert plus de "struct compte" par la suite
typedef struct {
int no_compte ;
char etat ;
char nom[80];
float solde;
} cpt ;
Structures imbriquées
• Une structure peut être membre d'une autre structure
struct date {
int jour;
int mois;
int annee;
};
struct compte {
int no_compte ;
char etat ;
char nom[80];
float solde;
struct date dernier_versement;
};
• Remarque : ordre de déclaration des structures
Structures
• Tableaux de structures
struct compte client[100];
• La portée du nom d'un membre est limité à la structure dans laquelle il
est défini. On peut avoir des membres homonymes dans des
structures distinctes.
struct s1 {
float x;
int y ;
};
struct s2{
char x;
float y;
};
Pas de confusion
Manipulation des structures
• Initialisation à la compilation
struct compte {
int no_compte ;
char etat ;
char nom[80];
float solde;
struct date dernier_versement;
};
struct compte c1 = {12345,’p',"Dupond",2000.45,01,10,2019};
• Accés aux membres : opérateur .
1/
c1.solde = 3834.56;
2/
struct compte c[100];
y=c[33].solde;
Syntaxe : variable.membre
3/ c1.dernier_versement.jour = 15;
c[12].dernier_versement.mois = 10;
Manipulation des structures
• Sur les structures elles-mêmes
– Affectation :
c[4] = c1
– Pas de comparaison , il faut comparer tous les membres
Structures et pointeurs
• L'adresse de début d'une structure s'obtient à l'aide de l'opérateur &
typedef struct {
int no_compte ;
char etat ;
char nom[80];
float solde;
struct date dernier_versement;
} cpt ;
cpt c1 , * pc;
• c1 est de type cpt, pc est un pointeur sur une variable de type cpt
pc = &c1;
• Accés au membres à partir du pointeur
*pc.no-compte = ...
(*pc).no-compte = ...
• Opérateur ->
pc->no-compte = ...
Incorrect . est plus prioritaire que *
Structures et fonctions
• Les membres d'une structure peuvent être passés comme paramètres
à des fonctions avec ou sans modification
• Ex1 (sans modification)
float ajoute_au_compte(float solde1, float somme1) {
solde1 = solde1+somme1;
return (solde1);
}
void main () {
......
cpt c1;
c1.solde = 0.;
ajoute_au_compte(c1.solde,1000.0);
printf("%f\n",c1.solde); -> 0.000000
c1.solde=ajoute_au_compte(c1.solde,1000.0);
printf("%f\n",c1.solde); -> 1000.000000
Structures et fonctions
• Ex2 (avec modification)
void ajoute_au_compte(float * solde1, float somme1) {
*solde1 = *solde1+somme1;
}
void main () {
......
cpt c1;
c1.solde = 0.;
ajoute_au_compte(&(c1.solde),1000.0); /* ou &c1.solde */
printf("%f\n",c1.solde); -> 1000.000000
Structures et fonctions
• Un argument de fonction peut-être de type structure
float ajoute_au_compte(cpt c, float somme1) {
return(c.solde+somme1);
}
void main () {
cpt c1;
c1.solde = ajoute_au_compte(c1,1000.0);
printf("%f\n",c1.solde); -> 1000.000000
• Ou pointeur sur structure
void ajoute_au_compte (cpt * c, float somme1) {
c->solde = c->solde + somme1;
}
void main () {
cpt c1;
ajoute_au_compte(&c1 ,1000.0);
printf("%f\n",c1.solde); -> 1000.000000
Structures et fonctions
• La valeur de retour d'une fonction peut être une structure
cpt ajoute_au_compte(cpt c, float somme1) {
cpt c2;
c2=c;
c2.solde=c.solde+somme1;
return(c2);
}
void main () {
......
cpt c1;
c1.solde = 0.;
c1=ajoute_au_compte(c1,1000.0);
printf("%f\n",c1.solde); -> 1000.000000
Récursion
• Définitions :
• Une notion est dite récursive quand elle fait référence à elle-même
soit directement soit indirectement.
• Récursion directe : A -> A -> A
• Récursion indirecte : A-> B ->.... -> A
• Exemples :
• arbre : racine et des branches vers des sous-arbres
• n! = n*(n-1)!
• Un problème peut être représenté par un algorithme récursif quand il
peut être décomposé en un ou plusieurs sous-problèmes de même
type mais de taille inférieure.
Récursion
• Méthode générale :
1) Le paramétrage consiste à mettre en évidence les éléments dont
dépend la solution, en particulier la taille du problème.
2) La recherche et la résolution d'au moins un cas trivial : consiste à
résoudre le problème directement, c-à-d sans appel récursif, dans un
cas particulier.
3) La décomposition du cas général consiste à passer d'un problème
de taille N à un ou des problèmes de taille < N.
• Exemple 1 : calcul de factorielle.
1) paramétrage : n
2) cas triviaux : 1! = 0! = 1
3) décomposition : n! = n* (n-1)!
Récursion
int facto (int n) {
int p;
if (n==0) return (1);
else {
p = n * facto(n-1); /* appel récursif à la fonction facto
*/
return (p);
}
• Lors du calcul de facto(n) il y a n appels à la fonction facto.
La pile contient n "assiettes" correspondant à cette
fonction.
Récursion
Exemple 2 : Suite récurrente : Suite de Fibonacci (récursion double);
Un = Un-1 + Un-2
U1 = U0 =1
1) paramétrage : n
2) cas triviaux : U1 = U0 =1;
3) décomposition : Un = Un-1 + Un-2
•
Implantation en langage C
int fibo (int n) {
if ((n==0) || (n==1)) return (1);
else return ( fibo(n-1) + fibo (n-2) ); /* appel récursif
à la fonction fibo */
}
•
Lors du calcul de fibo (n) il y a 2n appels à la fonction fibo. La pile contient n
"assiettes" correspondant à cette fonction.
Récursion
Exemple 3 : Combinaisons Cnp (méthode du triangle de Pascal);
1) paramétrage : n et p
2) cas triviaux : Cn0 = 1 Cnn = 1
3) décomposition : Cnp = Cn-1p-1 + Cn-1p
Implantation en langage C
int combinaisons (int n, int p) {
if ((n==p) || (p==0)) return (1);
else return(combinaisons(n-1,p-1)+combinaisons(n-1,p))
/* appel récursif à la fonction combinaisons */
}
Récursion
Exemple 4 : Tri par partition d'un tableau T sur l'intervalle [a , b]
1) paramétrage : a et b
2) cas triviaux : a >= b-1 , rien à faire
3) décomposition :
Trier T sur [a,b] : - Faire la partition de T sur [a, b] et soit adpivot l'adresse
du pivot après partition
- Trier T sur [a , adpivot - 1]
- Trier T sur [adpivot + 1, b]
void tri (tab T , int a , int b){
int adpivot;
if (b > a+1) { /* sinon ne rien faire */
partition (T,a,b,&adpivot);
tri(T,a,adpivot-1);
tri(T,adpivot+1,b);
}
}
Récursion
Exemple 5 : Recherche du zéro d'une fonction continue sur [a , b]
Pb : Trouver un zéro x0 d'une fonction continue sur [a , b] avec une précision  >
0 donnée si il en existe, sinon détecter qu'il n'y a pas de zéros.
On cherche x et y / x < y ≤ x +  et tels que f(x).f(y) < 0.
Principe de dichotomie :
- si a et b sont tels que f(a). f(b) <0 alors il existe un zéro dans [a,b].
- sinon, on divise l'intervalle en 2 moitiés et on recherche dans le premier
intervalle. Si l'on n'a pas trouvé de zéro on cherche dans le second.
1) paramétrage : a , b et 
2) cas triviaux :
a < b ≤ a +  et f(a). f(b) <0 alors il existe un zéro dans [a,b] et x0 = a
a < b ≤ a +  et f(a). f(b) > 0 alors il n'existe pas de zéro dans [a,b]
3) décomposition : principe de dichotomie
Récursion
int zero (float a, float b, float epsilon, float * x) {
/ * retourne 0 si il n'existe pas de zero dans l'intervalle
a , b; 1 sinon */
int trouve;
if ( b-a <= epsilon)
if (f(a) * f(b) < 0 ) { *x = a; return (1); };
else return (0);
else
{
trouve = zero (a,(a+b)/2,epsilon,x);
if (trouve) return(1);
else return(zero ((a+b)/2,b,epsilon,x));
}
}
Récursion
Exemple 6 : Les tours de Hanoi :
Soient 3 socles A, B et C. Sur le socle A sont posées n disques de
taille décroissante. Le problème consiste à transférer tous les disques
du socle A au socle B en respectant les contrainte suivantes :
- on ne déplace qu'un disque à la fois
- on ne peut déplacer que les disques se trouvant en haut de
chaque socle
- on ne peut déplacer un disque que si on le pose sur un disque
plus grand ou sur un socle vide. Exemple avec 5 disques :
Socle A
Socle B
Socle C
Récursion
1) paramétrage : N : nombre de disques, socle de départ, socle relais,
socle final
2) cas triviaux : N = 0 ne rien faire ; N= 1, et socle final vide : déplacer de
départ à final
3) décomposition :
Transférer N disques de A vers B en passant par C :
- Transférer N-1 disques de A vers C en passant par B
- Déplacer 1 disque de A vers B
- Transférer N-1 disques de C vers B en passant par A
void hanoi (int n, char depart, char final, char relais) {
if (N>0) {
hanoi(n-1, depart, relais, final);
printf("deplacement de %c à %c\n", depart, final);
hanoi(n-1, relais, final, depart) ;} ;
}
Récursion
remarques :
- la seule véritable action est faite par le printf
- pour n disques il y a 2n appels à la fonction hanoi
appels successifs
hanoi(3,'A','B','C')
hanoi(2,'A','C','B')
hanoi(1,'A','B','C')
hanoi(0,'A','C','B')
A vers B
hanoi(0,'C','B','A')
A vers C
hanoi(1,'B','C','A')
hanoi(0,'B,'A,'C')
B vers C
hanoi(0,'A','C','B')
A vers B
hanoi(2,'C','B','A')
hanoi(1,'C','A','B')
hanoi(0,'C','B','A')
C vers A
hanoi(0,'B','A','C')
C vers B
hanoi(1,'A','B','C')
hanoi(0,'A,'C,'B')
A vers B
hanoi(0,'C','B','A')
Actions
rien
A vers B (1)
rien
A vers C (2)
rien
B vers C (3)
rien
A vers B (4)
rien
C vers A (5)
rien
C vers B (6)
rien
A vers B (7)
rien
1
Socle A
Socle B
Socle C
Socle A
2
Socle A
Socle B
Socle B
Socle C
Socle A
Socle B
Socle B
Socle C
5
Socle C
Socle A
6
Socle A
Socle C
3
4
Socle A
Socle B
Socle B
Socle C
7
Socle C
Socle A
Socle B
Socle C
Exercices
1- Que fait la fonction suivante ?
int f (int n) {
if (n<0) return(f(-n));
if (n==0) return (0);
return(1+f(n/10);
}
Exercice
Calculer xk de manière récursive en se basant sur les
propriétés suivantes
•x0 = 1
•xk = x si k=1
•xk = 1 / x-k si k est négatif
•xk = (xk-1) x si k est positif et impair
•xk = (xk/2) (xk/2) si k est positif et pair
Exercices
2- Ecrire une fonction récursive qui teste l'existence d'une
lettre donnée dans une chaine de caractères. Son prototype
est : int existe_lettre(char lettre, char * chaine)
Exercice
• Ecrire une fonction récursive qui prend en entrée une
chaîne de caractères sous la forme d'une somme
algébrique de réels, par ex. '−1,37 + 40−10,08+7−244 ‘ et
qui évalue cette somme algébrique.
Exercice
• Ecrire une fonction booléenne et récursive qui teste si une
chaîne de caractères donnée est un anagramme d'une
autre chaîne de caractères donnée.
• Par exemple: 'algorithme' est un anagramme de
'logarithme'.
Adresses et pointeurs
• Adresse = Pointeur
• Rappels :
opérateur & : & variable -> adresse de la variable
opérateur * : * adresse -> valeur qui se trouve à cette adresse
int i;
int * adresse_i; /* déclaration d'une adresse d'entier */
i=0;
adresse_i=&i;
printf("%d\n",i); -> 0;
printf("%d\n",*adresse_i); -> 0;
Adresses et pointeurs
• Déclaration de pointeurs
• Syntaxe
type * variable /* type : char, float, int, structure */
ex :
int * pi;
/* pi est un pointeur d'entier */
float * px; /* px est un pointeur de réel */
char * pc ; /* pc est un pointeur de caractère */
• Interprétations
int * pi ;
Le mot d'adresse pi est un entier => pi est un pointeur
int * pi ;
pi donne une adresse
Adresses et pointeurs
• Types de pointeurs
type_simple * variable
type_simple : int, float, char, pointeur
int
int
int
pi=
ppi
i;
* pi;
* * ppi ;/* adresse d'adresse d'entiers */
728 11509
i
&i;
...
= π
pi 11509 23712
ppi
...
23712 38124
• Pas de pointeurs de tableau (le nom du tableau est déjà un pointeur)
int t[10];
int * * ppi ;
ppi = &t;
Pointeurs et opérations
• Sur variable pointée : toute opération valide sur le type
float x,y,* px; ...
px= &x;
y = sinus (*px);
• Sur pointeurs
Remarque : La valeur d'un pointeur n'a pas d'intérêt en elle-même, d'autant
qu'elle change à chaque exécution.
1/ affectation
int * pi, * pj;
float * px;
...
pi= pj;
px = (float *) pi; /*conversion de type de pointeur */
Pointeurs et opérations
2/ comparaison d'égalité, d'inégalité
int * pi, * pj;
...
if (pi==pj) ......;
3/ comparaison :
>, < , .... mais aucun intérêt.
Etats d'un pointeur, NULL
Un pointeur doit donner l'adresse d'une zone mémoire allouée
int * pi, * pj;
int i1,i2;
i1=0;
pi=&i1;
i2 = *pi; /* correct */
i2 = *pj -> erreur
PB : comment différencier une adresse valide d'une adresse invalide ?
NULL est une valeur spéciale indiquant qu'un pointeur ne pointe vers rien
int * pi, i;
pi = NULL;
if ( ....) pi = &i;
if (pi != NULL) printf ("%d",*pi);
Etats d'un pointeur, NULL
3 états d'un pointeur :
1/ NULL
2/ ≠ NULL
2.1 adresse valide
2.2 adresse invalide
PB : comment différencier une adresse valide d'une adresse invalide ?
ne jamais avoir de pointeur dans l'état 2.2
=> initialiser tous les pointeurs à NULL
Allocation dynamique de mémoire
Jusqu’à maintenant, on a vu que tous les objets (variables) ainsi que leur taille
devaient être déclarés au moment de la compilation, càd explicitement dans le
code C.
En particulier, la dimension des tableaux doit être connue au moment de l’écriture
du pgm et ne peut être modifiée au moment de l’exécution.
Les fonctions de gestion "dynamique" de mémoire permettent de remédier à cette
limitation.
2 fonctions de base :
malloc() : allocation d'une zone de mémoire
free() : libération d'une zone mémoire précédemment alloué grâce à malloc
La fonction malloc
la fonction malloc permet de réserver n octets contigus (un tableau) dans la
mémoire.
La valeur de retour est l'adresse du premier octet réservé.
Exemple :
int * t; //t est un pointeur d'entier
int n;
printf("combien d entiers voulez-vous réserver ? \n");
scanf("%d",&n);
t = (int*) malloc (n*sizeof(int));
conversion
de type
Calcul du
nombre d'octets
La fonction malloc
Exemple :
int * t; //t est un pointeur d'entier
int n;
….
t = (int*) malloc (n*sizeof(int));
..
// accès aux éléments
*t = … // 1ere case ou bien t[0]
*(t+1)=…//2eme case ou bien t[1]
*(t+2)=…//3eme caseou bien t[2]
…
*(t+i)=…//ieme case ou bien t[i]
En fait, on a "réservé" un tableau de n cases
La fonction malloc
Prototype
void * malloc (int n) ;
type de
pointeur
nombre d'octets
La fonction free
La fonction free permet de libérer l'espace mémoire alloué par un malloc
précédent.
Ex :
int * pt;
int n;
… /*t1*/
…
pt = (int*) malloc(n*sizeof(int)); /*t2*/
…
free (pt); /*t3*/
…
n
n
n
pt
/*t1*/
pt
pt
/*t2*/
/*t3*/
La fonction free
Remarque :
Après free, pt ne vaut pas NULL et il indique pourtant une adresse inaccessible.
n
pt
/*t3*/
Faire toujours suivre un free par une mise à NULL du pointeur
free (pt); /*t3*/
pt=NULL;
malloc et free
l'adresse donnée comme paramètre à la fonction free doit correspondre à
une adresse renvoyée par un malloc précédent
int * pt;
int n;
pt = (int*) malloc(n*sizeof(int));
pt = pt+1
free (pt); => ERREUR
…
malloc et free
Il ne faut jamais "oublier" l'adresse renvoyée par un malloc, seul moyen
d'atteindre les cases réservées => risque de saturation de la mémoire
int * pt;
…
pt = (int*) malloc(sizeof(int)); /*t1*/
pt = (int*) malloc(sizeof(int)); /*t2*/
pt = (int*) malloc(sizeof(int)); /*t3*/
…
pt
/*t1*/
Libre
Occupé
Occupé inaccessible
pt
/*t2*/
pt
/*t3*/
Autres fonctions
• calloc : idem malloc + initialisation des cases réservées à
0
• realloc : permet d'agrandir une zone mémoire déjà
réservée
– Ex :
scanf ("%d", &taille);
t = (int *) malloc (taille*sizeof(int));
……..
……..
nouvelle_taille = taille+100;
t =(int *) realloc(t, nouvelle_taille*sizeof(int));
Tabelau à une dimension (inconnue à la
compil)
int * t; int n;
printf("taille du tableau ? ");
scanf ("%d", &n);
t = (int *) malloc (n* sizeof(int));
t[0]= …;
…
t[n-1]= …;
Tableaux à plusieurs dimensions variables
Rappels
t[dim1][dim2] ~ dim1 tableaux de dim2
int t[3][2] : tableau de 3 cases, chaque case est un tableau de 2 entiers
rappel : la première dimension n'est pas utilisée dans le calcul d'adresse
t[i][j] <-> *(t+i*dim2+j)
t[0][0]
t[0][1]
t[1][0]
t[1][1]
t[2][0]
t[2][1]
t[0]
t[1]
t[2]
Première dimension "variable"
Première dimension inconnue à la compilation ~ t[n] [2]
Même principe que pour les tableaux à une dimension
typedef int t2[2]; // t2 est un type
t2 * t;
// t est un pointeur sur un objet de type t2
scanf("%d",&n);
t[0][0]
t = (t2 *) malloc(n*sizeof(t2));
t[0][1]
t[1][0]
t[1][1]
t[2][0]
t[2][1]
t[n-1][0]
t[n-1][1]
t[0]
t[1]
t[2]
t[n-1]
L'accès aux éléments par la notation t[i][j] est utilisable puisque :
t[i][j] <-> *(t+i*2+j) // 2 est la taille d'un objet de type t2, c'est la 2eme dimension
Deuxième dimension "variable"
Deuxième dimension inconnue ~ t[4] [n]
Principe : on utilise un tableau de pointeurs
int * t[4]; // t est tableau de 4 pointeurs
// Création du tableau :
scanf("%d",&n);
for (i=0;i<4;i++)
t[i] = (int *) malloc(n*sizeof(int));
t[0]
t[1]
t[2]
t[3]
Deuxième dimension "variable"
Accès aux éléménts :
t[i][j] est traduit en *(t[i]+j) : donc notation utilisable
Intérêt : tableau de chaines de caractères
De plus pour les chaines de caractères, l'initialisation est possible :
char * semaine[7]= {"lundi","mardi,..,"dimanche"};
Tableaux à plusieurs dimensions variables
Deux dimensions inconnues ~ t[n] [m] ~ matrice
La deuxième dimension est inconnue à la compilation mais sera constante
int ** t; // t est un pointeur sur un tableau de
pointeurs
ex:
scanf("%d %d",&n,&m);
// allocation des lignes
t = (int * *) malloc(n*sizeof(int*));
// allocations des colonnes
for (i<0;i<m;i++)
t[i]=(int *) malloc (m*sizeof(int));
Tableaux à plusieurs dimensions variables
t[0]
t[1]
t[2]
t[3]
…
….
t[n-2]
t[n-1]
Accès aux éléménts :
t[i][j] est traduit en *(*(t+i)+j) : donc notation utilisable
Tableaux à plusieurs dimensions
Deux dimensions inconnues, la deuxième peut varier
Exemple : stocker un nombre inconnu de chaines de caractères
char ** dic;
int cpt = 0;
char tmp[100]; //tableau intermédiaire pour la lecture
int nb = 10;
// nombre de chaines prévues par défaut
dic = (char **) malloc(nb * sizeof(char *));
for(i=0;i<nb;i++) dic[i]=NULL;
printf("donner un nom"\n);
scanf("%s",tmp);
while (strlen(temp)!=0) {
dic[cpt] = (char *) malloc(strlen(tmp)*sizeof(char));
strcpy(dic[cpt],tmp);
nb++;
if (cpt> nb-1) { // on augmente la première dimension
nb=nb+10;
dic = (char **) realloc(dic,nb*sizeof(char*));
for(i=nb-1;i>nb-11;i--) dic[i]=NULL;
}
Exercice : trier les noms par ordre alphabétique
Structures et allocation dynamique de
mémoire : listes, files, piles, etc..
Pas plus de variables que de pointeurs déclarés
Lorsque on veut créer dynamiquement des variables, on :
- déclare un pointeur sur structure
- dans la structure on définit un membre qui est lui-même un pointeur (à l'aide
duquel on pourra créer dynamiquement une autre structure qui elle-même
contient un pointeur, etc …)
Structures et allocation dynamique de
mémoire : listes, files, piles, etc..
Exemple
struct ent {
int valeur;
struct ent * suivant;
};
struct ent * p1;
p1=NULL;
p1 = (struct ent *) malloc(sizeof(struct ent));
p1->valeur=1;
p1->suivant= (struct ent *) malloc(sizeof(struct ent));
p1->suivant->valeur=2;
p1->suivant->suivant= (struct ent *) malloc(sizeof(struct ent));
…..
Structures et allocation dynamique de
mémoire : listes, files, piles, etc..
p1=NULL; /*1*/
p1 = (struct ent *) malloc(sizeof(struct ent)); /*2*/
p1->valeur=1; /*3*/
p1->suivant= (struct ent *) malloc(sizeof(struct ent)); /*4*/
p1->suivant->valeur=2; /*5*/
p1->suivant->suivant= (struct ent *) malloc(sizeof(struct ent)); /*6*/
…..
valeur suivant
p1
p1
p1
1
p1
p1
1
2
1
p1
1
2
Structures et allocation dynamique de
mémoire : listes, files, piles, etc..
p1
1
2
Les valeurs sont accessibles par :
p1->valeur
p1->suivant->valeur
p1->suivant->suivant->valeur
Pb1 : on doit connaitre le nombre de (->suivant) au moment de l'écriture du pgm !!
Pb2 : quand doit on s'arrêter ?
rep : le dernier membre suivant doit être NULL.
Structures et allocation dynamique de
mémoire : listes, files, piles, etc..
struct ent {
int valeur;
struct ent * suivant;
};
struct ent * p1;
p1 = (struct ent *) malloc(sizeof(struct ent));
Syntaxe équivalente plus commode
typedef struct ent element, *pt ; //element et pt sont des types
struct ent {
int valeur;
pt suivant;
};
pt p1;
p1 = (pt) malloc(sizeof(element);
Listes
Création d'une liste contenant les n premiers entiers
pt deb =NULL; pt paux;
for(i=1;i<=n;i++) {
paux= (pt) malloc (sizeof(element));
paux->valeur = i;
paux->suiv=deb;
deb=paux;
}
Parcours des éléments de la liste
paux=deb;
// on prend un pointeur auxiliaire pour ne pas perdre l'adresse de début de la liste
while(paux!=NULL) {
printf("%d",paux->valeur);
paux=paux->suiv;
}
Attention: ne jamais perdre l'adresse du début de la liste
Listes : exemples de fonction
Fonction ajoutant une donnée en début de liste
void ajoutdeb (pt * d; int data) {
// doit être appelée avec le pointeur de début de la liste ex : ajout(&deb,56)
pt paux;
paux =(pt) malloc(sizeof(element));
paux->valeur=data;
paux->suiv=*d;
*d=paux;
}
Listes : exemples de fonction
Fonction ajoutant une donnée en fin de liste
void ajoutfin (pt * d; int data) {
// appelée avec le pointeur de début de la liste Ex : ajoutfin(&L,42);
pt paux,paux2;
paux=(pt) malloc(sizeof(element));
paux->valeur=data; `//attention priorité des opérateurs
paux->suiv=NULL;
if(*d==NULL) *d=paux;
// cas d'une liste initialement vide
else {
// autres cas
paux2=*d;
while(paux2->suiv!=NULL) paux2=paux2->suiv;
paux2->suiv=paux;
}
}
Listes : exemples de fonction
Fonction retournant l'adresse d'une donnée présente dans la liste
pt adresse1(pt d; int data) {
// doit être appelée avec le pointeur de début de la liste p=adresse2 (L,42);
while(d->valeur!=data) d=d->suiv;
return(d);
}
Fonction retournant l'adresse d'une donnée dont on ne sait si elle est présente ou
pas dans la liste (dans ce cas, la fonction renvoie NULL)
pt adresse2(pt d; int data) {
// doit être appelée avec le pointeur de début de la liste ex: p=adresse2 (L,42);
while(d!=NULL)
if(d->valeur==data) return(d);
else d=d->suiv;
return(NULL);
}
Listes : exemples de fonction
Fonction supprimant la liste
pt detruit (pt * d) {
// doit être appelée avec le pointeur de début de la liste
while (*d!=NULL) {
paux=*d;
*d=(*d)->suiv;
free(paux);
}
}
Listes : exemples de fonction
Fonction supprimant une donnée dans la liste
void detruit (pt * d, int data) {
// doit être appelée avec le pointeur de début de la liste ex: detruit (&L,42);
pt p,prec;
prec=NULL
*d
p=*d;
while (p!=NULL) {
if (p->valeur!=data) { //on avance dans la liste
prec=p;
p=p->suiv;
}
prec
else { //on a trouvé l'élément à supprimer
if(prec!=NULL) prec->suiv=p->suiv;
data
p
else *d =(*d)->suiv;
free(p);
return;
}
}}
Traitement récursif des listes chaînées :
• Le traitement itératif des listes est peu commode. Par exemple pour
insérer un élément dans une liste il faut repérer l'adresse de l'élément
précédent dans la liste. Même problème lorsque on veut retirer un
élément de la liste
• Idée : considérer une liste L comme L = x + L' ou x est le premier
élément de la liste et L' est la liste L privée du premier élément.
• L'adresse de x est connue c'est celle du début de la liste, celle de L'
également.
• Pour le traitement récursif :
• 1) cas triviaux : lorsque la liste est vide
• 2) décomposition : L = x + L' . Appliquer une traitement à L revient à
l'appliquer soit sur l'élément à x , soit à L'
Traitement récursif des listes chaînées :
• On suppose dans la suite que les listes sont déclarées de
la façon suivante :
typedef struct {
int data;
pt suiv;
} elementliste, *pt;
• chaque liste est déclarée par un pointeur sur son premier
élément
pt L=NULL;
Affichage d'un liste L dans l'ordre de ses
éléments :
• cas trivial : si L est vide, ne rien faire
• décomposition :
Afficher un liste L = 1/ imprimer x
2/ Afficher la liste L'
• Implantation en langage C
void affiche (pt L) {
if (L) { // si liste non vide; peut s'écrire if(L!=NULL))
printf(("%d"\n",L->data);
affiche (L->suiv);
}
}
Affichage d'un liste L dans l'ordre inverse de
ses éléments :
• cas trivial : si L est vide, ne rien faire
• décomposition :
Afficher un liste L = Afficher la liste L'
imprimer x
• Implantation en langage C
void affiche (pt L) {
if (L)
{
affiche (L->suiv);
printf(("%d"\n",L->data);
}
}
Retrait d'une donnée d d'une liste
• Cas trivial : si L est vide ne rien faire.
• Décomposition :
Retirer de L =
si x = d remplacer L par L'
sinon Retirer d de L' -> L".
faire L = x + L"
• Implantation en langage C
void retrait (pt * L, int d) {
pt p;
if (L) {
if (d == (*L)->data) {
/* element à retirer en début de liste */
p = *L;
*L = (*L) ->suiv;
free(p); };
else
/* element à retirer dans L' */
retrait ( &((*L)->suiv), d);
}
• remarque : l'appel récursif se faisant avec l'adresse du champ suivant qui est
modifié, il y reconstruction de la liste L c-à-d L = x + L"
Ajout d'une donnée dans une liste de façon
que les éléments soient rangés en ordre
croissant
• Cas trivial : Si L est vide ajouter d en début de liste
• Décomposition :
Ajouter d à sa bonne place dans la liste L 
Si d < x , ajouter d en tête de liste L
Sinon Ajouter d à sa bonne place dans la liste L' -> L"
L = x + L"
void ajout2 (pt * L) {
pt p;
if (!(*L)) {p = (pt) malloc (sizeof(elementliste)); // cas liste vide
p->data=d;
p->suiv= (*L);
*L = p;};
else
{if (x < (*L)->data ) {
/* ajout en début */
p = (pt) malloc (sizeof(elementliste));
p->data=d;
p->suiv= (*L);
*L = p;};
else ajout2 (&((*L)->suiv), d);}
}
Téléchargement