La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

1 Projet informatique 2A Séances de soutien Ces transparents sinspirent largement des cours 1A PET (M.

Présentations similaires


Présentation au sujet: "1 Projet informatique 2A Séances de soutien Ces transparents sinspirent largement des cours 1A PET (M."— Transcription de la présentation:

1 1 Projet informatique 2A Séances de soutien Ces transparents sinspirent largement des cours 1A PET (M. Desvignes) et PMP (R Bressoux, E Moisan, N Castagne) Merci à eux

2 2 Séance I. Lenvironnement LINUX Premier programme de l'année scolaire Compilation et exécution Rappel sur les Types de base, E/S, Structures de contrôle Séance II. Rappel sur les tableaux, pointeurs et fonctions Compilation séparée et Makefile Types structurés, tableaux et chaînes de caractères Entrées sorties fichier Séance IIIAllocation dynamique Débogueur (DDD, Valgrind) Types abstraits ; exemple des listes Récursivité Séance IVsuite de la séance III Arbres et graphes Planning prévisionnel

3 3 Tableaux

4 4 4 Collection de variables de même type, rangées continûment en mémoire Déclaration : spécifier le type des éléments le nom du tableau le nombre des éléments Exemples : float c[100]; /* tableau de 100 réels */ int tab[10]; /* tableau de 10 entiers*/ Exemple : tab1.c main() { int i; int tab[10]; /* Tableau de 10 entiers */ float c[20];/* Tableau de 20 réels */ for (i=0; i<10; i++) tab[i]= 2*i; /*Mettre 0,2,4,6..dans les elements*/ for (i=0; i<10; i++) printf("%d ",tab[i]); /* afficher les éléments de t */ } tab[9]: 18 tab[2]: 4 0 tab[1]: 2 tab[0]: 0 tab &tab[0] Adresse &tab[1] &tab[2] … &tab[9]

5 5 5 Tableaux tab&tab[0] Adresse &tab[1] tab[2] &tab[-2] … &tab[9] &tab[900] tab+1 tab+2 tab+9 Remarques Nombre déléments constant, non modifiable Le nom du tableau est son adresse (ie lendroit où il se trouve en mémoire Accès à un élément du tableau : nom_du_tableau[expression entiere] Exemple : tab[i] Comment fait le compilateur : on part du début du tableau, on ajoute i : cest lendroit où se trouve notre élément Attention : Pas de vérification sur les indices. Si on demande un élément avant ou après la fin du tableau, cest une erreur à lexecution du programme mais pas derreurs à la compilation

6 6 6 Tableaux : accès et débordements Exemple 2 : tab2.c main() { int i; int tab[10]; for (i=0; i<10; i++) tab[i]= 2*i; puts("Voici les elements : "); /* afficher les éléments de t */ for (i=0; i<5; i++) printf("%d ",tab[i]); puts(""); puts("Voici les adresses : "); /* afficher les adresses */ for (i=0; i<5; i++) printf("%p ",tab+i); puts(""); tab[-2]= 18952; printf("%d ",tab[900]); } xbffff384&tab[0] Adresse &tab[1] &tab[2] &tab[-2] … &tab[9] &tab[900] 0xbffff388 0xbffff38c 0xbffff3a8 0xbffff37c 0xc

7 7 7 Tableaux : opérations globales ? Attention : AUCUNE opération globale sur un tableau les opérations et les E/S doivent se faire élément par élément En particulier T1==T2 ne teste pas légalité de 2 tableaux T1=T2 ne recopie pas les éléments de T1 dans T2 Exemples : tab3.c main() {int i; double t1[10], t2[10]; /* t1 et t2 sont 2 tableaux de 10 reéls */ for (i=0; i<10; i++) {t1[i]=2*i; t2[i]=log(100*i+1); } printf("Emplacement de t1:%p de t2:%p\n",t1,t2); printf("Emplacement de t1[1]:%p de t2[1]:%p\n",t1+1,t2+1); printf("Valeur de t1[0]:%lf de t2[0]:%lf\n",t1[0],t2[0]); if (t1==t2) printf("t1 et t2 sont au meme endroit\n"); else printf("t1 et t2 ne sont pas au meme endroit\n"); for (i=0; i<10; i++) t2[i]= t1[i]; /*Copie de t2 dans t1: on peut remplacer cette ligne par: memcpy(t2,t1,sizeof(t1)); Mais on ne peut pas utiliser t1=t2; */ for (i=0; i<10; i++) printf(Valeur de t2[%d] : %lf\n,i,t1[i]); }

8 8 8 Tableaux : fonctions de la libc Les fonctions travaillant sur les zones mémoires : comme un tableau est une zone mémoire continue, on peut utiliser ces fonctions avec les tableaux #include void * memmove(void *s1, const void *s2, size_t n); Copie n octets de la zone de mémoire src vers la zone dest. Les deux zones peuvent se chevaucher. void *memcpy (void *dest, const void *src, size_t n); idem, mais les zones ne peuvent pas se chevaucher int memcmp (const void *s1, const void *s2, size_t n); compare les n premiers octets des zones mémoire s1 et s2. void *memset (void *s, int c, size_t n); remplit les n premiers octets de la zone mémoire pointée par s avec loctet c. void swab (const void * from, void * to, ssize_t n); copie n octets de la zone from dans la zone to, en échangeant les octets adjacents

9 9 Pointeurs

10 10 Pointeurs Variable contenant ladresse dun autre objet (variable ou fonction) Adresse : numéro dune case mémoire Déclaration : type_pointé* identificateur; Exemples : int* p1; /* p1 contient ladresse dun entier */ double* p2; /* p2 contient ladresse dun réel */ ATTENTION : un pointeur doit toujours être initialisé avant dêtre utilisé = Il doit contenir une adresse légale : soit celle dun objet existant soit celle obtenu par une demande dallocation dynamique soit NULL, qui est la valeur 0. Il est interdit de lire et écrire à l'adresse 0 Remarque : on affiche la valeur dun pointeur en hexadecimal par printf("Voici la valeur du pointeur %p\n",p );

11 11 Pointeurs Exemple : p1.c main() { int i=0; int* p1=NULL; /* p1: pointeur sur un entier */ p1 = &i; /*p1 pointe i, ie contient ladresse de i*/ /* ICI, i ou *p1 sont une seule et meme chose */ *p1 = 5; /* identique à i=5; */ printf("Valeur de i:%d, Adresse de i:%p\n",i,&i); printf("Valeur de p:%p, Valeur pointée:%d\n",p1,*p1); printf("Adresse de p:%p\n", &p1); } 0 p= 0 i= 0 0xbffff38c &i Adresse &p … 0xbffff388 p1=0xbffff38c i= 5

12 12 Opérations sur pointeurs Affectation : donner une valeur au pointeur, celle dune adresse légitime. p1=&i; /* &i : l'adresse de i */ Indirection : trouver la valeur pointée par p. j = *p1; /* *p1 : ce qu'il y a à l'adresse p1 */ Comparaison : == et != : p1==p2; p1 et p2 regardent ils la meme adresse ?, = : par exemple, p1 adresse : p 1 +1 est ladresse de lélément suivant p1 Adresse - Adresse ==> entier : p2 -p1 est donc le nombre déléments entre les adresses contenues dans p2 et p1. Valide uniquement si p1 et p2 sont de meme type. ATTENTION : les pointeurs étant typés, les opérations se font en nombre déléments et non en nombre doctets

13 13 Adresse et tableaux Nom du tableau : adresse du tableau Accès à un élément t[i] : on part de ladresse de début du tableau, on ajoute i et on obtient ladresse du ième élément. Lélément est obtenu par lopérateur dindirection * Conséquences Lélément t[i] s écrit aussi *(t+i) Ladresse de t[i] sécrit &t[i] ou bien (t+i)

14 14 Adresses et tableaux Exemple : p3.c main() { int tab[10]; int i; for (i=0; i<10; i++) tab[i]= 2*i; puts("Voici lelement dindice 2 : "); printf("%d %d",tab[2],*(tab+2)); puts(""); puts("Voici les adresses : "); for (i=0; i<5; i++) printf("%p %p",tab+i, &tab[i]); } xbffff364&tab[0] Adresse &tab[1] &tab[2] … &tab[9] 0xbffff368 0xbffff36c 0xbffff388 2 manières différentes d'ecrire l'adresse de t[i]

15 15 Parcourir un tableau avec un pointeur Exemple : p4.c main() {int* p=NULL; int i; int tab[5]; for (i=0; i<5; i++) tab[i]=2*i+1; p=tab; while (p

16 16 Structures Structures, définition de types

17 17 Créer ses propres types Pourquoi ? Clarifier l'écriture dun programme Exemple : jaime pas les int* je veux indiquer clairement quune variable est un octet et pas un caractère Définir un nouveau type : instruction typedef typedef ancien_type nouveau_type;

18 18 Créer ses propres types typedef unsigned char OCTET; typedef int* POINTEUR; void swap(POINTEUR p1, POINTEUR p2) { int c=*p1; *p1=*p2; *p2=c; } main() { OCTET a; int i,j; POINTEUR p; a=156; i=32546; p=&i; j=-5; printf("Valeur de loctet a :%d\n",a); printf("Valeur de i et de *p :%d %d\n",i,*p); swap(&i,&j); } POINTEUR peut remplacer int* partout

19 19 Les structures Regroupement dinformations de types identiques ou différents Relatif à une même entité abstraite. permet de manipuler sous un même nom plusieurs éléments d'informations Exemple : Un point : une structure comportant les deux coordonnées X et Y du point Un complexe : les parties réelle et imaginaire du complexe. un etat civil : une structure regroupant le nom, prénom, n°SS, age.... Déclaration de type struct ident1 { type nom_du_champ;.... } /* fin de structure */ Définition de variable : struct nomdestructure identif_var; a1.re a1.im a1 struct complex { double im,re; }; struct complex a1; Nom du type Nom de la variable

20 20 Structures (2) Un élément chimique avec les informations de type différent: struct element_atomique { char nom[20] ; char symbole[4] ; int nummeroatomique; double masseatomique; double densite ; double fusion ; /* Temperature de fusion en ° */ double vap; /* Temperature de vaporisation en ° */ double rayon; /* rayon atomique en A */ double rayon_cov; /* rayon de covalence en A */ char rayonionique[24] ; /* Rayon ionique */ } x; x.nom X x.symbole x.numeroat x.masseato x.densité Le type sappelle struct element_atomique

21 21 Structure (3) Accès à un champ Pour une variable structurée :opérateur. Pour une structure pointée : opérateur -> Opérations sur les structures : aucune sauf Affectation de structures meme type Passage et retour de structures par valeur dans les fonctions Les éléments de la structure sont des variables à part entière : ils ont une adresse typedef struct { int x,y} T_POINT; void aff(T_POINT a) { printf("%d %d\n",a.x,a.y); } main() { T_POINT a1, *pa1, a2; a1.x=1; a1.y=-1; pa1=&a1; pa1->x=0; a2 = a1; aff(a2); aff(*pa1); } a1.x a1.y a1 pa1 1 0xbffff398 0 a2.x a2.y a2 00xbffff38c 0xbffff394 0xbffff398 On définit une structure anonyme et un nouveau yype pa1 est un pointeur. Donc, pa1->x est la partie x de a1 aff est une fonction qui utilise un T_POINT : elle a accès aux 2 coordonnées de a, a.x et a.y

22 22 Structures (4) Aucune autre action globale, sauf la copie en particulier Comparaison de structure a1==a2 Lecture/ecriture de structure : printf("%lf",a1); Il faut tout faire champ par champ Exemple : struct3.c main() { T_POINT a3,a1,a2={1,-1}; scanf("%lf %lf",&a1.x, &a1.y); a3 = a2; /* Copîe a2 dans a3 : a3.x=1 et a3.y=-1 */ puts("Point a1"); aff(a1); /* affiche un point */ puts("Point a2"); aff(a2); /* affiche un point */ /* Ce code est impossible if (a1==a2) puts("a1 et a2 identiques"); else puts("a1 et a2 different"); */ if(a1.x==a3.x && a1.y==a3.y) puts("a1 et a3 identiques"); else puts("a1 et a3 different"); } Lecture clavier des 2 champs

23 23 Structure (5) Une structure est un type utilisable pour construire dautres types On peut faire des tableaux de structures des structures de structures

24 24 Exos Ex1 : faire un programme qui calcule la somme des éléments d'un tableau en utilisant uniquement des indices pointeurs (et pas entiers) Ex2 : faire un programme qui copie un tableau de N entiers (N=10) dans un autre tableau en utilisant uniquement des indices pointeurs Ex3 : faire un programme qui génère 100 nombres entiers dans un tableau, les affiche et trouve le minimum. Pour utiliser: $ man 3 rand, pour générer les nombres aléatoires Ex4 : faire un programme qui utilise un tableau mono-dimensionel d'entiers comme une matrice. Pour cela faire une fonction int element(int mat, int M, int N, int i, int j) qui retourne l'élèment de la i e ligne et de la j e colonne, ou M et N sont les dimensions de la matrice mat. Faites le programme principal qui teste cette fonction. Ex5 ecrire une structure Image contenant un tableau pour stocker les pixels et deux champs pour stocker les dimensions. Ecrire deux fonctions pour initialiser (initialiser une image) et remplir aléatoirement l'image. Je veux ajouter un champs à ma structure, quelles conséquences pour les deux foncctions ? Que se passe-t'il si je ne retourne pas la structure passée en paramètre ?

25 25 VIII. Fonctions et procédures VIII.1. Introduction VIII.2. Le mécanisme de base VIII.3. Passage par valeur et « passage par adresse » VIII.4. Notion de contrat de fonction, prototype dune fonction

26 26 VIII.1. Introduction #include x, y, z réels double x, y, z ; i entier int i ; z | x | z = fabs(x) ; associe un réel à un réel, z arctg ( x ) z = atan(x) ; associe un réel à un réel, i x i = floor(x) ; associe un entier à un réel, z x y z = pow(x, y) ; associe un réel à deux réels. Une fonction mathématique retourne une valeur de sortie (un résultat) établie à partir de différents paramètres dentrée (des arguments) : VIII.1.1 En maths …

27 27 En programmation, une fonction prend en entrée des données et renvoie des résultats après avoir exécuté différentes instructions. Une fonction ou procédure apparaît comme : - un bloc dinstructions regroupées sous un nom particulier, - qui pourra retourner une unique valeur de sortie (un résultat) - qui pourra admettre 0, 1, 2, … paramètres dentrée (des arguments). Les avantages sont nombreux : - réutilisation du même bloc dinstructions (sur des valeurs différentes), - réutilisation dans dautres programmes (bibliothèques). - décomposition du problème en fonctions qui effectuent des actions simples : - conception du programme plus facile, structuration - lisibilité du code accrue, - débuggage facilité. VIII.1. Introduction VIII.1.2 En programmation…

28 28 VIII.2. Le mécanisme de base #include "stdio.h"// bibliothèque de fonctions dentrées/sorties float discr ( float x, float y, float z) { float discriminant ; // bloc dinstructions (entre {…} ) de la discriminant = y * y – 4 * x * z ; // fonction nommée discr // return discriminant ; // } int main( ) { float a = 4., b = 3., c = -1.5, d, e ; d = discr ( a, b, c ) ; e = discr ( a, 3., 9.) ; return 0 ; } Le main est la fonction qui est automatiquement appelée lors de lexécution : cest le point dentrée du programme. 1/ appel de la fonction discr 2/ retour VIII.2.1. Exemple

29 29 float discr ( float x, float y, float z ) { float discriminant ; discriminant = y * y – 4 * x * z ; return discriminant ; } Le nom de la fonction La liste des arguments (type et nom) en entrée de la fonction. Les arguments sont des variables ! Cette liste peut être vide. Le résultat renvoyé par la fonction. Ici, cest la valeur de la variable locale discriminant Le type retourné. Cest le type de la valeur retournée. Le type peut être void (aucune valeur retournée) Corps de la fonction. Bloc dinstructions qui seront exécutées à chaque appel de la fonction. VIII.2.2. Déclaration dune fonction VIII.2. Le mécanisme de base Variable locale

30 30 int main(int argc, int ** argv) { float a = 4., b = 3., c = -1.5, res ; res = discr ( a, b, c ) ; printf( " discr de %f %f %f vaut %f\n", a, b, c, res); printf( " discr de %f %f %f vaut %f\n", a, 3., 9., discr ( a, 3., 9 ) ); return 0 ; } Appel de la fonction discr avec les valeurs de a, b et c. On suppose la fonction discr déclarée par ailleurs. La valeur retournée par discr est affectée à d. VI.2.3. Appel de fonction VIII.2. Le mécanisme de base

31 31 float discr ( float a, float y, float z) { float discriminant ; discriminant = y * y – 4 * a * z ; return discriminant ; } int main( ) { float a = 4., b = 3., d ; d = discr ( a, b, -1.5 ) ; return 0 ; } VIII.2.4. ce qui se passe… a = 4 b = 3 d = ??? Représentation de la mémoire Avant lappel de fonction VIII.2. Le mécanisme de base

32 32 a, y et z sont les « paramètres » discriminant est une « variable locale » float discr ( float a, float y, float z) { float discriminant ; discriminant = y * y – 4 * a * z ; return discriminant ; } int main( ) { float a = 4., b = 3., d ; d = discr ( a, b, -1.5 ) ; return 0 ; } a = 4 y = 3 z = -1,5 discriminant = ??? Appel de la fonction : copie des valeurs des paramètres effectifs dans de nouvelles variables VIII.2.4. ce qui se passe… VIII.2. Le mécanisme de base a = 4 b = 3 d = ???

33 33 float discr ( float a, float y, float z) { float discriminant ; discriminant = y * y – 4 * a * z ; return discriminant ; } int main( ) { float a = 4., b = 3., d ; d = discr ( a, b, -1.5 ) ; return 0 ; } a = 4 y = 3 z = -1,5 discriminant = ??? Appel de la fonction : copie des valeurs des paramètres effectifs dans de nouvelles variables VIII.2.4. ce qui se passe… VIII.2. Le mécanisme de base Ce nest pas la même variable "a" ! Elle est ailleurs en mémoire ! ! a = 4 b = 3 d = ???

34 34 float discr ( float a, float y, float z) { float discriminant ; discriminant = y * y – 4 * a * z ; return discriminant ; } int main( ) { float a = 4., b = 3., d ; d = discr ( a, b, -1.5 ) ; return 0 ; } a = 4 y = 3 z = -1,5 discriminant = 33 Appel de la fonction. Ca se passe comme dhabitude dans le « main » VIII.2.4. ce qui se passe… VIII.2. Le mécanisme de base a = 4 b = 3 d = ???

35 35 float discr ( float a, float y, float z) { float discriminant ; discriminant = y * y – 4 * a * z ; return discriminant ; } int main( ) { float a = 4., b = 3., d ; d = discr ( a, b, -1.5 ) ; return 0 ; } Fin dappel de fonction : destruction de toutes les variables locales et paramètres d prend la valeur retournée VIII.2.4. ce qui se passe… VIII.2. Le mécanisme de base discriminant = 33 a = 4 b = 3 d = 33

36 36 VIII.2. Le mécanisme de base VIII.2.5. Notion de portée de variable float test( float a, float b) { float d = a * b ; c = a * b ; return d; } int main( ) { float a = 4., b = 5, c ; c = test(a, b); // ou c =test(b,a) si on veut… d = 10.; return 0 ; } Erreur : "c" nest pas définie ici ! ! Erreur : "d" nest pas définie ici ! ! Ce code ne compile pas car : Rappel : Ce nest pas le même a ! Ce nest pas le même b ! les variables sont locales au bloc ou elles sont définies ( entre { } )

37 37 Lorsque lon rencontre une instruction return, lexécution de la fonction est arrêtée. On reprend lexécution des instructions qui suivent lappel de la fonction. Une fonction peut posséder plusieurs instructions return. int maximum( int a, int b) { if ( a > b ) return a ; else return b ; } VIII.2.6. return VIII.2. Le mécanisme de base

38 38 Une fonction peut ne pas renvoyer de résultat : son type de retour est noté void. Une telle fonction est appelée une procédure. #include "stdio.h » void afficheMax( int a, int b ) { // procédure de type void if ( a > b ) { printf("a est le max\n); return ; } printf(b est le max\n); } int main( ) { afficheMax(3, 5 ) ; // pas de valeur à récupérer ! return 0 ; } Remarque : une procédure peut utiliser des instructions return, qui alors ne retourne rien. VIII.2.7. Type void VIII.2. Le mécanisme de base

39 39 Écrire une fonction qui échange les valeurs de deux variables a et b de type int. VIII.3.1. Le problème #include void echange( int a, int b ) { int temp ; temp = b ; b = a ; a = temp ; } int main( ) { int a = 1, b = 2 ; echange( a, b ) ; printf("a: %d et b: %d\n", a, b); return 0 ; } Solution naïve : Que valent a et b ? VIII.3. Passage « par valeur » et « par adresse »

40 40 Ceci ne fonctionne pas car a et b (du main ) et a et b (de echange ) sont des variables différentes bien quétant des homonymes ! En fait, cest comme si on avait écrit le programme suivant : #include void echange( int n, int m ) { int temp ; temp = n ; n = m ; m = temp ; } int main( ) { int a = 1, b = 2 ; echange( b, a ) ; printf("a: %d et b: %d\n", a, b); return 0 ; } VIII.3.1. Le problème VIII.3. Passage « par valeur » et « par adresse »

41 41 Reprenons le déroulement du programme : 1) on définit a et b dans le main ; on leur affecte les valeurs 1 et 2. 2) on appelle la fonction echange avec les VALEURS de a et b. 3) les paramètres m et n reçoivent les valeurs 1 et 2. 4) la fonction echange échange les valeurs de m et n : les paramètres m et n locaux à la fonction valent 2 et 1. 5) on revient dans le main … a et b valent toujours 1 et 2 !!! Passage par valeur. ! VIII.3.1. Le problème VIII.3. Passage « par valeur » et « par adresse »

