Types de données et représentation

Slides:



Advertisements
Présentations similaires
Premier programme en C :
Advertisements

La boucle for : init7.c et init71.c
Chapitre annexe. Récursivité
Les fonctions A quoi ça sert ?
Rappels C.
Portée des variables VBA & Excel
Calculs de complexité d'algorithmes
Formation C débutant. Notion de compilation source.c executable Phase de compilation Fichier de texte brut, inexploitable directement par la machine Fichier.
La classe String Attention ce n’est pas un type de base. Il s'agit d'une classe défini dans l’API Java (Dans le package java.lang) String s="aaa"; // s.
GEF 243B Programmation informatique appliquée Types dérivés, structures et tableaux §
GEF 243B Programmation informatique appliquée
1 UMLV 1. Introduction 2. Hachage ouvert 3. Hachage fermé 4. Implémentation des fonctions Méthodes de hachage.
Cours n°2M2. IST-IE (S. Sidhom) UE 303 Promo. M2 IST-IE 2005/06 Conception dun système d'information multimédia Architecture trois-tiers : PHP/MySQL &
C.
Paramètres et pointeurs
Structures et unions types énumérés Qu'est-ce qu'une structure
Les pointeurs Manipulation d'adresses et de ce qui est contenu dans ces adresses Très important, fondamental même en C mauvaise réputation : 'dur à comprendre',
Tableaux Certains problèmes nécessitent beaucoup de variables du même type. Exemple : relevé de températures matin et soir dans 10 villes pour 10 jours.
Chap. 1 Structures séquentielles : listes linéaires
FLSI602 Génie Informatique et Réseaux
8. Les tableaux P. Costamagna – ISEN N1.
Algorithme et programmation
Initiation à la programmation et algorithmique cours 3
Principes de programmation (suite)
ALGORITHMES RECURSIFS
Cours 7 - Les pointeurs, l'allocation dynamique, les listes chaînées
Récursivité.
Les méthodes en java Une méthode est un regroupement d’instructions ayant pour but de faire un traitement bien précis. Une méthode pour être utilisée.
RESUMES Module II1 SOMMAIRE CYCLE 1 : Saisir – Afficher – Données
8PRO100 Éléments de programmation Allocation dynamique de la mémoire.
TRAITEMENT DE STRUCTURES
IFT-2000: Structures de Données Listes chaînées Dominic Genest, 2009.
Quest-ce quune classe dallocation? Une classe dallocation détermine la portée et la durée de vie dun objet ou dune fonction.
Les pointeurs Enormément utilisé en C/C++ ! Pourquoi? A quoi ça sert?
Les pointeurs Modes d’adressage de variables. Définition d’un pointeur. Opérateurs de base. Opérations élémentaires. Pointeurs et tableaux. Pointeurs et.
IFT-2000: Structures de données
Introduction à l’algorithmique
Contrôle de types Les types en programmation Expressions de types Un contrôleur de types Equivalence de types Conversions de types Généricité.
IFT 6800 Atelier en Technologies d’information
Chapitre 9 Les sous-programmes.
8PRO100 Éléments de programmation Les types composés.
Structures de données IFT-2000 Abder Alikacem La récursivité Semaine 5 Département dinformatique et de génie logiciel Édition Septembre 2009.
Procédures et fonctions
Plan cours La notion de pointeur et d’adresse mémoire.
Structures de données IFT-2000 Abder Alikacem La récursivité Département d’informatique et de génie logiciel Édition Septembre 2009.
L’essentiel du langage C
Structures des données
Le langage C Structures de données
2.1 - Historique Chapitre 2 : Introduction au langage C++
Les pointeurs L'opérateur &.
Le langage C Rappel Pointeurs & Allocation de mémoire.
Ch. PAUL - Piles et Files à l'aide de listes chainées
LES PILES ET FILES.
Les Pointeurs et les Tableaux Statiques et Tableaux Dynamiques
Notions de pointeurs en C
O-notation 1. Introduction 2. O-notation 3. Opérations 3.1 Somme 3.2 Produit 4. Règles générales 5. Exemple 6.Analyse des algorithmes récursifs 6.1 Dilatation.
Les adresses des fonctions
SIF-1053 Architecture des ordinateurs
Un survol du language C.
1 Structures des données. 2  Le tableau permettait de désigner sous un seul nom un ensemble de valeurs de même type, chacune d'entre elles étant repérée.
Tutorat en bio-informatique
ETNA – 1ème année Guillaume Belmas –
ISBN Chapitre 10 L'implémentation des sous- programmes.
Cours LCS N°4 Présenté par Mr: LALLALI
Classe 1 CSI2572 Autres modificateurs de déclaration de variables: & volatile & register & static & auto & extern & const volatile Indique au compilateur.
8PRO107 Éléments de programmation Les adresses et les pointeurs.
PRO-1027 Programmation Scientifique en C
Informatique 2A Langage C 5ème séance. Déroulement de la séance 5 1 ère partie Étude des chaînes de caractères 2 ème partie Les structures 3.
Informatique 1A Langage C 6 ème séance 1. Objectifs de la séance 6  Allocation dynamique de mémoire  Application à la création de tableaux 2.
Transcription de la présentation:

Types de données et représentation B. Rouzeyre, Polytech'ERII Langage C Types de données et représentation

Représentation des algorithmes Organigramme : façon graphique de représenter le déroulement d'un calcul 1. Action de base 2. Séquencement 3. Bloc et niveau de détail Action 1 Action 1 Action 2 Action 11 Action 1 Action 12 Action 2

Organigramme Σ Evi = 1 et Evi.Evj = 0 4. Sélection Structuré Non structuré Action 1 Action 1 Ev.1 Action 21 Critère ? Critère ? Ev.2 Action 22 Ev.1 Ev.2 Ev.3 Ev.3 Action 23 Action 21 Action 22 Action 23 Action 3 Action 3 Se lit de haut en bas et de droite à gauche Se lit en suivant les fils

Organigramme 4. Cas particulier : alternative Structuré Non structuré Action 1 Action 1 Vrai Action 21 Critère ? Critère ? Vrai Faux Faux Action 22 Action 21 Action 22 Action 3 Action 3

Organigramme 5. Itérations : exprime le fait que l'on répète une action 5.1 "Tant que" Action 1 Action 1 Tant que critère vrai Vrai Faux Critère Action 2 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 Remarque : éventuellement l'action 2 n'est pas exécutée

Organigramme 5. Itérations : 5.2 "Jusqu'à ce que" Action 1 Action 1 Jusqu'à ce que critère soit vrai Action 2 Action 2 Faux Vrai Critère Action 3 Action 3 1 – On exécute l'action 2 – s'il est faux, retour en 1 2 bis – s'il est vrai, on passe à la suite Remarque : l'action 2 est exécutée au moins une fois

e <- valeur suivante de E Organigramme 5. Itérations : 5.3 "Pour tout e ε E" Action 1 Action 1 Pour chaque e de E Vrai Faux e ε E ? Action 2 Action 2 Action 3 Action 3 e <- valeur suivante de E l'action 2 est exécutée autant de fois qu'il y a d'éléments dans E

Organigramme : conseil utiliser que les primitives précédentes intérêt : traduction (presque) directe en C ne pas faire de spaghetti : traduction difficile debug impossible résultat incompréhensible => 0 à l'examen Action 1 Vrai Faux e ε E ? Action 2 Action 3 e <- valeur suivante de E

Organigramme : exemple x = premier S=0 x = premier S=0 x pair ? fin = faux S = S+ x Tant que x pair et fin = faux S = S+ x fin = v x dernier ? Vrai x dernier ? Faux x= suivant x= suivant Imprimer S Imprimer S KO OK

Codage d'un algorithme en C Les actions ou "instructions" en langage C ne sont en fait que des opérations élémentaires (+,-, * etc…) sur des données unitaires. Chaque instruction est suivie d'un ; Le début d'un bloc est signalé par un { , la fin d'un bloc par }. Un bloc devient l'équivalent syntaxique d'une instruction. Ex : { x=1; y=2; z=x+y; printf("%d",z); } ; x = 1 y = 2 Calcul z = x+y imprimer z optionnel

Codage d'un algorithme en C Alternative if (expression) action1 ; else action2 ; fin de l'instruction if … if (z == 3) { printf("calcul juste"); z=z+10; } else printf ("problème"); printf ("%d",z); calcul "calcul juste" Vrai z=3 ? z = z+10 Faux "problème" Imprimer z

Codage d'un algorithme en C Cas particuliers de alternative calcul … if (z == 3) ; else printf("problème); printf ("%d",z); Vrai z=3 ? "problème" Faux Imprimer z

Codage d'un algorithme en C Cas particuliers de alternative … if (z == 3) { printf("calcul juste"); z=z+10; } else; printf ("%d",z); ou bien }; calcul "calcul juste" Vrai z=3 ? z = z+10 Faux Imprimer 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 (si besoin passer à plusieurs if imbriqués) Action 1 z=3 "ok" z ? 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 TANT QUE : while (condition) action; x=1; while (x <=10) { printf("%d\n",x); x=x+1; }; … x=1 Tant que x ≤ 10 imprimer x x=x+1 JUSQU'À CE QUE : do action while (condition); x=1; do { printf("%d\n",x); x=x+1; } while (x <= 11); … Jusqu'à ce que x >11 x=1 imprimer x x=x+1

Codage d'un algorithme en C POUR Pour x E {1,2,…,10} for(x=1; x≤10; x=x+1) printf("%d\n",x); imprimer x équivalent TANT QUE x=1; while (x <=10) { printf("%d\n",x); x=x+1; }; … x=1 Tant que x ≤ 10 imprimer x x=x+1

