Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
Publié parOdette Bailleul Modifié depuis plus de 10 années
1
1 Projet informatique 2A Séances de soutien Francois.Portet@imag.fr Nicolas.Castagne@imag.fr Ces transparents s’inspirent largement des cours 1A PET (M. Desvignes) et PMP (R Bressoux, E Moisan, N Castagne) Merci à eux
2
2 Séance I. L’environnement 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 III. Variables & Opérateurs III.1. Introduction III.2. Variables III.3. Types numériques et opérateurs III.4. Autres types III.5. Types pointeur et notion d’adresse III.6. Constantes nommées
4
4 En C, on a en permanence besoin de stocker des données, des valeurs. Pour cela on utilise des variables. Une variable est caractérisée par : - son type : contient-elle un entier ? un réel ? un caractère ? … Le C est fortement typé. Toute variable a un type. Le type de la variable va déterminer la façon dont elle sera codée par l’ordinateur et la place (en octets) qu’elle occupera en mémoire. - son nom : c’est une étiquette qui nous permettra de manipuler cette variable. L’étiquette correspond à l’adresse de la variable (voir ci après). - sa valeur : c’est la valeur effective de la variable. C’est l’information manipulée. - sa adresse mémoire : l’endroit de la mémoire de l’ordinateur où la variable est stockée, choisie par le système - sa portée ou durée de vie : l’endroit dans le code où on a le droit de l’utiliser. => Entre les deux accolades du bloc dans laquelle elle est déclarée III.1. Introduction
5
5 maVariable1 0 0 000 0 0 0 0 000 00 0 0 1 11 111 11 1111 1111 0 x 2 2 f f 7 2 0 x 2 2 f f 7 4 maVariable2 Type Étiquette/Nom Valeur Mémoire centrale Adresse 0 x 2 2 f f 7 3 0 x 2 2 f f 7 5 « ceci » « cela » Ce qui nous intéresse… Ce qui est géré par le processeur mais … que l’on ne peut « totalement » ignorer. un octet un bit type1 type2 III.1. Introduction 000010110 x 2 2 f f 765
6
6 Le C est un langage dit fortement typé. Toute variable doit : 1) être déclarée : quelle est son type ? Quelle est son nom ? Informations capitales pour le compilateur qui doit savoir comment stocker la variable et comment coder sa valeur. 2) être initialisée quelle est sa valeur initiale ? - soit en dur dans le code source, - soit par lecture au clavier ou depuis le disque dur. Ensuite et seulement ensuite, il est possible : 3) de la manipuler : opérations arithmétiques par exemple. 4) de sortir les résultats à l’écran ou sur le disque dur. III.1. Introduction
7
7 syntaxe : type nom = valeurInitiale;// Attention au point virgule « ; » ! type est un mot clé réservé du langage qui permet d’indiquer si on a affaire à une variable entière, réelle, … (Cf. paragraphe II.3.) nom est le nom que vous choisissez pour la variable : - les mots clés ( main,... ) du langage sont évidemment interdits. - ne choisir que des noms explicites. Jamais de truc, toto, bidule !!! - sont autorisés les lettres ( a…z A…Z ), les chiffres (0…9) et l’underscore _ - interdit de commencer par un chiffre - jamais de caractère espace ! exemples de noms possibles : NombreJours _MaVariable exemples de noms interdits : 2Truc âge // â interdit II.2. Variables III.2.1. Définition d’une variable en C III.2. Variables
8
8 En dur dans le code source : nom = valeur ; // = opérateur d’affectation valeur peut être une valeur tapée en dur dans le code source, une autre variable ou encore le résultat d’une opération arithmétique. Par lecture au clavier : (clavier = entrée standard) Cf. chap suivant #include int toto; scanf("%d", &toto); Par lecture dans un fichier : Cf. chap Entrées/Sorties sur fichiers. Remarque : il est possible de combiner définition et initialisation en une ligne : int maVariable = 5; III.2.2. Initialisation / affectation d’une variable en C III.2. Variables
9
9 R ien de très extraordinaire : int somme = 0; int a = 9; somme = 1 + a ; // par exemple Affichage à l’écran : (écran = sortie standard) Cf. chap suivant #include int toto = 9; printf( "valeur de toto %d\n", toto); Sauvegarde sur disque dur : Cf. chap Entrées/Sorties sur fichiers. III.2.3. Manipulation d’une variable en C III.2. Variables III.2.4. Sortie du résultat
10
10 /* ******************************************************* Commentaire général ******************************************************* */ // Entête #include // Fonction principale int main( ) { // Définition des variables float test = 0.; // Initialisation et/ou saisie clavier (ou depuis fichier) scanf("%f\n", &test); // Opérations test = test + 9.5 ; // Affichage des résultats (écran ou sur fichier) printf("valeur de test %f\n", test); } III.2.4. Rappel - structure d’un programme simple III.2. Variables
11
11 Tout langage de programmation permet de manipuler des variables entières qui reflètent les entiers naturels ou relatifs. Exemples : int i = 9 ; // définition d’une variable nommée i de type int long n, p ; // définitions de 2 variables nommées n et p de type long Type CStockageBorne InfBorne Sup char 1 octets0255 short 2 octets-32 768+32 767 int 2 ou 4 octets selon le processeur long 4 octets-2 147 483 6482 147 483 647 unsigned short 2 octets065 535 unsigned int 2 ou 4 octets selon le processeur unsigned long 4 octets04 294 967 295 Les entiers sont bornés. III.3.1. Types entiers III.3. Types numériques et opérateurs
12
12 Affectation : opérateur = Opérations arithmétiques : + - * / % / % : quotient et modulo // 5 / 2 donne 2 et 5 % 2 donne 1. Opérations de comparaison : Cf. chap Conditions booléennes. Exemples : int i, j, k ; i = 1 ; j = 2 ; k = i + j ; Remarque : il faut savoir ce que l’on fait quand on mélange des choux et des carottes : short i ; long unLong ; unLong = 32767 ; i = unLong + 1 ; // i = ??? ! III.3.1. Opérations sur les entiers III.3. Types numériques et opérateurs
13
13 Tout langage de programmation permet de manipuler des variables réelles. Ces types correspondent à des nombres dits à virgule flottante, ou « flottants », qui permettent d’approcher les nombres réels. En C, il existe des flottants simple précision et des flottants double précision Exemples : float x, y, z ; // définition de 3 variables de type float x = 2.3 ; // attention au. y = 2.5e+2 ;// y vaut 250. e+2 ou e2. Type CStockageBorne InfBorne Sup float 4 octets ±1,4.10 -45 ±3,4.10 38 double 8 octets±4,3.10 -324 ±1,7.10 308 Les flottants sont bornés et discrétisés. III.3.2. Types réels (flottant) III.3. Types numériques et opérateurs
14
14 Affectation : opérateur = Opérations arithmétiques : + - * / // 5 * 1.0 / 2 donne 2.5 Opérations de comparaison : Cf. chap Conditions booléennes. Remarque : il faut savoir ce que l’on fait quand on mélange des choux et des carottes : int i ; float x ; x = 2.5 ; i = x ; // sommes nous sûrs de ce qui se passe ? ! III.3.2. Opérations sur les réels (flottant) III.3. Types numériques et opérateurs
15
15 Les flottants sont bornés et discrétisés. … car il est impossible de représenter l’infinité des nombres réels dans un univers fini Conséquences : la Troncature : il existe toujours une erreur de calcul exemple float x = 0.0001 ; // en fait, x « vaut » 0.0000999999997845 L’addition et la multiplication ne sont plus exactement associatives (3.11 * 2.30) * 1.50 = 10.729500000000000 3.11 * (2.30 * 1.50) = 10.729499999999998 La multiplication n’est plus distributive 3.11 * (2.30 + 1.50) n’est pas égal à 3.11 * 2.30 + 3.11 * 1.50 ! Attention : les « erreurs » risquent de se cumuler… Conclusion sur l’arithmétique flottante Les flottants ont un domaine de variation limités (mais plus grand que les entiers) Les calculs flottants ont une précision limitée (suffisante dans la plupart des cas) ! III.3.2. Opérations sur les réels (flottant) III.3. Types numériques et opérateurs
16
16 Gadgets utiles : i = i +1 ; ⇔ i++ ; i = i - 1 ; ⇔ i-- ; x = x+y ; ⇔ x += y ; x = x*y ; ⇔ x *= y ; III.3.3. Compléments sur les types entier et flottant III.3. Types numériques et opérateurs
17
17 Différence entre division entière et division flottante … dépend des types des opérandes en jeu ! float x; x = 5 / 2;// division entière => 5/2 vaut 2 et x « vaut » 2.0 x = 5. / 2. ;// division flottante => x « vaut » 2.5 int i = 5; x = i / 2;// division entière => x « vaut » 2.0 x = i / 2.;// division flottante => x « vaut » 2.5 x = ( (float) i ) / 2;// division flottante => x « vaut » 2.5 Conversion de type (« cast ») ( (float) i ) est de type float … et vaut « à peu près » 5. III.3.3. Compléments sur les types entier et flottant ! III.3. Types numériques et opérateurs
18
18 II.3.4. Opérations sur les caractères On appelle caractère tout caractère alphanumérique. Tous les caractères accessibles au clavier et affichables à l’écran sont possibles. Exemple : char c ; c = ′a ′ ; // valeur ′a ′ de type caractère ( entourée par ′ et ′ ) // affectée à la variable c ; c vaut donc ′a′ Remarque sur l’encodage ASCI… Affectation : opérateur = Opérations de comparaison : Cf. chap Conditions booléennes. Remarque : Ne pas confondre un caractère seul ( ′a ′ ) et une chaîne de caractères comme par exemple ( "Ceci est une chaine de caracteres" ). III.4.1. Type caractère III.4. Autres types
19
19 Dans les langages où il existe, le type booléen permet de manipuler des variables qui peuvent prendre seulement deux valeurs (True ou False) et d’effectuer des calculs logiques. Il n’existe de pas de type booléen en C. En C, toute variable entière ≠ 0 « peut » être considérée True. toute variable entière = 0 « peut » être considérée False. (Cf. chap V Conditions booléennes). III.4.1 Type booléen ??? III.4. Autres types
20
20 Rappel : une variable est caractérisée par : Un type : entier, réel, etc… Un nom (une étiquette permettant de la manipuler dans le programme) Une valeur Une durée de vie (ou portée) : de l’endroit où elle est déclarée à la fin du bloc : } De plus une variable occupe un espace dans la mémoire, celui où sa valeur est stockée À partir de « l’adresse de la variable » int a; III.5. Types pointeur et notion d’adresse III.5.1. Notion d’adresse
21
21 L’opérateur d’adresse & permet de trouver l’adresse mémoire d’une variable #include main() { int a; a=5; printf("Bonjour %d\n", a); printf("La valeur de a est %d \n", a); printf("L’adresse de a est %p \n", & a); } III.5. Types pointeur et notion d’adresse III.5.2. Opérateur d’adresse & Création (allocation) de « a » à une adresse choisie par l’ordinateur Attention : Valeur de a quelconque ! Affectation de « a » Ecriture de la valeur 5 à l’adresse de « a » en mémoire Destruction de « a » à la fin du « bloc » Affichage de l’adresse de la variable a
22
22 Une adresse n’est pas un entier, ni un réel, ni un caractère, … Si on veut la stocker dans une variable, il faut une variable de type pointeur. Type pointeur : il est repéré par * lors de la déclaration d’un pointeur. Exemple : int i = 2 ; // définition d’une variable i de type entier (int) int * p_i = NULL ;// définition d’une variable p_i //de type « pointeur vers un entier » (int * ) // La valeur de p_i est l’adresse d’un entier. // On initialise le pointeur à NULL adresse invalide p_i = & i ; // affectation de l’adresse de i a la vairable p_i. // on dit que » p_i pointe vers i » ! III.5. Types pointeur et notion d’adresse III.5.3. Type pointeur Une variable pointeur est une variable comme les autres… Elle a un nom, un type (pointeur vers type_X), une valeur (qui est l’adresse d’autre chose)… et bien sûr une adresse ! On peut créer des variables pointeur vers tous les types (int, float…)… et même des pointeurs (eg int ** pp_i; )
23
23 Pour accéder à la valeur qui est à une adresse donnée, on utilise l’opérateur de déréférencement * Exemple : int i = 2 ; // définition d’une variable i de type entier (int) int * p_i = NULL ;// définition d’une variable p_i de type « pointeur vers entier » p_i = & i ; // affectation de l’adresse de i a la vairable p_i. // on dit que » p_i pointe vers i » ! *p_i = 3 ; // on affecte 3 à l’adresse pointée => à « i » ! printf("adresse de i %p %p\n", p_i) ; // affiche adresse de i &i valeur de p_i ! printf("valeur de i : %d \n", *p_i ) ; // on affiche valeur pointée (la valeur de i) III.5. Types pointeur et notion d’adresse III.5.3. Type pointeur
24
24 maVariable Valeur Adresse « ceci » * p_maVariable = & maVariable maVariable &maVariable *p_maVariable p_maVariable III.5. Types pointeur et notion d’adresse III.5.4. Résumé
25
25 Exemple : que valent x, y, z et *ptr ? pour répondre, représentez la mémoire ! // … de nombreux usages à discuter ultérieurement… III.5. Types pointeur et notion d’adresse III.5.4. Exemple
26
26 Notion de pointeur Un pointeur est une variable dont la valeur est une adresse mémoire Déclaration et syntaxe : float * unPtr = NULL ; // unPtr est un « pointeur vers une variable flottante » // Comme toute variable, unPtr a un type (float*), un nom, // et une valeur stockée quelque part en mémoire (à l’adresse de unPtr) // La valeur de unPtr est une adresse, celle d’un float. // *unPtr est la valeur qui est stockée à cette adresse Exemple : que valent x, y, z et *z ? pour répondre, représentez la mémoire ! // … de nombreux usages à discuter ultérieurement… III.5. Types pointeur et notion d’adresse
27
27 II.4. Constantes Deux manières à connaître pour manipuler des valeurs constantes en C Avec la primitive de préprocesseur #define III.6 Constantes nommées #define UNE_CONSTANTE 5// par convention, en MAJUSCULE et sans point virgule ! // Avant la compilation, toute occurrence de // UNE_CONSTANTE sera remplacée par « 5 » int main( ) { printf("valeur de UNE_CONSTANTE %d\n", UNE_CONSTANTE); }
28
28 IV. Entrée / sortie sur le Terminal IV.1. Notion d’entrée / sortie IV.2. Afficher avec printf IV.3. Lire avec scanf IV.4. Synthèse des formats « % » IV.5. Compléments divers
29
29 Opération permettant la communication entre le programme et l’extérieur. Sortie : exportation d’une valeur vers l’extérieur du programme, à travers un périphérique d’entrée : écran, fichier, réseau, carte son… Sortie dans le terminal (« sortie standard ») : avec printf, puts, etc. Voir les man ( man printf ) Entrée : introduction d’une valeur à l’intérieur du programme, depuis un périphérique d’entrée : clavier, fichier, réseau, carte d’acquisition… Entrée depuis le clavier (« entrée standard ») : avec scanf, getc, etc. Voir les man ( man scanf ) IV.1. Notion d’entrée / sortie
30
30 printf() est une fonction de la librairie stdio (standard input-output). #include Afficher un message : printf("Mon message\n"); Afficher une valeur (par exemple la valeur d’une variable) : int i = 4; float x = 4.9; printf("Mon entier i : %d\n", i ); printf("La somme de %d et %f et %d vaut :\t %f \n », i, x, 9, (i+x+9) ); IV.2. Afficher avec printf ‘\n’ aller à la ligne Pour info : ‘\t’ tabulation La valeur à afficher %d : le symbole % indique que la lettre suivante est le type du nombre à afficher Un message optionel
31
31 Syntaxe : int printf(“format”, valeur1, valeur2,...); format : %[*][largeur] [.precision] type_carac spécifier le type du nombre à afficher (type_carac) par %d : nombre entier %c : un caractère %lf : un réel double précision %f : un réel simple précision %p : une adresse (par exemple la valeur d’un pointeur) … En option, on peut préciser le nombre de chiffres que l’on souhaite pour l’affichage par le nombre largeur et le nombre de chiffre apres la virgule pour un réel par le nombre précision – voir man printf IV.2. Afficher avec printf
32
32 printf() : le piège ! IV.2. Afficher avec printf #include main() { double x; int a = 6; x = 3.1415927; /* Affichage*/ printf("Le nombre a : %lf \n", a); printf("Le nombre x : %d \n", x); } Erreur sur le type des nombres à afficher a est un entier, le format demandé (%lf) est celui des réels idem pour x ==> le programme est syntaxiquement correct, la compilation est correcte, mais à l’exécution du programme, l’affichage sera incohérent !
33
33 scanf() est une fonction de la librairie stdio (standard input-output). IV.3. lire avec scanf #include main() { double x; int a; printf(“donnez un entier puis un réel\n”); scanf(“%d”, &a); scanf(“%lf”, &x); printf(”Les valeurs sont %d ; %lf \n", x, a); scanf(“%d %lf”, &a, &x); printf(”Les nouvelles valeurs sont %d ; %lf \n", x, a); } %d “on attend un entier et l’adresse d’un entier” ATTENTION : scanf attend des adresses mémoire (endroit ou la valeur lue va être stockée en mémoire), d’ou l’utilisation de l’opérateur d’adresse “&” ! Explication orale… Lecture de plusieurs valeurs à la suite…
34
34 Syntaxe : int scanf(“format”, adresse_variable1, adresse_variable2,...); format : %type_carac spécifier le type du nombre à lire (type_carac) par %d : nombre entier %c : un caractère %lf : un réel double précision %f : un réel simple précision … Attention conseil : Ne pas mettre de message, ni “\n” dans “format”. Par exemple, éviter: scanf(”bonjour %d”, &unentier); scanf(“%d\n”, &unentier); IV.3. lire avec scanf
35
35 scanf() : les pièges ! #include main() { double x; int unEntier; printf("Entrer un nombre reel \n"); scanf("%d", &x); printf("Valeur de x %lf \n", x); printf("Entrer un nombre entier \n"); scanf("%d", unEntier); printf("La variable a vaut : %d\n", unEntier); } Erreur sur le type de la variable ou stocker la valeur lue => valeur lue incohérente, ou arrêt fatal du programme Erreur oubli de l’opérateur d’adresse & Le programme veut stocker ce qui est lu à l’adresse « valeur de unEntier »… Mais ou est-ce en mémoire ? Valeur lue incohérente, ou arrêt fatal du programme (« segmentation fault accès mémoire non autorisé) IV.3. lire avec scanf
36
36 Bibliothèque de fonctions mathématiques : #include // sqrt, ln, log10, pow, sin, cos, … Voir man math Les arguments et les valeurs renvoyées par ces fonctions sont de type double. Exemple : double x, y, z ; x = 3.0 ; y = 2.0 ; z = pow ( x, y ) ; // puissance IV.5 compléments divers
37
37 V. Structures de contrôles V.1. Introduction V.2. Conditions booléennes V.3. Structures alternatives V.4. Structures répétitives
38
38 De nombreux problèmes algorithmiques nécessitent : - soit de faire des choix : selon le résultat d’un choix on pourra exécuter un bloc d’instructions ou un autre (exemple : suivant que le discriminant positif OU négatif, faire…). structures alternatives. - soit de répéter de nombreuses fois le même bloc d’instructions : structures répétitives. Dans les deux cas, nous aurons à manipuler des conditions booléennes : - conditions du choix alternatif, - conditions d’arrêt de la répétition. V.1. Introduction
39
39 Une condition est une expression booléenne qui peut être : - soit vraie (Vrai, True). En C, tout entier différent de 0 est « vrai » ; - soit fausse (Faux, False). En C, la valeur entière 0 en C est « faux ». Exemple : les comparaisons entre valeurs comparables : a égal b ? a différent de b ? a supérieur à b ? Opérateurs de comparaison en C : pour les entiers, réels, caractères = = != > = <= a = = b teste l’égalité entre a et b ; a != b teste la différence entre a et b ; a = b affecte b à a (et renvoie la valeur de b ). « = » n’est donc pas un opérateur de comparaison !!! ! V.2. Conditions booléenne V.2.1. Opérateurs de comparaison Piège !
40
40 Opérateurs logiques : 2 expressions booléennes A et B peuvent être associées à l’aide des opérateurs logiques habituels : ET OU NON. A ET B A OU B NON A Opérateurs logiques en C : && || ! Exemples : ( ( 1 = = 2 ) && ( 3 < 5 ) ) est Faux ( ( 1 = = 2 ) || ( 3 < 5 ) ) est Vrai ! ( (1 = = 2) || (5 > 2 )) est Faux VraiFaux Vrai Faux VraiFaux Vrai FauxVraiFaux AVraiFaux NON AFauxVrai Attention aux priorités entre Opérateurs. Utiliser des ( ) si besoin est. ! V.2. Conditions booléenne V.2.2. Opérateurs logiques B A B A
41
41 Condition VRAIFAUX Bloc n°1Bloc n°2 Bloc suite Si (Condition) Alors faire Bloc n°1 Sinon faire Bloc n°2 Fin Si Bloc suite AlgorithmiqueEn langage C if ( Condition ) { // Bloc n°1 ; } else { // Bloc n°2 ; } // Bloc suite ; Un bloc est un ensemble d’instructions regroupées entre {... } Conventions de codage : pensez à indentez vos structures pour la lisibilité !!! V.3. Structures alternatives V.3.1. Si … sinon
42
42 Exemple #include main() { int a,b; printf("Entrer 2 entiers a et b, separes par un espace\n") ; scanf("%d %d", &a, &b) ; if (a>b) { printf("a (%d) est plus grand que b (%d)\n", a, b); } else { printf("a (%d) est plus petit que b (%d) – ou égal\n", a, b); } printf("fin du programme\n"); } V.3. Structures alternatives V.3.1. Si … sinon
43
43 Si (Condition) Alors faire Bloc n°1 Fin Si Bloc suite AlgorithmiqueC if ( Condition ) { // Bloc n°1 ; } // Bloc suite ; Le bloc Sinon est facultatif : Condition VRAIFAUX Bloc suite Bloc n°1 V.3. Structures alternatives V.3.2. Si …
44
44 Si ( Condition1 Alors faire Si ( Condition2 ) Alors faire Bloc n°1 Sinon faire Bloc n°2 Fin Si Sinon faire Bloc n°3 Fin Si Bloc suite Algorithmique En langage C if ( Condition1 ) { if ( Condition2) { Bloc n°1 ; } else { Bloc n°2 ; } else { Bloc n°3 ; } Bloc suite ; if ( Condition1 ) { if ( Condition2) { // Bloc n°1 ; } else { // Bloc n°2 ; } else { // Bloc n°3 ; } // Bloc suite; Indentation : Qui est plus facile à lire ? À comprendre ? À débugger ? V.3. Structures alternatives V.3.3. Si imbriqués
45
45 Si ( Condition1 ) Alors faire Bloc n°1 Sinon Si ( Condition2 ) Alors faire Bloc n°2 Sinon faire Bloc n°3 Fin Si Bloc suite Algorithmique C if ( Condition1 ) { Bloc n°1 ; } else { if ( Condition2 ) { Bloc n°2 ; } else { Bloc n°3 ; } Bloc suite ; if ( Condition1 ) { // Bloc n°1 ; } else if ( Condition2 ) { // Bloc n°2 ; } else { // Bloc n°3 ; } // Bloc suite ; Écriture plus compacte. Surtout lorsque le nombre de cas augmente !!! V.3. Structures alternatives V.3.4. Si … sinon si …
46
46 Quand on a un choix parmi plusieurs valeurs, une syntaxe spécifique existe en C : if ( x = = valeur1 ) { // Bloc n°1; } else if (x = = valeur2) { // Bloc n°2 ; } else { // Bloc n°3 ; } // Bloc suite ; switch ( x ) { case valeur1: // Bloc n°1 ; break ; case valeur2: // Bloc n°2 ; break ; default: // Bloc n°3 break ; } // Bloc suite ; La section default est facultative. Les instructions break sont importantes… sinon l’exécution est surprenante ! x doit être un entier ou un caractère. V.3. Structures alternatives V.3.5. Choix parmi …
47
47 Exemple #include main() { char c ; int prix = 0; printf("\t 1 : Vin rouge\n"); printf("\t 2 : Eau\n"); printf("\t 3 : Rien\n"); printf("\t -----> "); scanf("%c", &c); printf("Vous avez choisi : "); switch (c ) { case '1' : printf("Du rouge\n");prix=100; break; case '2' : printf("De l’eau\n");prix=10; break; case '3' : printf("rien\n");prix=0; break; default : printf("Choix non valide\n"); break; } printf("Pour un prix de %d euros\n", prix); } V.3. Structures alternatives V.3.5. Choix parmi …
48
48 Les structures répétitives, encore appelées boucles, sont utilisées lorsque l’on a besoin de faire exécuter plusieurs fois de suite une opération. Il existe trois type de boucles : - répéter jusqu’à ce qu’une condition soit atteinte, - répéter tant qu’une condition est vérifiée, - répéter n fois avec n connu à l’avance. V.4. Structures répétitives V.4.1. Introduction
49
49 Condition VRAIFAUX Bloc Bloc suite Tant que (Condition) Faire Bloc Fin Tant que Bloc suite AlgorithmiqueC while ( Condition ) { // Bloc ; } // Bloc suite ; Le bloc est répété tant que la condition est vraie. Si la condition n’est jamais vraie, on ne rentre jamais dans le bloc. Si la condition est toujours vraie, on ne sort jamais de la boucle !!! On parle alors de « boucle infinie »… une cause de bug que vous rencontrerez ! ! V.4. Structures répétitives V.4.2. Boucle tant que … faire
50
50 Exemple #include #define MAX 10 main() { int i = 1; int accumulateur = 0; while ( i <= MAX) { accumulateur += i; i ++; } printf("la somme des %d premiers entiers vaut %d\n", MAX, accumulateur); } V.4. Structures répétitives V.4.2. Boucle tant que … faire
51
51 Condition VRAI FAUX Bloc Bloc suite Faire Bloc Tant que ( Condition ) Bloc suite AlgorithmiqueC do { // Bloc ; } while ( condition ) ; // Bloc suite ; Le bloc est répété tant que la condition est vraie. Le bloc est toujours répété au moins une première fois. Si la condition est toujours vraie, on ne sort jamais de la boucle !!! ! V.4. Structures répétitives V.4.3. Boucle Répéter … tant que
52
52 Exemple #include main() { int choix ; do { printf("Choisir parmi : \n"); printf("\t 1 : Vin rouge \n"); printf("\t 2 : Eau \n"); printf("\t 3 : Rien \n"); scanf("%d", &choix); } while ( choix 3 ) ; printf("Votre choix est : %d\n", choix); } V.4. Structures répétitives V.4.3. Boucle Répéter … tant que
53
53 Pour i de début à fin par pas de incr Faire Bloc Fin Pour Bloc suite AlgorithmiqueC for( init ; cond ; incr ) { Bloc ; } Bloc suite ; Quand le nombre de répétition est prévu, on peut utiliser une boucle « pour ». int i; for( i = 1 ; i <= 101 ; i = i + 2 ) { printf("%d \n », i); } InitialisationIncrémentationCondition de continuation Initialisation Incrémentation Condition VRAIFAUX Bloc suite Bloc V.4. Structures répétitives V.4.4. Boucle Pour Habituellement, les compteurs sont des entiers nommés i, j, k, …
54
54 Lorsqu’on programme, il faut être capable de se « substituer à l’ordinateur ». C’est ce qu’on appelle « tracer » un programme. Cela est particulièrement vrai lorsqu’on manipule une boucle ou la mémoire (pointeurs, tableaux, etc.). Avec les boucles, trois catégories de questions à se poser sont : - est-ce que la boucle se termine bien ? Ou est-ce une boucle infinie ? Ou est « l’incrément » dans la boucle ? Est-ce que la condition et bien modifiée par la boucle, et la condition d’arrêt va-t-elle être atteinte ? - que se passe-t-il lors des premiers passages dans la boucle ? Que vaut l’incrément ? Qu’est-ce que fait le programme ? Conseil : toujours « tracer » les deux premiers passages dans la boucle - que se passe-t-il lors des derniers passage dans la boucle (avant dernier, dernier…) ? Que vaut l’incrément ? Qu’est-ce que fait le programme ? Conseil : toujours « tracer » les deux derniers passages dans la boucle V.5. Savoir tracer un programme Le cas des boucles
55
55 Que font ces exemples ? #include main() { int i=0; while (i<100) { printf("%d ",i); i += 2 ; } printf("\n"); } Le cas des boucles #include main() { int i=5; while (i<100) { printf("%d\n",i); i = i -1 ; } printf("\n"); } #include main() { int i=5; do { printf("%d\n",i); i -= 1 ; } while (i<0) ; printf("\n"); } V.5. « Tracer » un programme
56
56 Que fait cet exemple ? Remarque : une boucle pour est toujours équivalente à une boucle while… main() { int i, n ;// deux entiers int s=0;// accumulateur //saisie printf("n ?\n"); scanf("%d", &n); // calcul for (i = 1 ; i <= n ; i++) { s = s + i; } //affichage printf("n %d - s %d \n", n, s); } Le cas des boucles V.5. « Tracer » un programme
57
57 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 d’une fonction
58
58 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 d’entrée (des arguments) : VIII.1.1 En maths …
59
59 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 d’instructions 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 d’entrée (des arguments). Les avantages sont nombreux : - réutilisation du même bloc d’instructions (sur des valeurs différentes), - réutilisation dans d’autres 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…
60
60 VIII.2. Le mécanisme de base #include "stdio.h"// bibliothèque de fonctions d’entrées/sorties float discr ( float x, float y, float z) { float discriminant ; // bloc d’instructions (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 l’exécution : c’est le point d’entrée du programme. 1/ appel de la fonction discr 2/ retour VIII.2.1. Exemple
61
61 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, c’est la valeur de la variable locale discriminant Le type retourné. C’est le type de la valeur retournée. Le type peut être void (aucune valeur retournée) Corps de la fonction. Bloc d’instructions qui seront exécutées à chaque appel de la fonction. VIII.2.2. Déclaration d’une fonction VIII.2. Le mécanisme de base Variable locale
62
62 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
63
63 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 l’appel de fonction VIII.2. Le mécanisme de base
64
64 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 = ???
65
65 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 n’est pas la même variable "a" ! Elle est ailleurs en mémoire ! ! a = 4 b = 3 d = ???
66
66 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 d‘habitude dans le « main » VIII.2.4. ce qui se passe… VIII.2. Le mécanisme de base a = 4 b = 3 d = ???
67
67 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 d’appel 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
68
68 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" n’est pas définie ici ! ! Erreur : "d" n’est pas définie ici ! ! Ce code ne compile pas car : Rappel : Ce n’est pas le même a ! Ce n’est pas le même b ! les variables sont locales au bloc ou elles sont définies ( entre { } )
69
69 Lorsque l’on rencontre une instruction return, l’exécution de la fonction est arrêtée. On reprend l’exécution des instructions qui suivent l’appel 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
70
70 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
71
71 É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 »
72
72 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, c’est 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 »
73
73 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 »
74
74 #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 »
75
75 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 »
76
76 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 l’adresse 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 »
77
77 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 (c’est à dire la valeur de *p_b) VIII.3.3. Exemple VIII.3. Passage « par valeur » et « par adresse »
78
78 Une variable “a” définie dans une fonction n’est 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 l’appel de la fonction. Pour modifier la valeur d’une variable lorsqu’on 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é
79
79 Avant même d’écrire le corps d’une fonction, il faut être très sur le « contrat » qu’elle doit remplir, c’est à dire : Son rôle (ce qu’elle 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 d’utilisation de la fonction, et en particulier l’état que doit respecter le système avant l’appel de la fonction : préconditions L’état que dans lequel sera le système après l’appel de la fonction Les cas d’erreur, et la façon dont la fonction réagit en cas d’erreur. VIII.4.1. Introduction VIII.4. Notion de contrat de fonction
80
80 Le « contrat de fonction » peut être formalisé à l’aide de commentaire. Exemple : définir une fonction résolvant dans une équation du second degré. Le « contrat » d’une telle fonction pourrait s’écrire : VIII.4. Notion de contrat de fonction VIII.4.2. Exemple Prototype Contrat
81
81 VIII.4. Notion de contrat de fonction VIII.4.3. Utilisation du prototype comme promesse d’existence 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 ; }
82
82 /* ******************************************************* 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 l’essentiel, 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 d’un programme
83
83 Tableaux
84
84 Tableaux Tableaux 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]
85
85 Tableaux 18 4 0 2 0 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 l’endroit 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 : c’est l’endroit 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, c’est une erreur à l’execution du programme mais pas d’erreurs à la compilation
86
86 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]); } 18 4 0 2 0 0xbffff384&tab[0] Adresse &tab[1] &tab[2] &tab[-2] … &tab[9] &tab[900] 0xbffff388 0xbffff38c 0xbffff3a8 0xbffff37c 0xc0000194
87
87 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]); }
88
88 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 l’octet 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
89
89 Pointeurs
90
90 Pointeurs Variable contenant l’adresse d’un autre objet (variable ou fonction) Adresse : numéro d’une case mémoire Déclaration : type_pointé* identificateur; Exemples : int* p1; /* p1 contient l’adresse d’un entier */ double* p2; /* p2 contient l’adresse d’un réel */ ATTENTION : un pointeur doit toujours être initialisé avant d’être utilisé = Il doit contenir une adresse légale : soit celle d’un objet existant soit celle obtenu par une demande d’allocation dynamique soit NULL, qui est la valeur 0. Il est interdit de lire et écrire à l'adresse 0 Remarque : on affiche la valeur d’un pointeur en hexadecimal par printf("Voici la valeur du pointeur %p\n",p );
91
91 Pointeurs Exemple : p1.c main() { int i=0; int* p1=NULL; /* p1: pointeur sur un entier */ p1 = &i; /*p1 pointe i, ie contient l’adresse 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
92
92 Opérations sur pointeurs Affectation : donner une valeur au pointeur, celle d’une 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<p2 sur un meme tableau, p1 est il avant p2 Arithmétique Adresse + entier ==> adresse : p 1 +1 est l’adresse 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 d’octets
93
93 Adresse et tableaux Nom du tableau : adresse du tableau Accès à un élément t[i] : on part de l‘adresse de début du tableau, on ajoute i et on obtient l’adresse du ième élément. L’élément est obtenu par l’opérateur d’indirection * Conséquences L’élément t[i] s ’écrit aussi *(t+i) L’adresse de t[i] s’écrit &t[i] ou bien (t+i)
94
94 Adresses et tableaux Exemple : p3.c main() { int tab[10]; int i; for (i=0; i<10; i++) tab[i]= 2*i; puts("Voici l’element d’indice 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]); } 18 4 0 2 0 0xbffff364&tab[0] Adresse &tab[1] &tab[2] … &tab[9] 0xbffff368 0xbffff36c 0xbffff388 2 manières différentes d'ecrire l'adresse de t[i]
95
95 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<tab+5) { printf(" Pointeur: %p ",,p); printf(" Valeur %d",*p); *p=234; printf("Valeur modifiee %d\n",*p); p++; } 9 5 0 3 1 0xbffff384&tab[0] Adresse &tab[1] &tab[2] … &tab[4] 0xbffff388 0xbffff38c 0xbffff394 7 &tab[3] 0xbffff390 i= 5 &i 0xbffff398 p= 0 &p 0xbffff39c 0xbffff384 tab[0]=234 0xbffff388 tab[1]=234 0xbffff38c tab[2]=234 0xbffff390 tab[3]=234 tab[4]=234 0xbffff394
Présentations similaires
© 2025 SlidePlayer.fr Inc.
All rights reserved.