42 42 #include void echange( int * p_a, int * p_b ) { int temp ; temp = *p_b ; *p_b = *p_a ; *p_a = temp ; } int main( ) { int a = 1, b = 2 ; echange( &a, & b ) ; printf("a: %d et b: %d\n", a, b); return 0 ; } Les pointeurs viennent à notre secours : VIII.4.3. La solution VIII.3. Passage « par valeur » et « par adresse »

43 43 Reprenons le déroulement du programme : 1) on définit a et b dans le main ; on leur affecte les valeurs 1 et 2. 2) on appelle la fonction echange avec les ADRESSES de a et b. 3) p_a et p_b reçoivent les ADRESSES de a et b. 4) la fonction echange échange les valeurs des cases mémoire pointées par p_a et p_b. 5) on revient dans le main … a et b valent maintenant 2 et 1 ! Passage par adresse. ! VIII.3.2. La solution VIII.3. Passage « par valeur » et « par adresse »

44 44 void echange( int * p_a, int * p_b ) { int temp ; temp = *p_b ; *p_b = *p_a ; *p_a = temp ; } int main( ) { int a = 1, b = 2 ; echange( &a, &b ) ; return 0 ; } a = 1 b = 2 p_a = 0xbffff3b0 p_b =0xbfffff3b4 temp = 2 Appel de la fonction : copie des valeurs des paramètres effectifs dans de nouvelles variables p_b vaut ladresse de b. donc *p_b vaut 2 valeursadresses 0xbffff3b0 0xbffff3b4 0xbffff388 0xbffff3bc 0xbffff3c0 VIII.3.3. Exemple VIII.3. Passage « par valeur » et « par adresse »

