Académique Documents
Professionnel Documents
Culture Documents
Rouzeyre, Polytech'ERII
Langage C
2. Séquencement
Action 1
Action 2
Action 11
Action 1
Action 12
Action 2
Organigramme
4. Sélection
Σ Evi = 1 et Evi.Evj = 0
Structuré Non structuré
Action 1 Action 1
Ev.1
Action 21
Ev.2 Critère ?
Action 22
Ev.1 Ev.2 Ev.3
Ev.3
Action 23 Action 21 Action 22 Action 23
Action 3
Action 3
Action 1 Action 1
Vrai
Action 21
Critère ?
Vrai Faux
Faux
Action 22 Action 21 Action 22
Action 3
Action 3
Organigramme
4. Cas particulier : alternative sans action
Action 1 Action 1
Vrai
Action 21
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
Action 1
Action 1
Vrai Faux
Action 2 Critère
Action 2 Action 3
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
Action 1
Action 1
Action 2
Action 2
Faux
Vrai
Critère
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
Action 1
Action 1
Vrai Faux
Action 2 eεE?
Action 2 Action 3
Action 3
e <- valeur
suivante de E
Action 1
Vrai Faux
eεE?
Action 2 Action 3
e <- valeur
suivante de E
Organigramme : exemple
x = premier
S=0
x = premier
x pair ? S=0
fin = faux
S = S+ x S = S+ x fin = v
Vrai
x dernier ?
Faux
x= suivant(x)
x=
suivant(x) Imprimer S
Imprimer S
KO OK
Codage d'un algorithme en C
{
x=1
x=1;
y=2
Calcul y=2;
z = x+y z=x+y;
imprimer z 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" if (z == 3) {
z = z+10 printf("calcul juste");
z=z+10;
};
Faux else printf ("problème");
"problème" printf ("%d",z);
…
Imprimer z
Codage d'un algorithme en C
Cas particuliers de alternative
calcul …
Vrai if (z == 3) ;
else printf("problème);
printf ("%d",z);
…
Faux "problème"
Imprimer z
Codage d'un algorithme en C
Cas particuliers de alternative
…
if (z == 3) {
printf("calcul juste");
z=z+10;
};
else;
calcul "calcul juste" printf ("%d",z);
Vrai
…
z = z+10
ou bien
Faux
…
if (z == 3) {
Imprimer z
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=3
"ok"
z=4
"PB1"
autres
"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
x=1;
x=1 while (x <=10) {
printf("%d\n",x);
imprimer x
x=x+1;
x=x+1 };
…
JUSQU'À CE QUE : do action while (condition);
x=1;
x=1 do {
printf("%d\n",x);
imprimer x
x=x+1;
x=x+1 }
while (x <= 11);
…
Codage d'un algorithme en C
POUR
x=1;
x=1 while (x <=10) {
printf("%d\n",x);
imprimer x
x=x+1;
x=x+1 };
…
Compilateurs
• Sur le web • En TP
• 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
• Un caractère est représenté sur un octet (8 bits) suivant la table ASCII (American
Standard Code for Information Interchange)
• Table ASCII
ex : code ASCII du 'A' = 65
'A'<'B'<……..< 'Z'
'0'<'1'<'2'<…..<'9'
'a'<'b'<……….<'z'
• Remarques :
les chiffres sont codés suivant un ordre croissant (48 à 57)
é, ö , ä, æ, œ etc…
c='a';
…..
Type caractère
• 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
i 0
• Déclarations
int a,b;
….
a = 1;
….
b= 3;
Type entier relatif
• Codage complément à 2
n2
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
110 -2 2 010
-3 3
101 -4 011
100
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
• Déclaration
float x,y;
x = 3.14;
y = 2.0 e+3;
SM Eb M
Mantisse et exposant
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
à 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
// 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)
• 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;
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
• 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
• 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; float a,b;
a=10; b= 3 a=12.6; b= 3.0
a+b 13 a+b 13.6
a-b 7 a-b 9.6
a*b 30 a*b 37.8
a/b 3 (division euclidienne) a/b 4.2 (division réelle)
a%b 1 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'
• Exemples
i=1; i=1;
printf("i= %d\n",i); -> i=1 printf("i= %d\n",i); -> i=1
printf("i= %d\n",++i); -> i=2 printf("i= %d\n",i++); -> i=1
printf("i= %d\n",i); -> i=2 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
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;
• 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
• Syntaxe
expression1 ? expression2 : expression3
• Exemple :
maximum = (x>y) ? x : y;
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
= += -= *= /= %=
2 >>= <<= &= ^= |= assignations <-
1 , 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()
• 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);
• 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 i = 123 , x = 456.0
rep2 : 123456 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 c1= 'a' , c2 = 'b'
rep2 : a☐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
exemples
char ch[100];
scanf("%[0123456789]",ch);
rep : 32ab48 ch="32"
scanf("%[^0123456789]",ch);
rep : 32ab48 ch="ab"
• putchar(caractère)
• Exemple
char c;
c='a';
putchar ( c );
putchar ('\n');
putchar('b');
Affichage :
a
b
Printf()
• Format
printf(format,arg1,arg2,…..,argn);
réels :
%10f le réel est imprimé sur 10 caractères (en tout) avec 6
chiffres en partie décimale (cadrage à droite)
%-10f idem + cadrage à gauche
• ("%*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
• 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"
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
• 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
• 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
• 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 s1
e2
ex1 : if(e1) if(e2) s1; s2
else s2; e1
s3
else if (e3) s3;
e3
else s4;
s4
s1
ex2 : if(e1) s1;
else if (e2) s2; e1
s2
s1 e2
ex3 : if(e1) s1;
e1
else if (e2) s2; s2
else s3; e2
s3
s1 s1
e2 e2
ex4 : if(e1) if(e2) s1;
s2
else s2;
e1 e1
s2
Compléments sur les instructions de contrôle
e1 ou bien
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
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;}
Rouge
Exemple : Vert
char c;
Bleu
printf("donner un e couleur\n");
c=getchar();if (c>='a' && c<='z') c=c+'A'-'a' Autre
switch (c) {
case 'R' : printf("Rouge \n"); R
case 'V' : printf("Vert\n"); Vert
case 'B' : printf("Bleu\n"); Bleu
default : printf ("Autre");
Autre
} V
Bleu
Autre
B
Autre
switch + break
Exemple : Rouge
char c;
printf("donner un e couleur\n");
c=getchar();if (c>='a' && c<='z') c=c+'A'-'a'
switch (c) { R
case 'R' : printf("Rouge \n");break; Vert
case 'V' : printf("Vert\n");break;
case 'B' : printf("Bleu\n");break;
default : printf ("Autre"); V
} Bleu
B
Autre
switch + break
Exemple : Rouge
char c;
printf("donner une couleur\n");
c=getchar();
switch (c) { r ou R
case 'r','R' : printf("rouge \n");break; Vert
case 'v', 'V' : printf("Vert\n");break;
case 'b','B' : printf("Bleu\n");break;
default : printf ("Autre"); 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)
*t t[0] t[48]
t[49]
Tableaux
• Initialisation à la compilation
1 2 3 4 5 6 7 8 9 10
int t[10] = {1,2,3,4,5,6,7,8,9,10};
float x[4] = {0.,0.25,3.14,2.57}; 0. 0.25 3.14 2.57
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"
• Implantation mémoire
t[0][0]
int t[3][2]; t[0][1] t[0]
t[1][0]
t[1]
t[1][1]
t[2][0]
t[2][1] t[2]
Tableaux à plusieurs dimensions
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
Argument
de retour
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.
• 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 b 0 b 0 b 0 Inchangé
Copie
a 0 a 1
• 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 0 b 1 *a b 1
void main () {
int t[50],t2[100];
...
printtab(t,50); /*affiche toutes les cases de t de 0 à 49*/
printtab(t2,100); /*affiche toutes les cases de t2 de 0 à 99*/
printtab(t+20,30);/*affiche toutes les cases de t de 20 à 49*/
printtab(t+20,10);/*affiche toutes les cases de t de 20 à 30*/
}
Passage d'un tableau à une dimension en
paramètre
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);
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 */
zero_mat (t1,20,40);
zero_mat (t2,30,10);
zero_mat (t1,20,5); /* correct ou incorrect ? effet ? */
zero_mat (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.
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; sous-entendu i_f1
...
}
void f2 () {
char i; sous-entendu i_f2
...
}
void main(){
int i; 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
1, 1, 1, 1, … 1, 2, 3, 4, …
Compléments : register
• register int i;
Structures
Déconseillé
Déclarations de variables
• Autres façons de déclarer des variables structure
Recommandé
typedef struct {
int no_compte ;
char etat ;
char nom[80];
float solde;
} cpt ;
Structures imbriquées
struct compte {
int no_compte ;
char etat ;
char nom[80];
float solde;
struct date dernier_versement;
};
struct s1 {
float x;
int y ;
}; Pas de confusion
struct s2{
char x;
float y;
};
Manipulation des structures
• Initialisation à la compilation
struct compte {
int no_compte ;
char etat ;
char nom[80];
float solde;
struct date dernier_versement;
};
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 = ... Incorrect . est plus prioritaire que *
(*pc).no-compte = ...
• Opérateur ->
pc->no-compte = ...
Structures et fonctions
• Les membres d'une structure peuvent être passés comme paramètres
à des fonctions avec ou sans modification
• Ex1 (sans modification)
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
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
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.
2
3
Socle A Socle B Socle C
Socle A Socle B Socle C
4 5
6 7
int f (int n) {
if (n<0) return(f(-n));
if (n==0) return (0);
return(1+f(n/10);
}
Exercice
• 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 ;
int * pi ;
int i;
int * pi;
int * * ppi ;/* adresse d'adresse d'entiers */
pi= &i; i 728 11509
...
ppi = π
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é
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
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 Calcul du
de type 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]
nombre d'octets
type de
pointeur
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 pt pt
n
pt
/*t3*/
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*/ Libre
pt = (int*) malloc(sizeof(int)); /*t2*/ Occupé
pt = (int*) malloc(sizeof(int)); /*t3*/
Occupé inaccessible
…
pt pt pt
– 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;
t[0]= …;
…
t[n-1]= …;
Tableaux à plusieurs dimensions variables
Rappels
t[dim1][dim2] ~ dim1 tableaux de dim2
t[0][0]
t[0][1] t[0]
t[1][0]
t[1]
t[1][1]
t[2][0]
t[2][1] t[2]
Première dimension "variable"
Première dimension inconnue à la compilation ~ t[n] [2]
t[n-1][0]
t[n-1][1] t[n-1]
t[0]
t[1]
t[2]
t[3]
Deuxième dimension "variable"
t[0]
t[1]
t[2]
t[3]
….
t[n-2]
t[n-1]
struct ent {
int valeur;
struct ent * suivant;
};
valeur suivant
p1
p1 p1 1
p1 1 p1 1 p1 1
2 2
Structures et allocation dynamique de
mémoire : listes, files, piles, etc..
p1 1
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
p=*d;
while (p!=NULL) {
if (p->valeur!=data) { //on avance dans la liste
*d
prec=p;
p=p->suiv;
}
else { //on a trouvé l'élément à supprimer
if(prec!=NULL) prec->suiv=p->suiv;
else *d =(*d)->suiv;
free(p); prec
return;
}
p data
}}
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 :
• 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 :
• 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