Types de données 3 types de base Remarques : caractères ex : 'a', '1', '\n' entier relatifs ex : 0 , -12, 328 réel 3.14, 2.3 e+4 Remarques : Pas de booléen (vrai, faux) Pas de type chaînes de caractères prédéfini

Type caractère Caractère : Symboles alphanumériques (a,z,!,1,9) + caractères spéciaux (retour à la ligne, beep, etc..) Un caractère est représenté sur un octet (8 bits) suivant la table ASCII (American Standard Code for Information Interchange) ex : 'a' = 9710 = 6116 = 0110 00012 Table ASCII ex : code ASCII du 'A' = 65 'A'<'B'<……..< 'Z' '0'<'1'<'2'<…..<'9' 'a'<'b'<……….<'z' Déclaration de variable de type caractère char c; c = 'a'; Constante de type caractère #define caractère_a 'a'

Déclaration d'une variable caractère : Table ASCII Remarques : les chiffres sont codés suivant un ordre croissant (48 à 57) idem pour les lettres (65 à 90, 97 à 122) code des majuscules est inférieur au code des majuscules (différence constante = 32) les codes supérieurs à 128 dépendent de la langue : é, ö , ä, æ, œ etc… Déclaration d'une variable caractère : char c; c='a'; …..

Type caractère Caractères spéciaux (retour à la ligne, tabulation etc..) Exemple : retour à la ligne = CR = code ASCII 13 char retour; retour = 13; ou bien retour = '\n'; Conventions \n : retour à la ligne \t : tabulation \f : nouvelle page \' : apostrophe \0 : caractère nul (indique la fin d'une chaîne de caractères)

Les entiers : entiers naturels Codage sur 2 ou 4 octets suivant le calculateur Sur deux octets on peut coder les nombres de 0 à 216-1 (0 à 65535) Nombre représenté en base 2, les bits sont rangés dans des cellules correspondant à leur poids, on complète à gauche par des 0 Exemple : 13 = 8 + 4 +1 = 1*23+1*22+0*21+1*20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 Déclaration d'une variable entier naturel x unsigned int x; short unsigned int x; (on force sur 16 bits) long unsigned int x; (on force sur 32 bits)

Type entier relatif Implantation sur 2 ou 4 octets suivant le compilateur Représentation sur n bits : codage complément à 2 Déclarations int a,b; …. a = 1; b= 3;

Type entier relatif Codage complément à 2 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

le plus grand entier positif est 2n-1-1 Complément à 2 Représentation non symétrique : le plus petit nombre n’a pas d’opposé : sur n bits le plus grand entier positif est 2n-1-1 le plus petit entier négatif est -2n-1 sur 3 bits : 000 111 001 -1 1 110 -2 2 010 3 -3 -4 101 011 100

Codage complément à 2 Remarques Extensions 1/ Complément à 2 de x = Complément à 1 de x + 1 représentation de –3 ? 3 = 0000 0000 0000 0011 c1(3) = 1111 1111 1111 1100 +1 = 0000 0000 0000 0001 c1(3) +1= 1111 1111 1111 1101 2/ Représentation 16 bits = > 32 bits x >0 = 0000 0000 0000 0011 => x = 0000 0000 0000 0000 0000 0000 0000 0011 x <0 = 1111 1111 1111 1101 => x = 1111 1111 1111 1111 1111 1111 1111 1101 Extensions int 2 ou 4 octets ? problème de portabilité short int x; 2 octets long int x ; 4 octets unsigned int x ; bit de signe utilisé unsigned short int x ; unsigned long int x ;

Type réel Déclaration Implantation sur 4 octets float x,y; x = 3.14; y = 2.0 e+3; Implantation sur 4 octets Représentation suivant le compilateur en général mantisse exposant (norme IEEE) 10-38 < x < 1038 Extension : double x; x est codé sur 8 octets

Codage des réels : virgule flottante, Norme IEEE Nombre réels Codage des réels : virgule flottante, Norme IEEE flottant stocké sous la forme M * BE M : Mantisse ; B : Base ; E : Exposant exemple : 123 . 103 = 123 000 Représentation IEEE 754 (signe 1 bit, exposant et mantisse sur 32 ou 64 bits pour simple et double précision) SM : signe de la mantisse : 1 bit Eb : exposant biaisé : 8 ou 11 bits M : Mantisse : 23 ou 52 bits SM Eb M

Signe : bit de poids fort (0 = + ; 1 = -) Exposant Mantisse et exposant Signe : bit de poids fort (0 = + ; 1 = -) Exposant placé avant la mantisse pour simplifier les comparaisons (pour ceci il ne doit pas être représenté en complément à deux : -1 > 2) sur 8 bits : 0..256 sans signe mais biaisé de 127 (simple précision) : Eb = 1 ⇒ E = 1 – 127 = -126 Eb = 254 ⇒ E = 254 – 127 = 127 les exposants 255 (erreur) et 0 (nb dénormalisé) sont interdits Mantisse normalisée : bit de poids fort n’est pas 0 et un seul chiffre avant la virgule ex : 11,012 = 1,101 * 21

Virgule Flottante Comme le bit de poids fort de la mantisse est nécessairement 1 : on ne l’indique pas (gaspillage de place), il est implicite Mantisse partie fractionnaire = f1f2 …fn ⇒ m = 1,f1f2…fn nombre x = (-1)SM * 1,M * 2Eb-127 Exemple x = (-2,5)10 = -1,01*212 SM = 1 ; E= 1 => Eb= 128 = 1000 0000 ; m=1,01 => M =010…….0 Déclaration de variables réelles : float x; double y; SM Eb M f1,f2,……fn

Variables Variable : élément de mémorisation élémentaire Toutes les variables doivent être déclarées suivant un type int a, b; ou bien int a; int b; float x; char caractere; Identificateur de variables (noms) Le premier caractères doit être une lettre Les autres caractères sont les lettres (majuscules et minuscules), les chiffres et le caractère _ Majuscule / minuscule significatifs Exemples : Pi, pi3_14, a2B3 : corrects 2x,i-E : incorrects A1  a1

Variables Exemple : Initialisation des variables char a; int un; a = 'a'; un =1; a = '1'; Initialisation des variables à l'exécution : int i; …. i = 0; à la compilation int i = 0;

Conversion de 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.3; // i prend la valeur -3 i = (int) 3.4; // + propre

Conversion de types : application Passer au caractère suivant char c; c ='a'; c = c+1; // calcul fait en entier puis résultat converti en char c = (char) ((int) c+1) ; //+ propre Conversions majuscule<-> minuscule c='t'; // conversion en Majuscule c=c-32; // c contient 'T' ou mieux c=c-('a'-'A'); c=c+1; // c contient 'U' // conversion en minuscule c=c+32; ou c=c+('a'-'A') if ((c >= 'a') && (c <= 'z')) c = c-('a'-'A');

Tableaux Lorsque on veut mémoriser plusieurs données de même type, on peut utiliser un tableau c-à-d on regroupe sous un même nom plusieurs informations Exemple de déclaration d'un tableau d'entiers int tab[100]; int : type des éléments du tableau tab : identificateur (nom du tableau) 100 : nombre d'éléments du tableau (dimension) tab peut recevoir 100 entiers identifiés de 0 à 99 (attention !) le premier est tab[0] le second tab[1] .. le dernier tab [99]

Tableaux Utilisation Exemples : chaque élément du tableau est accessible par un indice qui doit être de type entier, quelque soit le type des éléments du tableau exemples : int i ; tab[2] 3eme élément du tableau tab[2+3] 6eme élément du tableau tab[i] i+1eme élément du tableau Exemples : stocker les 100 premiers nombres pairs : 0,2,4,...,196,198 int i, t[100]; for (i=0; i < 100; i=i+1) t[i]= 2*i;

Tableaux Remarques: 1/ chaque élément du tableau s'utilise comme une variable tab[3] = 2; 2/ le nombre maximum d'éléments du tableau (dimension) 1/ doit être fixé à la compilation 2/ ne peut être modifié pendant l'exécution 3/ Pas d'opérations sur les tableaux en tant que tels

Parcours des éléments d’un tableau Parcours du premier au dernier int i; /* l’indice de balayge doit être un entier */ float t[100]; /* le type du tableau est quelconque */ for (i=0; i < 100; i=i+1) // ou bien for (i=0; i <= 99; i=i+1) t[i]= …….; Parcours du dernier au premier for (i=99; i >= 0; i=i-1) // ou bien for (i=0; i <= 99; i=i+1)

La dimension Bonne pratique de programmation int i; int t[100]; for (i=0; i < 100; i=i+1) t[i]= 100; Pb : modification du pgm, changement de la taille du tableau malaisée #define TAILLE 100 int t[TAILLE]; for (i=0; i < TAILLE; i=i+1) Il suffit de changer TAILLE

Exemples Point de l'espace 1ere solution : float x,y,z; 2eme solution float pt[3]; pt[0] pour x, pt[1] pour y, pt[2] pour z Mémorisation des 100 premiers nombres pairs et impairs: int pairs[100], impairs[100]; int i; for (i=0; i<100;i=i+1) { pairs[i]=2*i; impairs[i]=2*i+1; }