45 45 void echange( int * p_a, int * p_b ) { int temp ; temp = *p_b ; *p_b = *p_a ; *p_a = temp ; } int main( ) { int a = 1, b = 2 ; echange( &a, &b ) ; return 0 ; } a = 1 b = 1 p_a = 0xbffff3b0 p_b =0xbfffff3b4 temp = 2 Appel de la fonction : copie des valeurs des paramètres effectifs dans de nouvelles variables valeursadresses 0xbffff3b0 0xbffff3b4 0xbffff388 0xbffff3bc 0xbffff3c0 *p_b est la même case mémoire que la variable « b » du main => On affecte 1 à cette variable (cest à dire la valeur de *p_b) VIII.3.3. Exemple VIII.3. Passage « par valeur » et « par adresse »

46 46 Une variable a définie dans une fonction nest utilisable que dans cette fonction. On dit que a est locale à la fonction. Une variable a définie dans une autre fonction est une variable homonyme mais différente de la première ; elle occupera une autre case mémoire lors de lappel de la fonction. Pour modifier la valeur dune variable lorsquon appelle une procédure, il faut utiliser le mécanisme du passage par adresse avec des pointeurs … Faire attention à la manipulation des & et des * !!! Remarque : les termes passage par valeur et passage par adresse sont en fait des abus de langage… En C, on passe toujours par valeur, valeurs qui valent parfois des adresses.. ! VIII.4. Passage par valeur et par adresse VIII.3.4. Résumé

47 47 Avant même décrire le corps dune fonction, il faut être très sur le « contrat » quelle doit remplir, cest à dire : Son rôle (ce quelle fait) Son prototype. Le « prototype » ou la « signature » est constitué de : nom de la fonction, type de retour, type et nom des paramètres Chacun des paramètres est soit : IN : utilisé uniquement en entrée. Passé par valeur en C. OUT : utilisé uniquement en sortie. Passé par pointeur en C. IN/OUT : utilisé en entrée et en sortie. Passé par pointeur en C. Le champ dutilisation de la fonction, et en particulier létat que doit respecter le système avant lappel de la fonction : préconditions Létat que dans lequel sera le système après lappel de la fonction Les cas derreur, et la façon dont la fonction réagit en cas derreur. VIII.4.1. Introduction VIII.4. Notion de contrat de fonction

48 48 Le « contrat de fonction » peut être formalisé à laide de commentaire. Exemple : définir une fonction résolvant dans une équation du second degré. Le « contrat » dune telle fonction pourrait sécrire : VIII.4. Notion de contrat de fonction VIII.4.2. Exemple Prototype Contrat

49 49 VIII.4. Notion de contrat de fonction VIII.4.3. Utilisation du prototype comme promesse dexistence Pour que le compilateur puisse vérifier les appels de fonctions, la fonction ou son prototype doivent être définis avant le premier appel de la fonction. #include //commentaire précisant « contrat » void echange( int * p_a, int * p_b) ; // Prototype de la fonction int main( ) { int a = 1, b = 2 ; echange( &a, & b ) ; // Appel de la fonction return 0 ; } void echange( int * p_a, int * p_b) // Définition de la fonction { int temp ; temp = *p_b ; *p_b = *p_a ; *p_a = temp ; }