Chaînes de caractères En c, pas de type prédéfini chaîne de caractères. En pratique on utilise des tableaux de caractères. Convention : le dernier caractère utile est suivi du caractère \0 (de code ascii 0) Exemples : char t[10]; (9 caractères max, puisque une case réservée pour \0; strcpy(t,"abcdefghi") chaque lettre est accessible par l'indice char t[12]; strcpy(t,"abcdefghi"); Initialisation char t[12]= "abcdefghi"; ou char t[]= "abcdefghi"; Constante chaîne de caractères #define ceciestuntexte "azssddqsdqsd" 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' ?

Définitions de type utilisateur typedef int entier; (entier est maintenant un type) entier i; typedef int tableaude20[20]; tableaude20 t;  int t[20];

Les expressions en langage C

Expressions : introduction Remarque : expression  instruction une instruction indique à l'ordinateur de mener une action expression = élément syntaxique Expressions : variable ou constante, ex : x, 3 constituées à l'aide d'opérateurs : x+ y conversion de type, opérateurs arithmétiques, de taille, relationnels et logiques, affectation, bit-à-bit, conditionnels, adresse

Expressions Une expression représente une donnée élémentaire : constante, variable, un élément de tableau, la référence à une fonction ou à une valeur, le résultat d'un calcul etc …. Exemples 3 a+b x=y c = a+b x <= y x == y i++ sin(3.14) Toute expression a une valeur

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 x= a; a= x+1; c = a; -> c='b'

Opérateurs arithmétiques Opérateurs bi-opérandes + , - * , / , % (modulo) Les opérandes doivent être des valeurs numériques. entier opérateur entier -> résultat entier réel opérateur réel -> résultat réel entier opérateur réel -> résultat réel Exemples int a,b; a=10; b= 3 a+b 13 a-b 7 a*b 30 a/b 3 (division euclidienne) a%b 1 float a,b; a=12.6; b= 3.0 a+b 13.6 a-b 9.6 a*b 37.8 a/b 4.2 (division réelle) a%b erreur de syntaxe

Opérateurs arithmétiques - 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 int ou float) Conversion majuscule  minuscule Exemple : char c = 'a'; c = c+1; => c = 'b' mécanisme : c+1 = 98 + 1 =99 c = code ascii 99 = 'c' Exemple : char c ; if (c >= 'a' && c <='z') c = c-32 (ou bien c = c + 'A' –'a') if (c >= 'A' && c <='Z') c = c+32 (ou bien c = c - 'A' +'a')

opérateurs arithmétiques Opérateurs unaires (un opérande) a/ signe : + , - exemple : a = -a; b/ incrémentation, décrémentation : ++ (+1) , -- (-1) exemple : int i =1; ++i; printf("%d",i) ; -> 2; Syntaxes : ++i ou i++ ++i : la valeur de i est d'abord incrémenté, la valeur résultat est utilisée dans l'expression courante i++ : la valeur courante de i est utilisée dans l'expression courante, puis i est incrémenté

++ et -- Exemples Conclusions : 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 Attention : affectation et test d'égalité if (a =1) instruction1; else instruction2; L'instruction1 est toujours déclenchée. a = b = 3; (évaluation de droite à gauche)

Opérateurs d'affectation Affectation et opération : +=, -=, *=, /=, %=,<<= , >>=, &=, |=, ^= Syntaxe : variable opérateur expression équivalent à : variable = variable opérateur expression Exemple : int i; i= 3; i+=2; (i=i+2;) printf("%d\n",i); -> 5

Opérateurs relationnels et logiques Valeur logique : 0 : faux  0 : vrai exemple : if (3) traitement1 ; else traitement 2; équivalent à : traitement1; Relationnels : >= , > , == , <, <= , != La valeur de l'expression est 1 si l'expression est vraie , 0 si elle est fausse Exemple : 2 < 3 vaut 1 , 2 > 3 vaut 0 Attention à la confusion : test d'égalité == et l'affectation = ex : if (x=0) traitement 1; // au lieu de x==0 else traitement 2; Conséquence: non seulement le traitement 1 ne sera jamais exécuté mais en plus x vaudra 0 quelle que soit sa valeur initiale Logiques : && "et" logique , || "ou" logique, ! "non" logique Dans l'évaluation de l'expression, 0 est considéré comme la valeur logique "faux", toute valeur  0 comme la valeur logique "vraie" La valeur de l'expression est 1 ou 0 Exemples: 2 && 0 vaut 0 et donc est faux 2 || 0 vaut 1 et donc est vrai !0 vaut 1 !4 vaut 0

Opérateurs bit à bit Opèrent sur les représentations des valeurs & et , | ou, ^ ou-exclusif, ~ complément à 1 , << décalage à gauche, >> décalage à droite, Attention : &  && Exemples 5 0000 0000 0000 0101 20 0000 0000 0001 0100 5 & 20 0000 0000 0000 0100 => 5 & 20 => 4 5 | 20 0000 0000 0001 0101 => 5 | 20 => 21 5 ^ 20 0000 0000 0001 0001 => 5 ^ 20 => 17 ~5 1111 1111 1111 1010 => -6 Affectation/bit-à-bit : &=, |=, ^=, ~=

Décalages Décalages à gauche a << b : a est décalé à gauche de b bits (les bits ajoutés valent 0) 5 << 2 0000 0000 0001 0100 20 un décalage d'une position à gauche correspond à une multiplication par 2 à droite a >>b : a est décalé à droite de b bits (les bits insérés valent le bit de poids fort) 14 0000 0000 0000 1110 14 >> 2 0000 0000 0000 0011 3 -6 1111 1111 1111 1010 -6 >> 1 1111 1111 1111 1101 -3 un décalage d'une position à droite correspond à une division par 2 (en respectant le signe)

Opérateur conditionnel Syntaxe expression1 ? expression2 : expression3 à peu près équivalent à : if (expression1) expression2; else expression3; Exemple : maximum = (x>y) ? x : y; if (x>y) maximum =x ; else maximum = y; Conseil : ne pas utiliser (peu clair)

Opérateurs d'adressage Adresse de : & Syntaxe : &variable , donne l'adresse mémoire de la variable Exemple : int i,adr; adr = &i; ne pas confondre avec le "et" bit à bit Dont l'adresse est : * Syntaxe *expression : donne le mot mémoire dont l'adresse est donnée par l'expression i=1; printf("%d", *adr); -> 1

Opérateur de taille : sizeof Donne la taille de l'implantation 2 syntaxes 1/ sizeof expression exemple : int i,j ; j= sizeof i; -> 2 ou 4 (octets) 2/ sizeof (type) exmples : typedef char tab[100]; tab t; int n; n = sizeof(int), -> 2 ou 4 (octets) n = sizeof(tab) -> 100 (char)

Opérateurs divers ( ) : force l'ordre des calculs ex : 1 + 2 * 3 -> 7 (1+2) * 3 -> 9 [ ] pour les tableaux t[2] équivalent à *(t+2) -> et . (opérateurs sur structures, + tard)

Priorité des opérateurs   Priorité des opérateurs Priorité Opérateurs Description Associativité 15 () [ ] -> .  opérateurs d'adressage -> 14 ++ -- incrément/décrément <- ~ complément à un (bit à bit) ! non unaire & * adresse et  valeur (pointeurs) (type) conversion  de type (cast) +- plus/moins unaire (signe) 13 * / % opérations arithmétiques  -> 12 + -          "" 11 << >> décalage bit à bit 10 < <= > >= opérateur relationnels 9 == != 8 &  et bit à bit 7 ^  ou exclusif bit à bit  6 | ou bit à bit  5 &&  et  logique 4 || ou logique 3 ?: conditionnel 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() getchar() : sert à la lecture de caractères isolés la valeur de getchar() est le code ascii du caractère lu utilisation char c; c = getchar(); Exemple :on veut lire et mémoriser 2 caractères donnés sur 2 lignes différentes char c1,c2; c1 = getchar() // acquiert le 1er caractère getchar (); // filtre le <cr> c2 = getchar () // acquiert le 2ème caractère

scanf () Sert à la lecture de données et convertit la succession de caractères donnés en entiers, flottants, caractères, chaîne de caractères Syntaxe : scanf (format,arg1,arg2,……,argn) le nombre d'arguments est quelconque arg1, arg2, …, argn sont les adresses des variables dans lesquelles on stocke les valeurs lues variable simple (entier,caractère,flottant) : &v chaîne de caractères = tableau : v le format est une chaîne de caractères précisant le type des arguments afin de convertir la suite de caractères lus dans les arguments

Scanf : format Format chaîne de caractères composée de caractères % suivis d'une lettre et éventuellement séparés par des blancs la lettre indique le type de conversion à effectuer exemple : int i; float x; scanf("%d %f", &i,&x); le %d indique que le premier argument est un entier le %f indique que le second est un réel réponse : 2312.6 23 est converti en entier et stocké dans i 12.6 est stocké en flottant et stocké dans x

Scanf : format caractères de conversion Exemple c : donnée de type caractère simple d : donnée de type entier relatif f : donnée de type flottant e : donnée de type flottant en notation exponentielle x : donnée de type entier hexadécimal s : donnée de type chaîne de caractères (tabl. de char terminé par \0) Exemple char t[20]; int i ; float x; scanf ("%s %d %f", t,&i,&x); réponses : 1/ abcde 123 0.05 2/ abcde 123  0.05 3/ abcde 123 

Scanf : rôle des caractères , , tabulation, 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); rep2 : abc c1= 'a' , c2 = 'b', c3= 'c' rep2 : ab  c c1= 'a' , c2 = 'b', c3= 'c'