50 50 /* ******************************************************* Titre ******************************************************* */ // Entête (#include, #define, etc.) … // Prototypes des fonctions et procédures, accompagné de commentaires pour les « contrats » … int main( ) { // fonction principale …// Définition des variables du main … // Appels des fonctions et procédures // Dorénavant le main sera être réduit à son minimum : // pour lessentiel, définition de variables et appels de fonctions et procédures. return 0 ; } // Définition des fonctions // fonction exemple exemple( ) { … // Définition des variables locales de la fonction exemple … // Instructions de la fonction exemple return … ; // valeur de retour } … VIII.4. Notion de contrat de fonction VIII.4.4 Structure générale dun programme

51 51 Exercice : Calcul des racines dune équation du second degré – suite et fin ! Écrire un programme qui : - demande les coefficients a, b, c de léquation a x² + b x + c = 0 ; - calcule la / les racine(s) réelle(s) de léquation - affiche les résultats On écriera une fonction générique de résolution des équations du second degré, qui traitera tous les cas possibles et qui ne ne fera aucune entrée/sortie. Exercice

52 52 Compilation séparée et Makefile

53 53 Exercice : Calcul des racines dune équation du second degré – suite et fin ! Récrire le programme précédent mais en séparant le main de la fonction de calcul d'une équation du second degré, il devrait y avoir au moins les fichiers suivants : - Makefile - testcalcul.c - fonction_equa.c - fonction_equa.h Écrire un autre main (calcul_alea.c) dans lequel on effectue 10 calculs à partir de valeur aléatoires pour a,b et c. Ajouter une ligne à votre Makefile pour pouvoir le compiler Exercice

54 54 Les chaines de caractères

55 55 Les chaînes de caractères En C, les chaînes de caractères ne sont pas un type particulier mais sont : Des tableaux de char, exemple : char t[256]; Terminés par \0. "Ceci est une chaine" est une chaine de caractère constante Pour utiliser une chaîne de caractère, 2 solutions Créer un tableau et utiliser les fonctions dE/S sur les chaînes (scanf, gets) qui ajoutent un \0 en fin de chaine. Créer un tableau et le remplir élément/élément, y compris le \0 terminal. Exemple : chaine0.c main() {char s[256]; s[0]=u; s[1]=n; s[2]=e; s[3]=\0; /* Affichage de s */ puts(s); /* ou printf("%s\n",s); } ???? e 0 n u 0xbffff2e0&s[0] Adresse &s[1] &s[2] &s[255] 0xbffff3e1 0xbffff3df \0 &s[3] ???? les autres valeurs sont aléatoires car on ne les a pas entrées

56 56 Les chaînes de caractères Exemple : chaine1.c : crée et lit 2 chaines au clavier avec 2 methodes main() { int i; char s1[256], s2[256]; puts("Entrer une chaine"); /* Lecture dune chaine. Gets positionne la marque de fin de chaine*/ gets(s1); /* On peut aussi utiliser scanf("%s",s1); */ /* Affichage dune chaine. Utilise la marque de fin de chaine pour afficher les éléments du tableau réellement utilise*/ puts(s1); /* ou printf(%s",s1); */ /* Lecture caractères/caractères de 10 caractères */ for (i=0; i< 10; i++) s2[i]=getchar(); /* Ajout de la marque de fin de chaine */ s2[i]='\0'; /* Affichage de s2 */ puts(s2); /* Affichage de s2 element/element*/ for (i=0; s2[i]!= '\0'; i++) printf("%c", s2[i]); }

57 57 Les chaînes de caractères Les chaînes de caractères sont des tableaux ==> AUCUNE opération globale nest possible : s1==s2 ne compare pas 2 chaînes s1=s2 ne copie pas s2 dans s1 Il faut utiliser les fonctions spécifiques Concaténation : strcat Copie : strcpy, strncpy Comparaison : strcmp, strncmp, strcasecmp, strncasecmp, strcoll Recherche : strstr, strchr Longueur : strlen Decoupage de la chaine en sous chaine : strtok Toutes ces fonctions mettent le resultat dans une chaîne de caractères Attention : la chaîne recevant le résultat doit exister et doit avoir une taille suffisante

58 58 Chaînes de caractères Copie d'une chaîne char* strcpy (char* destin, char* source); main() { char t1[256], t2[256]; printf("tapez une caine"); gets(t1); strcpy(t2,t1); puts(t2); strcpy("chaine", t1); } Concaténation de 2 chaînes : char* strcat (char* destin, char * source); main() { char t1[256], t2[256]; printf("tapez 2 chaines"); gets(t1); gets(t2); strcat(t1,t2); puts(t1); strcat ("ceci est une chaine", t2); } Duplication (allocation et copie) d'une chaîne Cbaine4.c char* strdup (char* source); main() { char t1[256], t2[256]; char* s1; :/* Pas de taille: pointeur */ gets(t1); s1 = strdup(t1); puts(s1); t2=strdup(t1); } Autre exemple main() { char t1[256], t2[256], t3[256]; gets(t1); strcat(strcpy(t2,t1),".obj"); strcat(strcpy(t3,t1),".exe"); puts(t1); puts(t2); puts(t3); }

59 59 Comparaison de 2 chaînes int strcmp (char* chain1, char* chain2) Retourne : un entier négatif si chain1 inférieure à chain2 0 si chain1 identique à chain2 un entier positif si chain1 supérieure à chain2 ordre lexicographique. int strcasecmp (char* chain1, char* chain2) majuscules et minuscules indifférentes Comparaison de 2 chaînes sur n octets int strncmp(char* chain1, char* chain2, int n) /*n= Nombre de caractères à comparer */ 59 Chaînes de caractères Formation/extraction dans des chaines de caractères int sprintf(char* s, char* format, valeurs) int sscanf(char* s, char* format, adresses) main() { char t1[256], t2[256]; int i; float x; double y; sprintf(t1,"Valeurs %d et %f et %lf", i,x,y); puts(t1); strcpy(t1," truc "); sscanf(t1,"%d %f %s %lf ",&i,&x,t2,&y); sscanf(t1, "%d %f %lf ",i,x,y) ; }

60 60 Chaînes de caractères Longueur d'une chaîne unsigned strlen ( char* chaine) Recherche d'une chaîne char* strstr (char* chain1, char* chain2) Recherche de la chaine chain2 dans chaine1. Retourne l'adresse de la première occurrence de chaine2 dans chaine1 Recherche d'un caractère char* strchr (char* chain1, int n) Recherche du caractère n dans chaine1. Retourne l'adresse de la première occurrence du caractère n dans chaine1

61 61 Chaînes de caractères Découpage dune chaine en mots Char* strtok(char* str, char* separateur) Découpe la chaine str en mots. Les séparateurs sont définis par les caractères de la chaine separateur. #include main() { char t1[256], t2[256]; char* p; int i =0; /* Les separateurs sont espace, virgule, point et point virgule et d'interrogation */ strcpy(t2, " ;.,."); strcpy(t1,"Voici une chaine. Vide? Non"); /*Iniitalisation du decoupage */ p=strtok(t1, " ;.,."); while (p!=NULL) { printf("mot numero %d : %s \n",i,p); i++; /* On cherche le mot suivant */ p= strtok(NULL, " ;.,."); }

62 62 Exo : Chaînes de caractères Ex1 : faire un programme qui initialise une chaine de caractères avec toutes les lettres de l'alphabet et qui affiche la chaine obtenue. Quel caractère ne faut il pas oublier ? Ex2 : en utilisant man 3 fgets faire un programme qui saisie un texte sur le terminal et qui l'affiche en retour Ex3 : faire un programme qui demande le nom et le prénom à l'utilisateur et qui affiche bonjour PRENOM NOM en utilisant strcat. Ex4 : faire un programme qui demande de deviner un chiffre entre 1 et 6 (tiré aléatoirement dans le programme). Lorsque le chiffre est trouvé le programme doit afficher : « il vous a fallu NOMBRE essai(s) pour trouver la solution ». Vous devez utiliser la fonction sprintf (man 3 sprintf) pour générer cette chaine.

63 63 XIII. Entrées / sorties sur fichiers XIII. 1. Introduction XIII.2.Ouverture et fermeture dun fichier XIII.3.Lecture et écriture dans un fichier texte XIII.4.Exercice

64 64 Pourquoi ? Stockage des donnés dans un fichier (nombres, caractères, …) Acquisition de donnés nombreuses et complexes Comment ? Création d'un flux vers un fichier Ouverture du fichier Echange avec le fichier (lectures/écritures) Fermeture du flux XIII.1. Introduction