Scanf : compléments Nombre de caractères lus faire précéder le caractère de format du nombre de caractères (max) désiré Exemples int i,j,k; scanf("%3d %3d %3d",&i,&j,&k); rep1 : 123 i=1 j=2 k=3 rep2 : 123456789 i=123 j=456 k=789 rep3 : 123456789 i=123 j=456 k=789 rep4 :123456789 i=123 j=4 k=567 int i; float x;char c; scanf("%3d%5f%c,&i,&x,&c); rep : 10234.567t i=10 x=234.5 c='6'

Scanf : compléments Lecture d'une chaîne de caractères char ch[50]; scanf("%s",ch) // pas de & rep : abcdefghi Le caractère 0 de fin de chaîne est ajouté automatiquement Exercice : faire l'équivalent de scanf("%s",ch) à l'aide de getchar() Saut conditionnel de caractères : %*d, %*f permet de sauter des données correspondantes dans la réponse exemple : int i,j; char c; scanf("%d  %*d  %c",&i,&c); rep1 : 1234x i=12 c='x' rep2 : 12x i=12 c='x' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' ?

scanf : compléments Filtre sur chaînes de caractères [ caractères admissibles] ou [^caractères non admissibles] exemples char ch[100]; scanf("%[0123456789]",ch); rep : 32a48 ch="32" scanf("%[^0123456789]",ch); rep : 32a48 ch="a" raccourcis : [0123456789] ou [0-9] [abcdefg] ou [a-g]

Ecriture 2 fonctions de base : putchar() et printf() putchar(caractère) Exemple char c; c='a'; putchar ( c ); putchar ('\n'); putchar('b'); Affichage : a b

Printf() Format Exemples : printf(format,arg1,arg2,…..,argn); les arguments sont des valeurs d'expression à imprimer le format donne le texte mort et le mode de décodage des arguments le format est une chaîne de caractères Exemples : printf("bon"); printf("jour");printf("\n") bonjour i=j=1; printf("i=%d\n",i) i=1 printf("%d%d%d\n",i,j,i+j); 112 printf("%d%d%d%d\n",i,j,i+j,sqrt(i)); 1121 x=3.0; printf("%f%d\n",x,i); 3.0000001 printf("%d\n%d\n",i,i+j); 1 2

Printf Caractères spéciaux de format %d : imprime les entiers sur le nombre de caractères nécessaires %f : imprime les réels avec 6 chiffres de partie décimale %e : imprime les réels en notation exponentielle %c : imprime un caractère %s : imprime une chaîne de caractères jusqu'à rencontrer le caractère de fin de chaine 0 (erreur si absent) ….. \n : saut à la ligne \t : tabulation \p : saut à la page ….

Printf : mises en forme Forçage du nombre de caractères entiers : %5d l'entier est imprimé sur 5 caractères au moins (blancs) avec cadrage à droite %-5d l'entier est imprimé sur 5 caractères au moins (blancs) avec cadrage à gauche 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 limitation de la partie décimale %20.3f le réel est imprimé sur 20 caractères (en tout) avec 3 chiffres en partie décimale

Printf : format variable ("%*d",n,i) n donne le nombre de caractères pour i ("%*.3f,n,x) n donne le nombre total de caractères pour x ("%*.*f,n,m,x) n donne le nombre de total caractères m donne le nombre de caractères pour la partie décimale Exemples

Printf La valeur de retour du printf est le nombre de caractères écrits et une valeur négative si il y a eu un pb. Exemple : int a,x; a=32; x = printf ("%d\n",a); printf ("%d\n",x); -> 3

Autres fonctions d'E/S Beaucoup d'autre fonctions d'E/S voir "stdio.h" gets, puts permettent de lire et d'écrire des chaînes de caractères exemple : #include "stdioh" main() { char ligne[80]; gets(ligne); puts(ligne); }

Lecture/Ecriture dans fichiers Principe : identique aux lecture/écriture sur clavier/écran En fait, le clavier et l'écran sont des fichiers particuliers Il faut simplement en plus "ouvrir" le fichier c-à-d l'associer à un fichier physique (sur disque) l'associer à un variable interne du pgm Un fichier peut être soit lu (read) soit (ré-)écrit (write) bande magnétique soit écrit à la fin (append) Déclaration : FILE * variable-interne ex : FILE * f; // f est une variable spéciale de type "fichier"

Ouverture de fichier : fopen variable = fopen("nom du fichier sur disque",mode d'ouverture) mode d'ouverture : syntaxe : chaîne de caractères 1er caractère : 'r' = read = lecture 'w' = write = écriture 'a' = append = écriture à la fin fopen renvoie la valeur NULL (=0) si le pb sur le fichier physique Exemple FILE * f; f = fopen ("c:\texte.txt","r1234"); if (f==NULL) printf ("le fichier est absent\n"); else printf ("ok\n"); variable interne fichier physique

Fermeture du fichier : fclose Supprime l'association fichier physique-variable interne La variable interne peut être associée à un autre fichier physique Syntaxe fclose (variable interne) Exemple : FILE * f; f= fopen (fichier1, "r"); …. fclose (f); … f= fopen (fichier2,"w");

Lecture dans fichier Lecture : fgetc() et getc() ↔ getchar() exemple c=fgetc(f); fscanf() ↔ scanf() fscanf(f,"%d",i) le 1er argument est le variable interne fichier le caractère EOF indique la fin de fichier Ecriture fputc () ↔ putchar() fprintf() ↔ printf()

Exercice Afficher à l'écran le contenu d'un fichier (idem commande type) main() { FILE * monfichier; char sur_disque[100]; char c; /* acquisition du nom */ scanf("%s",sur_disque); /*ouverture*/ monfichier=fopen(sur_disque,"r"); if (monfichier==NULL) printf("erreur\n"); else { // lecture affichage while ((c=getc(monfichier))!=EOF) printf("%c",c); fclose (monfichier); }

Lecture/ecriture dans chaines de caractères sprintf (char * s, format, paramètres) = écriture dans la chaine s sscanf (char * s, format, paramètres) = lecture dans la chaine s exemple char tokenstring[] = "15 12 14... " ; char s[81]; char c; int i; float fp; /*lecture de différentes valeurs: */ sscanf( tokenstring, "%s", s ); sscanf( tokenstring, "%c", &c ); sscanf( tokenstring, "%d", &i ); sscanf( tokenstring, "%f", &fp ); /* Sortie*/ printf( "String = %s\n", s ); printf( "Character = %c\n", c ); printf( "Integer: = %d\n", i ); printf( "Real: = %f\n", fp ); } Sortie : String = 15 Character = 1 Integer: = 15 Real: = 15.000000

Lecture/écriture dans chaines de caractères Exercice : 1/ reporter dans une chaine de caractères un tableau d’entiers. Dans la chaine, les nombres sont séparés par une virgule. 2/ 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 s1 e2 ex1 : if(e1) if(e2) s1; else s2; else if (e3) s3; else s4; s2 e1 s3 e3 s4 s1 ex2 : if(e1) s1; else if (e2) s2; e1 s2 e2 s1 ex3 : if(e1) s1; else if (e2) s2; else s3; e1 s2 e2 s3 s1 s1 s1 s1 e2 e2 ex4 : if(e1) if(e2) s1; else s2; s2 e1 e1 s2

Compléments sur les instructions de contrôle Règle : le else se rapporte au if le + imbriqué if(e1) {if(e2) s1;} else s2; s1 s1 e2 e1 if(e1) if(e2) s1; else ; else s2; s2

Compléments sur les instructions de contrôle : continue Dans une structure itérative : l'instruction continue permet d'arrêter l'itération courante sans sortie de la boucle Exemple: Calculer la moyenne des valeurs positives d'un tableau d'entiers relatifs. nb_valeurs=0; somme = 0; for (i=0;i<dim;i++) { if (T[i]<0) continue; somme = somme + T[i]; nb_valeurs ++; } moyenne = somme / nb_valeurs

Instruction break L'instruction break fait sortir de la structure de contrôle dans laquelle elle est imbriquée Utilisation dans les boucles : permet de faire une boucle avec une condition de type "et" logique ex: while (c1 && c2) {traitement;} while (c1) if (!c2) break; else {traitement;} Application typique : gestion d'exception

break On veut afficher tous les éléments d'un tableau d'entiers jusqu'à rencontrer un nombre <0 (si il y en a un) for (i=0;t[i]>=0 && i<dim;i++) ou bien i=0; printf("%d", t[i]); while(t[i]>=0 && i<dim) {printf("%d", t[i]);i++}; Pb : lorsque i=dim, il y a évaluation de t[dim] qui n'existe pas => erreur Solution 1 : avec un "drapeau » positif=1; // positif indique que l’on a eu que des valeurs positives jusqu’à maintenant for (i=0; (positif==1) && (i<dim) ; i++) { if(t[i]<0) positif=0; else printf("%d", t[i]); } Solution 2 : for (i=0; i<dim;i++) { if(t[i]<0) break;

Instruction switch syntaxe : switch (expression) instructions où expression a une valeur entière ou caractère L'instruction est une expression composée d'alternatives. Chaque alternative commence par une énumération de cas switch (expression) { case valeur 1 : instruction1; instruction 2; ….; case valeur 2 : instruction1; instruction 2; ….; … case valeur n : instruction1; instruction 2; ….; default : instruction1; instruction 2; ….; // optionnel };

switch Rouge Exemple : Vert char c; printf("donner un e couleur\n"); c=getchar();if (c>='a' && c<='z') c=c+'A'-'a' switch (c) { case 'R' : printf("Rouge \n"); case 'V' : printf("Vert\n"); case 'B' : printf("Bleu\n"); default : printf ("Autre"); } Vert Bleu Autre R Vert Bleu Autre V Bleu Autre B Autre

switch + break Exemple : char c; Rouge printf("donner un e couleur\n"); c=getchar();if (c>='a' && c<='z') c=c+'A'-'a' switch (c) { case 'R' : printf("Rouge \n");break; case 'V' : printf("Vert\n");break; case 'B' : printf("Bleu\n");break; default : printf ("Autre"); } R Rouge V Vert B Bleu Autre

switch + break Exemple : char c; Rouge printf("donner un e couleur\n"); c=getchar(); switch (c) { case 'r','R' : printf("rouge \n");break; case 'v', 'V' : printf("Vert\n");break; case 'b','B' : printf("Bleu\n");break; default : printf ("Autre"); } Rouge r ou R Vert v ou V Bleu b ou B Autre

Les tableaux Rappel : tableau =regroupement de données de même type sous un même nom, accessibles par un indice (0,..,dim-1) Déclaration et implantation mémoire : int t[50]; => réservation dans la mémoire de 50 cases contiguës d'entiers. L'adresse de la première case est t &t[0]  t *t  t[0] t[0] t t[1] t[2] t[48] t[49]

Tableaux Dimension par défaut: Initialisation à la compilation int t[10] = {1,2,3,4,5,6,7,8,9,10}; float x[4] = {0.,0.25,3.14,2.57}; char couleur[4]= {'r','v','b','j'}; char texte[10]="abcd"; int t1[10] = {1,2,3}; Dimension par défaut: int t[ ]={0,0,0} => dimension =3 char t [ ]={'r','v','b','j'}; => dimension=4 char t[ ]="abcd" => dimension=5 par contre int t[ ] sans initialisation est incorrect 1 2 3 4 5 6 7 8 9 10 0. 0.25 3.14 2.57 r v b j a b c d \0 ? ? ? ? ? 1 2 3 ?

Tableaux Accès aux éléments d'un tableau int t[50]; syntaxe 1 // accès à la (i+1)ème case avec i compris entre 0 et 49 t[i]; syntaxe 2 puisque t est l'adresse de la iere case t[0]  *t // mot d'adresse t, * : opérateur mot dont l'adresse est) t[1]  *(t+1) // rem : priorité des opérateurs) … t[i]  *(t+i) // *t+i  t[0]+i

Tableaux à plusieurs dimensions Tableau dont chaque case est elle-même un tableau ex : typedef int t[100] // t est un type t matrice [20]; matrice est un tableau de 20 cases, chacune est un tableau de 100 entiers => matrice est un tableau de 20*100 entiers autre déclaration : int matrice [20][100]; // tableau de 20 "lignes" et 100 "colonnes" Accès aux éléments par un 1er indice allant de 0 à 19 et par un 2eme indice allant de 0 à 99 matrice[3] est la 4eme case de tableau. C'est un tableau de 100 cases (entiers) matrice[3][48] est un entier. matrice [i][j] avec i de 0 à 19 et j de 0 à 99 matrice est un tableau à 2 dimensions

Tableaux à plusieurs dimensions Pas de limitations sur le nombre de dimensions Ex à 3 dimensions : tableau de coord. de pts de l'espace typedef float point[3] ; // x: indice 0, y : indice 1, z : indice 2 point tab[100][100]; // tab = matrice de 100 points ou bien tab[100][100][3]; tab[2][5][1] représente le "y" du point rangé en ligne 2 et colonne 5 Implantation mémoire int t[3][2]; t[0][0] t[0] t[0][1] t[1][0] t[1] t[1][1] t[2][0] t[2] t[2][1]

Tableaux à plusieurs dimensions Initialisation (à la compilation) int t[3][2] = {1,2,3,4,5,6}; ou bien (+ clair) int t[3][2] = {{1,2},{3,4},{5,6}}; int t[3][2] = {{1,2},4,{5,6}}; => t[1][1] non initialisé Initialisation grâce à des boucles for (i=0;i<3;i++) for (j=0;j<2;j++) t[i][j]=0; t[0][0] = 1 t[0] t[0][1] = 2 t[1][0] = 3 t[1] t[1][1] = 4 t[2][0] = 5 t[2] t[2][1] = 6

Tableaux à plusieurs dimensions Accés aux éléments int t[dim1][dim2] ; t[i][j]  * (t+i*dim2+j) int t[dim1][dim2][dim3]; t[i][j][k]  * (t+i*dim2*dim3+j*dim3+k) int t[dim1][dim2]….[dimn] ; t[i1][i2]….[in]  * (t +i1*dim2*dim3*dim4….. *dimn +i2* dim3*dim4….. *dimn +….. +in-1 *dimn +in) ) => la première dimension n'est pas utilisée dans le calcul

Les fonctions Une fonction permet de : Remplacer une partie qui se répète Découper un programme en parties isolées -> débogage, lisibilité, etc.. Exemples : fonctions d'E/S (scanf, printf, …), mathématiques (sin, cos, …) Organisation d'un programme : type fonction1 (arguments) { Déclarations de variables et de types locaux à la fonction Instructions } type fonction2 (arguments) { ... void main (arguments) {

Type de la valeur de retour Exemple Type de la valeur de retour Argument char minus_majus (char c1) { char c2; /* déclarations locales */ if (c1 >= 'a' && c1 <= 'z') c2 = c1+'A'-'a'; else c2=c1; return (c2); } void main() { char c,majuscule; printf("Donner un caractere\n"); c = getchar(); getchar(); majuscule = minus_majus(c); printf ("La majuscule de %c est %c\n",c,majuscule); Instructions Valeur renvoyée Appel de la fonction

Définition de fonction : syntaxe type_fonction nom_fonction (type_arg1 arg1, …, type_argn argn) { … return (valeur retournée); } Dans l'exemple précédent : type_fonction : char, c'est le type de la valeur renvoyée par return nom_fonction : minus_majus Le nombre d'arguments est quelconque, éventuellement aucun, les parenthèses doivent toujours figurer (ex: main () )

Type de la fonction Une fonction peut ne pas renvoyer de valeur. Exemple void print_majus (char c1) { char c2; if (c1 >= 'a' && c1 <= 'z') c2 = c1+'A'-'a'; else c2=c1; printf("la majuscule de % est %c, c1, c2); return; /* ou bien return (); */ } Dans ce cas, le type de la fonction est : void Le type de la fonction ne peut être que : int, float, char, ou adresse de ni tableau, ni autre type complexe

Instruction return 1/ Indique la valeur de retour de la fonction. 2/ Arrête l'exécution de la fonction char minus_majus (char c1) { if (c1 >= 'a' && c1 <= 'z') return (c1+'A'-'a'); else return (c1); } Pour les fonction de type void, return est optionnel void print_majus (char c1) { char c2; c2 = c1+'A'-'a'; else c2=c1; printf("la majuscule de % est %c, c1, c2);

Appel des fonctions L'appel d'une fonction se fait en donnant son nom, suivi de la liste des paramètres entre parenthèses. L'ordre des paramètres correspond à celui des arguments. Exemple float puiss (float x, int n) { float y=1.0; if (n>0) for (i=1;i<=n;i++) y = y*x; else for (i=1;i<=n;i++) y = y/x; return (y); } void main () { float z,t; z = puiss(10.7,2); t = puiss (z, -6); ...

Appel des fonctions Un appel de fonction peut se faire comme opérande d'une expression, soit comme paramètre d'un autre appel de fonction. Exemple int maximum (int x, int y) { return((x>y)?x,y)); } void main () { int v1,v2,v3,m1; scanf("%d %d %d , &v1,&v2,&v3); m1 = maximum(v1,v2); m1 = maximum(m1,v3); printf("valeur maximale %d\n", m1); ou bien m1 =maximum(v1,v2); printf("valeur maximale %d\n", maximum(m1,v3)); 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 (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 Information suffisante pour le compilateur. float puiss (float,int); void main(){ puiss (10.2, 5); ...} float puiss (float x, int n){ float y=1.0; if (n>0) for (i=1;i<=n;i++) y = y*x; else for (i=1;i<=n;i++) y = y/x; return (y); } /*Prototype de la fonction puiss*/ /*Appel avant déclaration*/ /*Déclaration de la fonction */

Fichier "header" Conseil de programmation : Dans un fichier ".h" déclarer les prototypes de toutes les fonctions, par exemple malib.h Dans le ou les fichiers ".c", insérer la directive #include "malib.h"

Passage des paramètres Rappel : les paramètres sont associés aux arguments suivant l'ordre de déclaration. En c, cette association se fait par COPIE de la valeur du paramètre dans l'argument. Chaque argument est en fait une variable locale de la fonction. La fonction travaille sur l'argument. Conséquence : Une fonction ne modifie pas les paramètres d'appels void f (int a){ a=a+1; } void main(){ int b; b=0; f(b); printf("%d\n",b); ->0

Détail void f (int a){ a=a+1; /*3*/ } void main(){ int b; b=0; /*1*/ f(b); /*2*/ printf("%d\n",b); /*4*/ Inchangé b b b b Copie a a 1 /*1*/ /*2*/ /*3*/ /*4*/

Modification des paramètres Si l'on veut qu'une fonction modifie un paramètre, on ne passe pas la variable mais l'adresse de la variable. Il y a copie de l'adresse de la variable. Dans la fonction on va chercher la variable par son adresse. Rappels : opérateur & : & variable -> adresse de la variable opérateur * : * adresse -> valeur qui se trouve à cette adresse int i; int * adresse_i; /* déclaration d'une adresse d'entier */ i=0; adresse_i=&i; printf("%d\n",i); -> 0; ...

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*/ f(&b); /*t2 &b est l'adresse de b */ printf("%d\n",b); /*t4*/ -> 1 b 728 b &b 728 a Copie b 1 *a b 1 &b 728 a 728 /*t1*/ /*t2*/ /*t3*/ /*t4*/ Exemple : scanf ("%d",&v);

Passage d'un tableau à une dimension en paramètre Rappels: Lorsqu'on déclare un tableau, par ex int t[10], t est l'adresse du 1er élément du tableau Chaque élément du tableau peut être accédé par t[i] ou *(t+i) La première dimension d'un tableau n'est pas utilisée dans le calcul de l'adresse d'une case Exemple void printtab (int t1[50]) { int i; for (i=0;i<50;i++) printf("%d",t1[i]); } void main () { int t[50]; ..... printtab(t);

Passage d'un tableau à une dimension en paramètre Puisque la dimension n'est pas utilisée, on peut ne pas la donner void printtab (int t1[50]){ int i; for (i=0;i<50;i++) printf("%d",t1[i]); } ou bien void printtab (int t1[]) { Syntaxes équivalentes

Passage d'un tableau à une dimension en paramètre Conséquence : on peut donc appeler cette fonction avec tout tableau d'entiers quelle que soit sa dimension. C’est au programmeur à gérer les débordements de tableau =>donner le nombre de cases sur lequel travaille la fonction void printtab (int t1[], int n){ int i; for (i=0;i<n;i++) printf("%d",t1[i]); } void main () { int t[50],t2[100]; ... printtab(t,50); /*affiche 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 Puisqu'en fait t1 est une adresse, on peut le déclarer comme tel void printtab (int * t1,int n) { int i; for (i=0;i<n;i++) printf("%d",t1[i]); }

Passage d'un tableau à une dimension en paramètre Conséquence : Si un argument est de type tableau (càd une adresse), la fonction peut modifier les cases du tableau void misea0 (int t1[], int n){ int i; for (i=0;i<n;i++) t1[i]=0; } void main () { int t[10]={1,2,3,4,5,6,7,8,9,10}; printtab(t,10); -> 1 2 3 4 5 6 7 8 9 10 misea0(t,10); printtab(t,10); -> 0 0 0 0 0 0 0 0 0 0

Fonctions de manipulations de chaînes de caractères Rappel : une chaîne de caractères est un tableau de caractères. Le caractère \0 marque la fin de la chaine. #include "string.h" .... char S1[]="abcd"; char S2[20]; S2 = S1 ; incorrect strcpy(S2,S1); void strcpy( char * dest, char * source){ int i; i=0; while(s[i]!=\0) { dest[i]=source[i] i++; }

Fonctions de manipulations de chaînes de caractères Toutes les fonctions de manipulation de chaines de caractères suivent ce gabarit. strlen(char * s) : donne la longueur de s, le 0 final non compris strcat (char * s1, char * s2): ajoute s2 à la fin de s1 strcmp(char * s1, char * s2): compare les chaines suivant l'ordre lexicographique. Valeur de retour : >0 si s1 > s2 =0 si s1 = s2 <0 si s1 < s2 "abc" < "abcd" "aa"< "ab" "aa" < "b" et bien d'autres fonctions (voir string.h)

Fonctions et tableaux à plusieurs dimensions Rappel: Pour de tableaux à plusieurs dimensions, la première dimension n'est pas utilisée pour le calcul d'adresse, mais la seconde , la troisième etc.. sont nécessaires int t[dim1][dim2]….[dimn] ; t[i1][i2]….[in]  * (t +i1*dim2*dim3*dim4….. *dimn +i2* dim3*dim4….. *dimn +….. +in-1 *dimn +in) Conséquence Lorsqu'un argument est un tableau à plusieurs dimension il faut donner explicitement sa deuxième, troisième etc... Exemple int rang_matrice (float t[][40]) { ..... }

Fonctions et tableau à plusieurs dimensions int rang_matrice (float t[][40]) { ..... } Cette fonction n'est utilisable qu'avec des tableaux dont la deuxième dimension est 40 int t1[30][40]; int t2[30][50]; /* on ne peut pas utiliser la fonction sur t2 */ Ennuyeux ! si l'on manipule des matrices avec une deuxième dimension différente. Comment contourner la difficulté ? Faire soit-même l'adressage des cases dans la fonction en gérant les dimensions

Fonctions et tableau à plusieurs dimensions Exemple : mise à zéro d'une matrice à n lignes et c colonnes void mat0 (float * t, int n , int c) { int i,j; /* rappel t[i][j] = *(t+i*dim2+j) */ for (i=0;i<n;i++) for (j=0;j<c;j++) *(t+i*c+j) = 0.0; } void main(){ float t1[20][40],t2[30][10]; mat0 (t1,20,40); mat0 (t2,30,10); mat0 (t1,20,10); /* correct ou incorrect ? */ mat0 (t1,10,40); /* correct ou incorrect ? */

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 mat0 (float * t, int n , int c) { int i,j; /* rappel t[i][j] = *(t+i*dim2+j) */ for (i=0;i<n;i++) for (j=0;j<c;j++) *(t+i*c+j) = 0.0; } void main(){ float t1[20][40],t2[30][10]; mat0 (t1,20,40); /* correct */ mat0 (t2,30,10); /* correct */ mat0 (t1,20,5); /* incorrect */ mat0 (t1,10,40); /* correct */

Fonctions et tableau à plusieurs dimensions Solution : passer la deuxième dimension en paramètre void mat0 (float * t, int n , int c, int dim2) { int i,j; /* rappel t[i][j] = *(t+i*dim2+j) */ for (i=0;i<n;i++) for (j=0;j<c;j++) *(t+i*dim2+j) = 0.0; } void main(){ float t1[20][40],t2[30][10]; mat0 (t1,20,40,40); // correct mieux: mat0 (&t1[0][0],…) mat0 (t2,30,10,10); // correct mat0 (t1,20,5,40); // correct mat0 (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. Règle 1 : variables globales Les variables déclarées avant la 1ere fonction peuvent être utilisées dans toutes les fonctions. Ces variables sont dites globales. int i; void f1 () { i = i+1; } void main(){ i=0; f1(); printf("%d\n",i) -> 1

Visibilité des variables Règle 2 : variables locales Les variables déclarées dans une fonction ne peuvent être utilisées que dans cette fonction. Ces variables sont dites locales. void f1 () { int i; i = i+1; } void main(){ i=0; -> ERREUR : i n'existe pas pour main ...

Visibilité des variables Règle 3 : arguments = variables locales Les arguments d'une fonction sont 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 int i; void f1 () { 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 locales et globales. .

Compléments : static Static : une telle variable maintient sa valeur à travers les appels de la fonction void inc( ) { int i=0; i++; printf ("%d", i); } void inc( ) { static int i=0; i++; printf ("%d", i); } 1, 1, 1, 1, … 1, 2, 3, 4, …

Compléments : register Une déclaration "registre" indique au compilateur qu'une variable sera utilisée fréquemment. Si c'est possible, le compilateur utilisera un registre pour implanter la variable plutot qu'un emplacement mémoire (vitesse d'exécution) register int i;

Structures Une structure permet de rassembler sous un même nom des informations de type différent. Une structure peut contenir des donnés entières, flottantes, tableaux , caractères, pointeurs, etc... Ces données sont appelés les membres de la structure. Exemple : fiche d'indentification d'un personne nom, prénom, âge, liste des diplômes, etc...

Définition d'une structure Déclaration d'une structure : syntaxe Exemple : compte bancaire struct nomdelastructure { typemembre1 nommembre1 ; typemembre2 nommembre2 ; … typemembren nommembren ; } struct compte { int no_compte ; char etat ; char nom[80]; float solde; }; struct compte a,b,c; /*déclaration de 3 variables de ce type*/

Déclarations de variables Autres façons de déclarer des variables structure struct compte { int no_compte ; char etat ; char nom[80]; float solde; } a, b; /*déclaration de 2 variables de ce type*/ struct compte c; /*déclaration de 1 variable de ce type*/ struct { /* le nom de la structure est facultatif */ int no_compte ; char etat ; char nom[80]; float solde; } a,b,c; /*déclaration de variables de ce type ici */ /* mais plus de possibilité de déclarer d'autres variables de ce type*/ Déconseillé

Déclarations de variables Autres façons de déclarer des variables structure Dans ce cas puisque on ne se sert plus de "struct compte" par la suite typedef struct compte { int no_compte ; char etat ; char nom[80]; float solde; } cpt ; /* cpt est alors un type équivalent à struct compte*/ cpt a,b,c; /*déclaration de variables de ce type*/ Recommandé typedef struct { int no_compte ; char etat ; char nom[80]; float solde; } cpt ;

Structures imbriquées Une structure peut être membre d'une autre structure Remarque : ordre de déclaration des structures struct date { int jour; int mois; int annee; }; struct compte { int no_compte ; char etat ; char nom[80]; float solde; struct date dernier_versement;

Structures Pas de confusion Tableaux de structures La portée du nom d'un membre est limité à la structure dans laquelle il est défini. On peut avoir des membres homonymes dans des structures distinctes. struct compte client[100]; struct s1 { float x; int y ; }; struct s2{ char x; float y; Pas de confusion

Manipulation des structures Initialisation à la compilation Accés aux membres : opérateur . Syntaxe : variable.membre struct compte { int no_compte ; char etat ; char nom[80]; float solde; struct date dernier_versement; }; struct compte c1 = {12345,'i',"Dupond",2000.45,01,11,2009}; 1/ c1.solde = 3834.56; 2/ struct compte c[100]; y=c[33].solde; 3/ c1.dernier_versement.jour = 15; c[12].dernier_versement.mois = 11;

Manipulation des structures Sur les structures elles-mêmes Affectation : Pas de comparaison => comparer chaque membre c[4] = c1

Structures et pointeurs L'adresse de début d'une structure s'obtient à l'aide de l'opérateur & c1 est de type cpt, pc est un pointeur sur une variable de type cpt Accés au membres à partir du pointeur Opérateur -> typedef struct { int no_compte ; char etat ; char nom[80]; float solde; struct date dernier_versement; } cpt ; cpt c1 , * pc; pc = &c1; *pc.no-compte = ... (*pc).no-compte = ... Incorrect . est plus prioritaire que * 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) float ajoute_au_compte(float solde1, float somme1) { solde1 = solde1+somme1; return (solde1); } void main () { ...... cpt c1; c1.solde = 0.; ajoute_au_compte(c1.solde,1000.0); printf("%f\n",c1.solde); -> 0.000000 c1.solde=ajoute_au_compte(c1.solde,1000.0); printf("%f\n",c1.solde); -> 1000.000000

Structures et fonctions Ex2 (avec modification) void ajoute_au_compte(float * solde1, float somme1) { *solde1 = *solde1+somme1; } void main () { ...... cpt c1; c1.solde = 0.; ajoute_au_compte(&(c1.solde),1000.0); /* ou &c1.solde */ printf("%f\n",c1.solde); -> 1000.000000

Structures et fonctions Un argument de fonction peut-être de type structure Ou pointeur sur 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 ajoute_au_compte (cpt * c, float somme1) { c->solde = c->solde + somme1; } void main () { cpt c1; ajoute_au_compte(&c1 ,1000.0); printf("%f\n",c1.solde); -> 1000.000000

Structures et fonctions La valeur de retour d'une fonction peut être une structure cpt ajoute_au_compte(cpt c, float somme1) { cpt c2; c2=c; c2.solde=c.solde+somme1; return(c2); } void main () { ...... cpt c1; c1.solde = 0.; c1=ajoute_au_compte(c1,1000.0); /* ou &c1.solde */ printf("%f\n",c1.solde); -> 1000.000000

Récursion Définitions : Une notion est dite récursive quand elle fait référence à elle-même soit directement soit indirectement.   Récursion directe : A -> A -> A Récursion indirecte : A-> B ->.... -> A Exemples : arbre : racine et des branches vers des sous-arbres n! = n*(n-1)! Un problème peut être représenté par un algorithme récursif quand il peut être décomposé en un ou plusieurs sous-problèmes de même type mais de taille inférieure.

Récursion Méthode générale : 1) Le paramétrage consiste à mettre en évidence les éléments dont dépend la solution, en particulier la taille du problème. 2) La recherche et la résolution d'au moins un cas trivial : consiste à résoudre le problème directement, c-à-d sans appel récursif, dans un cas particulier. 3) La décomposition du cas général consiste à passer d'un problème de taille N à un ou des problèmes de taille < N. Exemple 1 : calcul de factorielle. 1) paramétrage : n 2) cas triviaux : 1! = 0! = 1 3) décomposition : n! = n* (n-1)!  

Récursion int facto (int n) { int p; if (n==0) return (1); else { p = n * facto(n-1); /* appel récursif à la fonction facto */ return (p); }   Lors du calcul de facto(n) il y a n appels à la fonction facto. La pile contient n "assiettes" correspondant à cette fonction.

Récursion Exemple 2 : Suite récurrente : Suite de Fibonacci (récursion double); Un = Un-1 + Un-2 U1 = U0 =1 1) paramétrage : n 2) cas triviaux : U1 = U0 =1; 3) décomposition : Un = Un-1 + Un-2   Implantation en langage C int fibo (int n) { if ((n==0) || (n==1)) return (1); else return ( fibo(n-1) + fibo (n-2) ); /* appel récursif à la fonction fibo */ } Lors du calcul de facto(n) il y a 2n appels à la fonction facto. La pile contient n "assiettes" correspondant à cette fonction.

Récursion Exemple 3 : Combinaisons Cnp (méthode du triangle de Pascal); 1) paramétrage : n et p 2) cas triviaux : Cn0 = 1 Cnn = 1 3) décomposition : Cnp = Cn-1p-1 + Cn-1p   Implantation en langage C int combinaisons (int n, int p) { if ((n==p) || (p==0)) return (1); else return(combinaisons(n-1,p-1)+combinaisons(n-1,p)) /* appel récursif à la fonction combinaisons */ }

Récursion Exemple 4 : Tri par partition d'un tableau T sur l'intervalle [a , b] 1) paramétrage : a et b 2) cas triviaux : a >= b-1 , rien à faire 3) décomposition : Trier T sur [a,b] : - Faire la partition de T sur [a, b] et soit adpivot l'adresse du pivot après partition - Trier T sur [a , adpivot - 1] - Trier T sur [adpivot + 1, b] void tri (tab T , int a , int b){ int adpivot; if (b > a+1) { /* sinon ne rien faire */ partition (T,a,b,&adpivot); tri(T,a,adpivot-1); tri(T,adpivot+1,b); }

Récursion Exemple 5 : Recherche du zéro d'une fonction continue sur [a , b] Pb : Trouver un zéro d'une fonction continue sur [a , b] avec une précision  > 0 donnée si il en existe sinon détecter qu'il n'y a pas de zéros. On cherche x et y / x < y <= x +  et tels que f(x).f(y) < 0. Principe de dichotomie : - si a et b sont tels que f(a). f(b) <0 alors il existe un zéro dans [a,b]. - sinon, on divise l'intervalle en 2 moitiés et on recherche dans le premier intervalle. Si l'on n'a pas trouvé de zéro on cherche dans le second.   1) paramétrage : a , b et  2) cas triviaux : a < b <= a +  et f(a). f(b) <0 alors il existe un zéro dans [a,b] et le zéro égale a a < b <= a +  et f(a). f(b) > 0 alors il n'existe pas de zéro dans [a,b] 3) décomposition : principe de dichotomie

Récursion int zero (float a, float b, float epsilon, float * x) { / * retourne 0 si il n'existe pas de zero dans l'intervalle a , b; 1 sinon */ int trouve; if ( b-a <= epsilon) if (f(a) * f(b) < 0 ) { *x = a; return (1); }; else return (0); else { trouve = zero (a,(a+b)/2,epsilon,x); if (trouve) return(1); else return(zero ((a+b)/2,b,epsilon,x)); }

Récursion Exemple 6 : Les tours de Hanoi : Soient 3 socles A, B et C. Sur le socle A sont posées n disques de taille décroissante. Le problème consiste à transférer tous les disques du socle A au socle B en respectant les contrainte suivantes : - on ne déplace qu'un disque à la fois - on ne peut déplacer que les disques se trouvant en haut de chaque socle - on ne peut déplacer un disque que si on le pose sur un disque plus grand ou sur un socle vide. Exemple avec 5 disques :

Récursion 1) paramétrage : N : nombre de disques, socle de départ, socle relais, socle final 2) cas triviaux : N = 0 ne rien faire ; N= 1, et socle final vide : déplacer de départ à final 3) décomposition : Transférer N disques de A vers B en passant par C : - Transférer N-1 disques de A vers C en passant par B - Déplacer 1 disque de A vers B - Transférer N-1 disques de C vers B en passant par A void hanoi (int n, char depart, char final, char relais) { if (N>0) { hanoi(n-1, depart, relais, final); printf("deplacement de %c à %c\n", depart, relais); hanoi(n-1, relais, final, depart) ;} ; }

Récursion remarques : - la seule véritable action est faite par le printf - pour n disques il y a 2n appels à la fonction hanoi

appels successifs Actions hanoi(3,'A','B','C') hanoi(2,'A','C','B') hanoi(1,'A','B','C') hanoi(0,'A','C','B') rien A vers B A vers B (1) hanoi(0,'C','B','A') rien A vers C A vers C (2) hanoi(1,'B','C','A') hanoi(0,'B,'A,'C') rien B vers C B vers C (3) A vers B A vers B (4) hanoi(2,'C','B','A') hanoi(1,'C','A','B') C vers A C vers A (5) hanoi(0,'B','A','C') rien C vers B C vers B (6) hanoi(0,'A,'C,'B') rien A vers B A vers B (7)

1 2 3 4 5 6 7

Adresses et pointeurs Adresse = Pointeur Rappels : opérateur & : & variable -> adresse de la variable opérateur * : * adresse -> valeur qui se trouve à cette adresse int i; int * adresse_i; /* déclaration d'une adresse d'entier */ i=0; adresse_i=&i; printf("%d\n",i); -> 0; printf("%d\n",*adresse_i); -> 0;

Adresses et pointeurs Déclaration de pointeurs Syntaxe Interprétations type_simple * variable /* type_simple : char, float, int */ ex : int * pi; /* pi est un pointeur d'entier */ float * px; /* px est un pointeur de réel */ char * pc ; /* pc est un pointeur de caractère */ Interprétations int * pi ; Le mot d'adresse pi est un entier => pi est un pointeur pi donne une adresse

Adresses et pointeurs Types de pointeurs type_simple * variable type_simple : int, float, char, pointeur int i; int * pi; int * * ppi ;/* adresse d'adresse d'entiers */ pi= &i; ppi = π Pas de pointeurs de tableau (le nom du tableau est déjà un pointeur) int t[10]; int * * ppi ; ppi = &t; i pi ppi ... 11509 728 23712 38124

Pointeurs et opérations Sur variable pointée : toute opération valide sur le type float x,y,* px; ... px= &x; y = sinus (*px); Sur pointeurs Remarque : La valeur d'un pointeur n'a pas d'intérêt en elle-même, d'autant qu'elle change à chaque exécution. 1/ affectation int * pi, * pj; float * px; ... pi= pj; px = (float *) pi; /*conversion de type de pointeur */

Pointeurs et opérations 2/ comparaison d'égalité, d'inégalité int * pi, * pj; ... if (pi==pj) ......; 3/ comparaison >, < , .... mais aucun intérêt.

Etats d'un pointeur, NULL Un pointeur doit donner l'adresse d'une zone mémoire allouée int * pi, * pj; int i1,i2; i1=0; pi=&i1; i2 = *pi; /* correct */ i2 = *pj -> erreur PB : comment différencier une adresse valide d'une adresse invalide ? NULL est une valeur spéciale indiquant qu'un pointeur ne pointe vers rien int * pi, i; pi = NULL; if ( ....) pi = &i; if (pi != NULL) printf ("%d",*pi);

Etats d'un pointeur, NULL 2.1 adresse valide 2.2 adresse invalide PB : comment différencier une adresse valide d'une adresse invalide ? ne jamais avoir de pointeur dans l'état 2.2 => initialiser tous les pointeurs à NULL

Allocation dynamique de mémoire Jusqu’à maintenant, on a vu que tous les objets (variables) ainsi que leur taille devaient être déclarés au moment de la compilation, càd explicitement dans le code C. En particulier, la dimension des tableaux doit être connue au moment de l’écriture du pgm et ne peut être modifiée au moment de l’exécution. Les fonctions de gestion "dynamique" de mémoire permettent de remédier à cette limitation. 2 fonctions de base : malloc() : allocation d'une zone de mémoire free() : libération d'une zone mémoire précedemment alloué grâce à malloc

Calcul du nombre d'octets La fonction malloc la fonction malloc permet de réserver n octets contigus (un tableau) dans la mémoire. La valeur de retour est l'adresse du premier octet réservé. Exemple : int * t; //t est un pointeur d'entier int n; printf("combien d entiers voulez-vous réserver ? \n"); scanf("%d",&n); t = (int*) malloc (n*sizeof(int)); Calcul du nombre d'octets conversion de type

La fonction malloc Exemple : int * t; //t est un pointeur d'entier int n; …. t = (int*) malloc (n*sizeof(int)); .. // accès aux éléments *t = … // 1ere case ou bien t[0] *(t+1)=…//2eme case ou bien t[1] *(t+2)=…//3eme caseou bien t[2] … *(t+i)=…//ieme case ou bien t[i] En fait, on a "réservé" un tableau de n cases

La fonction malloc Prototype void * malloc (int n) ; 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 /*t1*/ /*t2*/ /*t3*/

La fonction free Remarque : Après free, pt ne vaut pas NULL et il indique pourtant une adresse inaccessible. Faire toujours suivre un free par une mise à NULL du pointeur free (pt); /*t3*/ pt=NULL; 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*/ pt = (int*) malloc(sizeof(int)); /*t2*/ pt = (int*) malloc(sizeof(int)); /*t3*/ Libre Occupé Occupé inaccessible /*t1*/ pt /*t2*/ pt /*t3*/ pt

Tableaux à plusieurs dimensions variables Rappels t[dim1][dim2] ~ dim1 tableaux de dim2 int t[3][2] : tableau de 3 cases, chaque case est un tableau de 2 entiers la première dimension n'est pas utilisée dans le calcul d'adresse t[i][j] <-> *(t+i*dim2+j) t[0][0] t[0] t[0][1] t[1][0] t[1] t[1][1] t[2][0] t[2] t[2][1]

Première dimension "variable" Première dimension inconnue à la compilation ~ t[n] [2] Même principe que pour les tableaux à une dimension typedef int t2[2]; // t2 est un type t2 * t; // t est un pointeur sur un objet de type t2 scanf("%d",&n); t = (t2 *) malloc(n*sizeof(t2)); L'accès aux éléments par la notation t[i][j] est utilisable puisque : t[i][j] <-> *(t+i*2+j) // 2 est la taille d'un objet de type t2 t[0][0] t[0] t[0][1] t[1][0] t[1] t[1][1] t[2][0] t[2] t[2][1] t[n-1][0] t[n-1] t[n-1][1]

Deuxième dimension "variable" Deuxième dimension inconnue ~ t[4] [n] Principe : on utilise en fait un tableau de pointeurs int * t[4]; // t est tableau de 4 pointeurs Création du tableau : scanf("%d",&n); for (i=0;i<4;i++) t[i] = (int *) malloc(n*sizeof(int)); t[0] t[1] t[2] t[3]

Deuxième dimension "variable" Accès aux éléménts : t[i][j] est traduit en *(t[i]+j) : donc notation utilisable Intérêt : tableau de chaines de caractères De plus pour les chaines de caractères, l'initialisation est possible : char * semaine[7]= {"lundi","mardi,..,"dimanche"};

Tableaux à plusieurs dimensions variables Deux dimensions inconnues ~ t[n] [m] ~ matrice La deuxième dimension est inconnue à la compilation mais sera constante int ** t; // t est un pointeur sur un tableau de pointeurs ex: scanf("%d %d",&n,&m); // allocation des lignes t = (int * *) malloc(n*sizeof(int*)); // allocations des colonnes for (i<0;i<m;i++) t[i]=(int *) malloc (m*sizeof(int));

Tableaux à plusieurs dimensions variables … …. t[n-2] t[n-1] Accès aux éléménts : t[i][j] est traduit en *(*(t+i)+j) : donc notation utilisable

Tableaux à plusieurs dimensions Deux dimensions inconnues, la deuxième peut varier Exemple : stocker un nombre inconnu de chaines de caractères char ** dic; int cpt = 0; char tmp[100]; //tableau pour la lecture int nb = 10;// nombre de chaines prévues par défaut dic = (char **) malloc(nb * sizeof(char *)); for(i=0;i<nb;i++) dic[i]=NULL; printf("donner un nom"\n); scanf("%s",tmp); while (strlen(temp)!=0) { dic[cpt] = (char *) malloc(strlen(tmp)*sizeof(char)); strcpy(dic[cpt],tmp); nb++; if (cpt> nb-1) { // on augmente la première dimension nb=nb+10; dic = (char **) realloc(dic,nb*sizeof(char*)); for(i=nb-1;i>nb-11;i--) dic[i]=NULL; } Exercice : trier les noms par ordre alphabétique

Structures et allocation dynamique de mémoire : listes, files, piles, etc.. Pas plus de variables que de pointeurs déclarés Lorsque on veut créer dynamiquement des variables, on : déclare un pointeur sur structure dans la structure on définit un membre qui est lui-même un pointeur (à l'aide duquel on pourra créer dynamiquement une autre structure qui elle-même contient un pointeur, etc …)

Structures et allocation dynamique de mémoire : listes, files, piles, etc.. Exemple struct ent { int valeur; struct ent * suivant; }; struct ent * p1; p1=NULL; p1 = (struct ent *) malloc(sizeof(struct ent)); p1->valeur=1; p1->suivant= (struct ent *) malloc(sizeof(struct ent)); p1->suivant->valeur=2; p1->suivant->suivant= (struct ent *) malloc(sizeof(struct ent)); …..

Structures et allocation dynamique de mémoire : listes, files, piles, etc.. p1=NULL; /*1*/ p1 = (struct ent *) malloc(sizeof(struct ent)); /*2*/ p1->valeur=1; /*3*/ p1->suivant= (struct ent *) malloc(sizeof(struct ent)); /*4*/ p1->suivant->valeur=2; /*5*/ p1->suivant->suivant= (struct ent *) malloc(sizeof(struct ent)); /*6*/ ….. valeur suivant p1 p1 p1 1 p1 1 2 p1 1 p1 1 2

Structures et allocation dynamique de mémoire : listes, files, piles, etc.. 1 2 Les valeurs sont accessibles par : p1->valeur p1->suivant->valeur p1->suivant->suivant->valeur Pb1 : on doit connaitre le nombre de (->suivant) au moment de l'écriture du pgm !! Pb2 : quand doit on s'arrêter ? rep : le dernier membre suivant doit être NULL.

Structures et allocation dynamique de mémoire : listes, files, piles, etc.. struct ent { int valeur; struct ent * suivant; }; struct ent * p1; p1 = (struct ent *) malloc(sizeof(struct ent)); Syntaxe équivalente plus commode typedef struct ent element, *pt ; //element et pt sont des types pt suivant; pt p1; p1 = (pt) malloc(sizeof(element);

Listes Création d'une liste contenant les n premiers entiers pt deb =NULL; pt paux; for(i=1;i<=n;i++) { paux= (pt) malloc (sizeof(element)); paux->valeur = i; paux->suiv=deb; deb=paux; } Parcours des éléments de la liste paux=deb; // on prend un pointeur auxiliaire pour be pas perdre l'adresse de début de la liste while(paux!=NULL) { printf("%d",paux->valeur); paux=paux->suiva; Attention: ne jamais perdre l'adresse du début de la liste

Listes : exemples de fonction Fonction ajoutant une donnée en début de liste void ajoutdeb (pt * d; int data) { // doit être appelée avec le pointeur de début de la liste ajout(&deb,56) pt paux; paux =(pt) malloc(sizeof(element)); paux->valeur=data; paux->suiv=*d; *d=paux; }

Listes : exemples de fonction Fonction ajoutant une donnée en fin de liste void ajoutfin (pt * d; int data) {// appelée avec le pointeur de début de la liste pt paux,paux2; paux=(pt) malloc(sizeof(element)); paux->valeur=data; `//attention priorité des opérateurs paux->suiv=NULL; if(*d==NULL) *d=paux; else { paux2=*d; while(paux2->suiv!=NULL) paux2=paux2->suiv; paux2->suiv=paux; }

Listes : exemples de fonction Fonction retournant l'adresse d'une donnée présente dans la liste pt adresse1(pt d; int data) { // doit être appelée avec le pointeur de début de la liste while(d->valeur!=data) d=d->suiv; return(d); } Fonction retournant l'adresse d'une donnée dont on ne sait si elle est présente ou pas dans la liste (dans ce cas, la fonction renvoie NULL) pt adresse2(pt d; int data) { while(d!=NULL) if(d->valeur==data) return(d); 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; }

Listes : exemples de fonction Fonction supprimant une donnée dans la liste pt detruit (pt * d, int data) { // doit être appelée avec le pointeur de début de la liste pt p,prec; prec=NULL p=*d; while (p!=NULL) { if (p!->valeur!=data) { //on avance dans la liste 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); return; }} *d prec p data