65 65 Deux types de fichiers : Fichier texte, fichier binaire Quand vous affichez un nombre sur l'écran avec printf("%d",i) i est une variable entière, codée sur 4 octets, en base 2. Il y a création dune chaîne de caractères contenant les chiffres du nombre (base 10) Si i vaut 10, la chaîne contient les 3 caractères 1, 0 et fin de chaîne Si i vaut , la chaîne contient les 7 caractères 2,5,6,8,4,5 et fin de chaîne. Ensuite, le système écrit la chaîne à l'écran. Les nombres affichés prennent un nombre doctets différent, chaque chiffre est codé sur un octet mais ils sont lisibles facilement sur nimporte quelle machine (less, more, editeur de texte, etc..) Dans un fichier, si il se passe la même chose, ce sont alors des fichiers texte. Lautre solution est décrire directement les 4 octets composant le nombre Tous les entiers sont alors écrits avec 4 octets, sans aucune transformation Ce sont des fichiers binaires, on les lit difficilement directement. Il faut utiliser la commande od, et interpréter soi-même la sortie XIII.1. Introduction

66 66 Rappel : il existe deux types de fichiers : Les fichiers texte qui contiennent des caractères Exemple : un fichier source.c est un fichier texte Exemple : un fichier image au format ppm est un fichier texte Les fichiers binaires qui contiennent des données binaires Exemple : un fichier exécutable.exe est un fichier binaire Exemple : un fichier pdf.pdf est un fichier binaire Exemple : un fichier image jpeg.jpg est un fichier binaire En C, il est possible de travailler avec ces deux types de fichiers… XIII.1. Introduction

67 67 XIII.1. Introduction 67 En plus, sur INTEL, dans un entier, les octets de poids faible sont en tete et ceux de poids fort en queue. Code ascii du caractère 6, puis de 5, de 5, de 3 et de 6 Texte (n1.txt) ou binaire (n1.bin)

68 68 Pour lire / écrire dans un fichier, il faut à chaque fois : 0. Inclure les headers nécessaires : #include #include 1. Ouvrir le fichier avec la fonction fopen() qui renvoie un pointeur sur le fichier de type FILE *. 2. Vérifier que l'ouverture sest bien passé en testant la valeur du pointeur qu'on a reçu. Si le pointeur vaut NULL, c'est que l'ouverture du fichier n'a pas marché, dans ce cas on ne peut pas continuer (il faut afficher un message d'erreur). 3. Si l'ouverture a marché alors on peut lire et/ou écrire dans le fichier 4. Une fois qu'on a terminé de travailler sur le fichier, il faut penser à le fermer avec la fonction fclose(). XIII.1. Introduction

69 69 Pour travailler avec un fichier, il faut commencer par louvrir en appelant la fonction fopen() dont le prototype est : FILE* fopen(const char* nomDuFichier, const char* modeOuverture); nomDuFichier : une chaine de caractères qui contient le chemin vers le fichier a ouvrir modeOuverture : une indication qui dit si vous voulez juste écrire dans le fichier, juste lire dans le fichier, ou les deux à la fois fopen() renvoie un pointeur de type FILE *. En cas déchec, le pointeur renvoyé vaut NULL. ATTENTION : toujours tester si louverture sest bien passée ( le pointeur renvoyé est non NULL). Quand on a fini de travailler avec un fichier, il faut toujours le fermer avec fclose() : int fclose(FILE * ptrVersFichier); XIII.2. Ouverture et fermeture dun fichier

70 70 Les modes douverture à connaître sont : "r" : ouverture en mode « fichier texte » "b" : ouverture en mode « fichier binaire » XIII.2. Ouverture et fermeture dun fichier

71 71 Les modes douverture à connaître sont : "r" : lecture seule. Vous pourrez lire le contenu du fichier, mais pas écrire dedans. Le fichier doit avoir été créé au préalable. "w" : écriture seule. Vous pourrez écrire dans le fichier, mais pas lire son contenu. Si le fichier n'existe pas, il sera créé. "a" : mode d'ajout. Vous écrirez dans le fichier, en partant de la fin du fichier. Vous rajouterez donc du texte à la fin du fichier. Si le fichier n'existe pas, il sera créé. "r+" : lecture et écriture. Vous pourrez lire et écrire dans le fichier. Le fichier doit avoir été créé au préalable. "w+" : lecture et écriture, avec suppression du contenu au préalable. Le fichier est donc d'abord vidé de son contenu, et vous écrivez et lisez ensuite dedans. Si le fichier n'existe pas, il sera créé. "a+" : ajout en lecture / écriture à la fin. Vous écrivez et lisez du texte à partir de la fin du fichier. Si le fichier n'existe pas, il sera créé. XIII.2. Ouverture et fermeture dun fichier

72 72 int main() { FILE* filePtr =NULL ; // création du pointeur et initialisation filePtr = fopen("test.bin", "rb"); //ouverture en lecture seule, mode binaire de./test.bin if (filePtr == NULL) {// vérification ouverture ! printf("erreur ouverture\n"); exit (-1); } // Maintenant, on peut lire dans le fichier… fclose(filePtr); // fermeture du fichier return 0; } XIII.2. Ouverture et fermeture dun fichier int main() { FILE* filePtr =NULL ; // création du pointeur et initialisation filePtr = fopen("…/dossier/fichier.txt", "wt"); // ouverture en écriture seule, mode texte // Si le fichier existe, son contenu est détruit ! if (filePtr == NULL) {// vérification ouverture ! printf("erreur ouverture\n"); exit (-1); } // Maintenant, on peut écrire dans le fichier… fclose(filePtr); // fermeture du fichier return 0; }

73 73 Une fois que le fichier est ouvert (on dispose dune variable de type FILE * non nulle) avec le modeOuverture adéquat, il est possible de lire et/ou écrire dans le fichier. La lecture / lécriture dun fichier texte est très similaire à ce qui se passe quand on lit au clavier ou écrit à lécran. Les prototypes des principales fonctions sont: // lecture int fscanf(FILE *stream, const char *format,...); //comme scanf ! Retourne le nombre de données lues int fgetc(FILE *stream); char * fgets(char *s, int n, FILE *stream); // ecriture int fprintf(FILE *stream, const char *format,...); //comme printf ! Retourne le nombre de données écrites int fputs(const char *s, FILE *stream); Notes : toutes opération de lecture / écriture dans un fichier avancent dans le fichier… XIII.3. Lecture et écriture dans un fichier texte

74 74 Dans un fichier, les données sont organisées. Cest la notion de « format de fichier ». Pour lire/écrire un fichier, il faut se mettre daccord sur son format. Le programme suivant lit un fichier contenant 3 valeurs entières, par exemple : contenu du fichier test.txt (tel quaffiché par exemple par gedit) XIII.3. Lecture et écriture dans un fichier texte int main() { FILE* fichier = NULL; int valeurs[3]; // Tableau ou on va stocker les 3 entiers du fichier int nbLus ; fichier = fopen("unRepertoire/test.txt", "r"); if (fichier ==NULL ) {// verification erreur printf("Impossible d'ouvrir le fichier test.txt\n"); exit(-1); } // si louverture sest bien passée… nbLus = scanf(fichier, "%d %d %d", &valeurs [0], &valeurs [1], valeurs + 2); printf( "Nombre de valeurs lues : %d\n", nbLus); printf("Les données du fichier sont : %d, %d et %d", valeurs[0], valeurs[1], valeurs[2]); fclose(fichier); return 0; }

75 75 Rappel : Fichier binaire : copie de la représentation mémoire Ouverture d'un fichier binaire : flag b dans le mode douverture lecture dans un fichier binaire int fread(void* t, int size, int nbel, FILE *fp); lecture de nb objets de taille size à partir du flux fp range les objets lus (nb * size octets) à l'adresse de lobjet t. Cette adresse est souvent celle d'un tableau. Retourne le nombre d'objets lus ou 0 si erreur ou fin de fichier Le tableau doit être alloué et contenir assez de place Ecriture dans un fichier binaire int fwrite(void *t, int size, int nbel, FILE *fp); ecriture de nb objets de taille size à partir du flux fp dans lobjet t. Retourne le nombre d'objets lus ou 0 si erreur ou fin de fichier XIII.4. Lecture et écriture dans un fichier binaire

76 76 Dans un fichier binaire, on connait le nombre doctets occupé par chaque donnée : cest le nombre doctet du type de la donnée 1 int = 4 octets, 1 double = 8 octets,.... Accès direct à un élément possible Exemple : si on a a un fichier de double, tous les double occupent la même taille dans le fichier on sait calculer la position du i ième double par rapport au début du fichier ! Savoir où on est dans le fichier long ftell(FILE *fp); Retourne la position courante dans le fichier, en nombre doctets. Se positionner dans le fichier à un octet donné int fseek(FILE *fp, int offset, int from); Positionne la prochaine lecture/ecriture à offset octets de from Pour from : 0:debut du fichier, 2:fin du fichier ou 1:position actuelle) XIII.4. Lecture et écriture dans un fichier binaire

77 77 Exemple : lecture de n1.bin (5 entiers) et ecriture de x+1 dans n2.bin main() { FILE* f1, *f2; int i; int t[5]; f1=fopen("n1.bin","rb"); f2=fopen("n2.bin","wb"); if (fread(t,sizeof(*t),5,f1) <5) /* Lecture des 5 entiers */ printf("Impossible de lire 5 entiers"); else { for (i=0;i<5;i++) t[i]=t[i]+1; /* on ecrit aussi t[i]++; */ if (fwrite(t,sizeof(*t),5,f2) <5) /* ecriture des 5 entiers */ printf("Impossible d'ecrire 5 entiers"); } fclose(f1); fclose(f2); } On ecrit les 5 entiers en 1 seule opération On lit les 5 entiers en 1 seule opération, mais il faut que t puisse contenir ces 5 entiers XIII.4. Lecture et écriture dans un fichier binaire

78 78 Exemple de positionnement dans un fichier binaire avec int fseek(FILE *fp, int offset, int from); Exemple : fichier5.c main() { FILE* fp; int a; /* Lecture ET ecriture */ fp=fopen("Fichier_a_lire","r+b"); fseek(fp,2*sizeof(*t),0); /* On se positionne sur le 3ième réel dans le file*/ a=16785; if (fwrite(&a,sizeof(*t),1,fp) <1) printf("Impossible d'ecrire"); /* On revient au debut du fichier */ fseek(fp,0,0); /* Lecture et affichage des nombres */ while (fread(&a,sizeof(a),1,fp) ==1) printf("%d ",a); puts(""); } Debut du fichier XIII.4. Lecture et écriture dans un fichier binaire

79 79 Écrire un programme qui : Ouvre un fichier "notes.bin" contenant des float Lit tous les entiers dans un tableau Recherche la note minimum (réutiliser la fonction de lexercice « recherche de minimum » Laffiche. XIII.4 Exercice

80 80 Exo : programme de traitement d'images Nous utiliserons le format ppm Le programme doit être capable: De representer une image en memoire D'initialiser une image D'afficher la valeur des pixels à l'écran De charger/écrire une image au format ppm vers/depuis un fichier ppm D'effectuer différentes transformations : negatif, binarisation, etc. Une image sera représentée par un grand tableau mono-dimensionel Commencez par organiser votre code en fichiers c et h Ecrivez votre makefile Definissez vos structures de données puis vos prototypes de fonctions Commencez par les fonctions les plus simples et compiler/tester regulièrement Pour les plus avancés, écrivez votre programme de façon à ce qu'il fonctionne comme une commande du shell avec des arguments. Par ex: $./traiteimage negatif entree.ppm imagemodif.ppm où négatif est une option, entree.ppm est l'image à traitée et imagemodif est le resultat du traitement


Télécharger ppt "1 Projet informatique 2A Séances de soutien Ces transparents sinspirent largement des cours 1A PET (M."

Présentations similaires


Annonces Google