Cours Langage C/C++ - Première partie Thierry Vaira BTS IRIS Avignon tvaira@free.fr v0.2
Historique (1/3) Introduction En 1970, Ken Thompson, créa un nouveau langage : Le B, descendant du BCPL (Basic Combined Programming Language, créé en 1967 par Martin Richards). Son but était de créer un langage simple, malheureusement, son langage fût trop simple et trop dépendant de l architecture utilisée. En 1971, Dennis Ritchie commence à mettre au point le successeur du B, le C. Le résultat est convaincant : Le C est totalement portable (il peut fonctionner sur tous les types de machines et de systèmes), il est de bas niveau (il peut créer du code aussi rapide que de l assembleur) et il permet de traiter des problèmes de haut niveau. Le C permet de quasiment tout faire, du driver au jeu. Le C devient très vite populaire. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 2 / 152
Historique (2/3) Introduction En 1989, l ANSI (American National Standards Institute) normalisa le C sous les dénominations ANSI C ou C89. Un programme écrit dans ce standard est compatible avec tous les compilateurs. En 1983, Bjarne Stroustrup des laboratoires Bell crée le C++. Il construit donc le C++ sur la base du C. Il garde une forte compatibilité avec le C. En 1999, l ISO (International Organization for Standardization) proposa une nouvelle version de la norme, qui reprenait quelques bonnes idées du langage C++. Il ajouta aussi le type long long d une taille minimale de 64 bits, les types complexes, l initialisation des structures avec des champs nommés, parmi les modifications les plus visibles. Le nouveau document est celui ayant autorité aujourd hui, est connu sous le sigle C99. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 3 / 152
Historique (3/3) Introduction Certaines autres extensions du C ont elles aussi été standardisées, voire normalisées. Ainsi, par exemple, des fonctions spécifiques aux systèmes UNIX, sur lesquels ce langage est toujours très populaire, et qui n ont pas été intégrées dans la norme du langage C, ont servi à définir une partie de la norme POSIX. Le langage C++ est normalisé par l ISO. Sa première normalisation date de 1998 (C++98), puis une seconde en 2003. Le standard actuel a été ratifié et publié par ISO en septembre 2011 (C++11). Mais certains compilateurs ne la supportent pas encore complétement. Les langages C et C++ sont les langages les plus utilisés dans le monde de la programmation. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 4 / 152
Introduction Apports du C++ par rapport au C Le C++ a apporté par rapport au langage C les notions suivantes : les concepts orientés objet (encapsulation, héritage), les références, la vérification stricte des types, les valeurs par défaut des paramètres de fonctions, la surcharge de fonctions (plusieurs fonctions portant le même nom se distinguent par le nombre et/ou le type de leurs paramètres), la surcharge des opérateurs (pour utiliser les opérateurs avec les objets), les constantes typées, la possiblité de déclaration de variables entre deux instructions d un même bloc tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 5 / 152
C vs C++ Introduction Ce cours traitera des aspects de la programmation C et C++ car : On peut passer progressivement de C à C++. Il suffit en premier lieu de changer de compilateur (par exemple gcc g++), sans nécessairement utiliser les nombreuses possibilités supplémentaires qu offre C++. Le principal intérêt du passage de C à C++ est de profiter pleinement de la puissance de la programmation orientée objets (POO). L option -std des compilateurs gcc/g++ permet de choisir la norme à appliquer au moment de la compilation. Par exemple : -std=c89 ou c99 pour le C et -std=c++98 ou c++0x pour le C++ (compilateur version 4.4.3) tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 6 / 152
Introduction Exemple 0a : premier programme en C #include <stdio.h> int main (int argc, char **argv) { int n; int i; printf("donnez un entier : "); scanf("%d", &n); for(i = 0; i < n; i++) printf("hello world!\n"); } return 0; L extension par défaut des fichiers écrits en langage C est.c tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 7 / 152
Introduction Exemple 0b : premier programme en C++ #include <iostream> int main (int argc, char **argv) { int n; std::cout << "Donnez un entier : " << std::endl; std::cin >> n; for(int i = 0; i < n; i++) std::cout << "Hello world!" << std::endl; } return 0; L extension par défaut des fichiers écrits en langage C++ est.cpp ou.cc tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 8 / 152
Introduction Par défaut, gcc et g++ fabriquent un exécutable de nom a.out (ou a.exe). Sinon, on peut lui indiquer le nom du fichier exécutable en utilisant l option -o. Exemples 0a et 0b : fabrication de l exécutable (sous Linux) $ gcc <fichier.c> -o <executable> $ g++ <fichier.cpp> -o <executable> L exécution de ces 2 programmes donne le même résultat : $./a.out Donnez un entier : 2 Hello world! Hello world! tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 9 / 152
Étapes de fabrication Introduction 1 Le préprocesseur (pré-compilation) Traitement de toutes les directives #xxxxx Inclusion de fichiers (.h) Substitutions lexicales : les "macros" 2 La compilation Vérification de la syntaxe Traduction dans le langage d assemblage de la machine cible 3 L assemblage Traduction finale en code machine Production d un fichier objet (.o ou.obj) 4 L édition de liens Unification des symboles internes Étude et vérification des symboles externes (bibliothèques.so ou.dll) Production du programme exécutable tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 10 / 152
Introduction Étapes de fabrication avec gcc (1/2) Pré-compilation $ gcc -E <fichier.c> -o <fichier_precompile.c> Compilation $ gcc -S <fichier_precompile.c> -o <fichier.s> // ou $ gcc -c <fichier.c> -o <fichier.o> Assemblage $ as <fichier.s> -o <fichier.o> tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 11 / 152
Introduction Étapes de fabrication avec gcc (1/2) Édition des liens $ gcc <fichier.o> -o <executable> // ou $ gcc <fichier1.o> <fichier2.o> <fichiern.o> -o <executable> // voir aussi : $ ld -dynamic-linker <fichier.o> -o <executable> Fabrication // Combiner en une seule ligne de commande toutes les étapes (préprocesseur, compilation, assemblage et édition des liens) $ gcc <fichier.c> -o <executable> Décomposition des étapes $ gcc -v -save-temps <fichier.c> -o <executable> tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 12 / 152
Définition Manipuler des variables Une variable est un espace de stockage pour un résultat. Une variable est un symbole (habituellement un nom qui sert d identifiant) qui renvoie à une position de mémoire (adresse) dont le contenu peut prendre successivement différentes valeurs pendant l exécution d un programme. Règle n 5 : Les données prévalent sur le code. Si vous avez conçu la structure des données appropriée et bien organisé le tout, les algorithmes viendront d eux-mêmes. La structure des données est le coeur de la programmation, et non pas les algorithmes. (Rob Pike) Cette règle n 5 est souvent résumée par «Écrivez du code stupide qui utilise des données futées.»... tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 13 / 152
Caractéristiques Manipuler des variables De manière générale, on distinguera les caractéristiques suivantes : son nom : c est-à-dire sous quel nom est déclaré la variable ; son type : c est la convention d interprétation de la séquence de bits qui constitue la variable. Le type de la variable spécifie aussi sa taille (la longueur de cette séquence) soit habituellement 8 bits, 32 bits, 64 bits,... ; sa valeur : c est la séquence de bit elle même. Cette séquence peut être codée de différrentes façons suivant son type. Certaines variables sont déclarées constantes (ou en lecture seule) et sont donc protégées contre l écriture. Le mot clé const permet de déclarer des variables constantes ; son adresse : c est l endroit dans la mémoire ou elle est stockée ; sa durée de vie : c est la portion de code dans laquelle la variable existe. Généralement, on parle de variable locale (à un bloc {}) ou globale (accessible de partout dans le code du programme) ; sa visibilité : c est un ensemble de règles qui fixe qui peut utiliser la variable. En C++, les variables (membres) private ne sont visibles qu à l intérieur de la classe par opposition aux variables (membres) public qui elles sont visibles aussi à l extérieur de la classe. En C, il est possible de "cacher" certaines variables ; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 14 / 152
Manipuler des variables Créer une variable Pour créer une variable en C/C++, il faut bien évidemment lui choisir son nom (parlant et précis) mais aussi obligatoirement lui choisir son type : pour les variables booléenes : bool (seulement en C++) pour les nombres entiers : char, short, int, long et long long pour les nombres à virgule flottante : float, double et long double Le type char permet aussi de stocker le code ASCII d une lettre (caractère). Il est recommandé d initialiser la variable (attention à ne pas l oublier!) avec une valeur du type correspondant. Remarque : pour les variables de type entier, il est possible de préciser à la déclaration si celle-ci aura un signe (signed) ou non (unsigned). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 15 / 152
Manipuler des variables Exemple 1a : des variables de différents types en C #include <stdio.h> /* fichier en-tête pour accéder à la déclaration de la fonction printf */ int main() /* la fonction principale appelée automatiquement au lancement de l exécutable */ { int nombredoeufs = 3; // 3 est une valeur entière unsigned long int jesuisunlong = 12345678UL; // U pour unsigned et L pour long float quantitedefarine = 350.0; // ".0" rend la valeur réelle char lettre = g ; // ne pas oublier les quotes : printf("la variable nombredoeufs a pour valeur %d\n", nombredoeufs); printf("la variable nombredoeufs occupe %d octet(s)\n", sizeof(int)); // l opérateur sizeof retourne la taille en octets de la variable printf("la variable jesuisunlong a pour valeur %lu\n", jesuisunlong); printf("la variable jesuisunlong occupe %d octet(s)\n", sizeof(unsigned long int)); printf("la variable quantitedefarine a pour valeur %f\n", quantitedefarine); printf("la variable quantitedefarine occupe %d octet(s)\n", sizeof(float)); printf("la variable lettre a pour valeur %c (%d ou 0x%02X)\n", lettre, lettre, lettre); printf("la variable lettre occupe %d octet(s)\n", sizeof(char)); printf("recette : il faut %d oeufs et %.1f %c de farine\n", nombredoeufs, quantitedefarine, lettre); } return 0; /* fin normale du programme */ tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 16 / 152
Manipuler des variables Exemple 1a : fabrication de l exécutable (sous Linux) $ gcc <fichier.c> Attention : Le binaire, le décimal et l hexadécimal ne sont qu une représentation numérique d une même valeur (cf. la variable lettre). Exemple 1a : exécution $./a.out La variable nombredoeufs a pour valeur 3 La variable nombredoeufs occupe 4 octet(s) La variable jesuisunlong a pour valeur 12345678 La variable jesuisunlong occupe 4 octet(s) La variable quantitedefarine a pour valeur 350.000000 La variable quantitedefarine occupe 4 octet(s) La variable lettre a pour valeur g (103 ou 0x67) La variable lettre occupe 1 octet(s) Recette : il faut 3 oeufs et 350.0 g de farine tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 17 / 152
Manipuler des variables Exemple 1b : des variables de différents types en C++ #include <iostream> /* pour cout */ using namespace std; int main() /* la fonction principale appelée automatiquement au lancement de l exécutable */ { bool reussie = true; // true est une valeur booléenne int nombredoeufs = 3; // 3 est une valeur entière unsigned long int jesuisunlong = 12345678UL; // U pour unsigned et L pour long float quantitedefarine = 350.0f; // ".0" rend la valeur réelle et f pour float char unite = g ; // ne pas oublier les quotes : cout << "La variable nombredoeufs a pour valeur " << nombredoeufs << endl; cout << "La variable jesuisunlong a pour valeur " << jesuisunlong << endl; cout << "La variable quantitedefarine a pour valeur " << quantitedefarine << endl; cout << "La variable unite a pour valeur " << unite << endl; cout << "Recette " << reussie << " : il faut " << nombredoeufs << " oeufs et " << quantitedefarine << " " << unite << " de farine" << endl; } return 0; /* fin normale du programme */ tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 18 / 152
Manipuler des variables Exemple 1b : fabrication de l exécutable (sous Linux) $ g++ <fichier.cpp> Exemple 1b : exécution La variable nombredoeufs a pour valeur 3 La variable jesuisunlong a pour valeur 12345678 La variable quantitedefarine a pour valeur 350 La variable unite a pour valeur g Recette 1 : il faut 3 oeufs et 350 g de farine tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 19 / 152
Manipuler des variables Remarque : std représente un espace de nom. cin et cout existent dans cet espace de nom mais pourraient exister dans d autres espaces de noms. Le nom complet pour y accéder est donc std::cin et std::cout. L opérateur :: permet la résolution de portée en C++ (un peu comme le / dans un chemin!). Pour éviter de donner systématiquement le nom complet, on peut écrire le code ci-dessous. Comme on utilise quasiment tout le temps des fonctions de la bibliothèque standard, on utilise presque tout le temps " using namespace std ; " pour se simplifier la vie! Exemple 1c : utilisation de l espace de nom std #include <iostream> using namespace std; // si C++ ne connaît pas un symbole (cout, endl ici), il le cherchera dans std int main() { cout << "Hello world!" << endl; return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 20 / 152
Manipuler des variables Affecter une variable tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 21 / 152
Les types entiers Manipuler des variables bool : false ou true booléen (seulement en C++) unsigned char : 0 à 255 (2 8 1) entier très court (1 octet ou 8 bits) [signed] char : -128 ( 2 7 ) à 127 (2 7 1) idem mais en entier relatif unsigned short [int] : 0 à 65535 (2 16 1) entier court (2 octets ou 16 bits) [signed] short [int] : -32768 ( 2 15 ) à +32767 (2 15 1) idem mais en entier relatif unsigned int : 0 à 4.295e9 (2 32 1) entier sur 4 octets ; taille "normale" actuelle [signed] int : -2.147e9 ( 2 31 ) à +2.147e9 (2 31 1) idem mais en entier relatif unsigned long [int] : 0 à 4.295e9 entier sur 4 octets ou plus ; sur PC identique à "int" (hélas...) [signed] long [int] : -2.147e9 à -2.147e9 idem mais en entier relatif unsigned long long [int] : 0 à 18.4e18 (2 64 1) entier (très gros!) sur 8 octets sur PC [signed] long long [int] : -9.2e18 ( 2 63 ) à -9.2e18 (2 63 1) idem mais en entier relatif tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 22 / 152
Manipuler des variables Les types à virgule flottante float : Environ 6 chiffres de précision et un exposant qui va jusqu à ±10 ±38 Codage IEEE754 sur 4 octets double : Environ 10 chiffres de précision et un exposant qui va jusqu à ±10 ±308 Codage IEEE754 sur 8 octets long double Codé sur 10 octets tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 23 / 152
Manipuler des variables Le standard IEEE 754 Le standard IEEE 754 définit les formats de représentation des nombres à virgule flottante de type float/double (signe, mantisse, exposant, nombres dénormalisés) et valeurs spéciales (infinis et NaN), un ensemble d opérations sur les nombres flottants et quatre modes d arrondi et cinq exceptions (cf. fr.wikipedia.org/wiki/ieee_754). La version 1985 de la norme IEEE 754 définit 4 formats pour représenter des nombres à virgule flottante : simple précision (32 bits : 1 bit de signe, 8 bits d exposant (-126 à 127), 23 bits de mantisse, avec bit 1 implicite), simple précision étendue (>= 43 bits, obsolète, implémenté en pratique par la double précision), double précision (64 bits : 1 bit de signe, 11 bits d exposant (-1022 à 1023), 52 bits de mantisse, avec bit 1 implicite), double précision étendue (>= 79 bits, souvent implémenté avec 80 bits : 1 bit de signe, 15 bits d exposant (-16382 à 16383), 64 bits de mantisse, sans bit 1 implicite). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 24 / 152
Manipuler des variables Mantisse et partie significative Le compilateur gcc (pour les architectures compatible Intel 32 bits) utilise le format simple précision pour les variables de type float, double précision pour les variables de type double, et la double précision ou la double précision étendue (suivant le système d exploitation) pour les variables de type long double. La mantisse est la partie décimale de la partie significative, comprise entre 0 et 1. Donc 1+mantisse représente la partie significative. Elle se code (attention on est à droite de la virgule) : pour le premier bit avec le poids 2 1 soit 0,5, puis 2 2 donc 0,25, 2 3 donnera 0,125, ainsi de suite... Par exemple :,010 donnera,25. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 25 / 152
Manipuler des variables Les limites des nombres Le fichier limits.h contient, sous forme de constantes ou de macros, les limites concernant le codage des entiers et le fichier float.h contient celles pour les "floattants". Exemple : les limites des nombres en C #include <stdio.h> #include <limits.h> #include <float.h> int main() { printf("*** Limites pour les entiers ***\n"); printf("nombre de bits dans un char : %d bits\n", CHAR_BIT); printf("%d <= char <= %d\n", CHAR_MIN, CHAR_MAX); printf("0 <= unsigned char <= %u\n", UCHAR_MAX); printf("%d <= int <= %d\n", INT_MIN, INT_MAX); printf("0 <= unsigned int <= %u\n", UINT_MAX); printf("%ld <= long <= %ld\n", LONG_MIN, LONG_MAX); printf("0 <= unsigned long <= %lu\n", ULONG_MAX); printf("\n*** Limites pour les réels ***\n"); printf("%e <= float <= %e\n", FLT_MIN, FLT_MAX); printf("%e <= double <= %e\n", DBL_MIN, DBL_MAX); printf("%le <= long double <= %Le\n", LDBL_MIN, LDBL_MAX); return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 26 / 152
Manipuler des variables Exemple : exécution *** Limites pour les entiers *** Nombre de bits dans un char : 8 bits -128 <= char <= 127 0 <= unsigned char <= 255-2147483648 <= int <= 2147483647 0 <= unsigned int <= 4294967295-2147483648 <= long <= 2147483647 0 <= unsigned long <= 4294967295 *** Limites pour les réels *** 1.175494e-38 <= float <= 3.402823e+38 2.225074e-308 <= double <= 1.797693e+308 3.362103e-4932 <= long double <= 1.189731e+4932 Première loi : Le type d une variable spécifie sa taille (de sa représentation en mémoire) et ses limites. Attention, Elle peut donc déborder (overflow). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 27 / 152
Les constantes Manipuler des variables Celles définies pour le préprocesseur : c est simplement une substitution syntaxique pure (une sorte de copier/coller). Il n y a aucun typage de la constante. #define PI 3.1415 /* en C traditionnel */ L utilisation de #define améliore surtout la lisibilité du code source. La convention usuelle est d utiliser des MAJUSCULES (pour les distinguer des variables). Celles définies pour le compilateur : c est une valeur typée, ce qui permet des contrôles lors de la compilation. const double pi = 3.1415; // en C++ et en C ISO A utiliser pour des valeurs constantes. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 28 / 152
Les pointeurs Manipuler des variables Les pointeurs sont des variables spéciales permettant de stocker une adresse (pour la manipuler ensuite). L adresse représente généralement l emplacement mémoire d une variable (ou d une autre adresse). Comme la variable a un type, le pointeur qui stockera son adresse doit être du même type pour la manipuler convenablement. Le type void* représentera un type générique de pointeur : en fait cela permet d indiquer sagement que l on ne sait pas encore sur quel type il pointe. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 29 / 152
Manipuler des variables Utilisation des pointeurs On utilise l étoile (*) pour déclarer un pointeur. déclaration d un pointeur (*) sur un entier (int) : int *ptrint ; On utilise le & devant une variable pour initialiser ou affecter un pointeur avec une adresse. déclaration d un entier i qui a pour valeur 2 : int i = 2 ; affectation avec l adresse de la variable i (&i) : ptrint = &i ; On utilise l étoile devant le pointeur (*) pour accéder à l adresse stockée. indirection ("pointe sur le contenu de i") : *ptrint = 3 ; Maintenant la variable i contient 3 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 30 / 152
Manipuler des variables Particularités des pointeurs Avec printf, il est possible d afficher une adresse mémoire en utilisant le formateur %p. Pour connaître la taille qu occupe une variable de type pointeur, on utilisera sizeof(). Sur une architecture donnée, tous les pointeurs ont la même taille (probablement 32 bits). Sur 32 bits, un pointeur pourra contenir une adresse dans un espace de 4 GO (2 32 ). Les pointeurs peuvent être incrémentés, décrémentés, additionnés ou soustraits. Dans ce cas, leur nouvelle valeur dépend du type sur lequel ils pointent. Exemple : incrémenter un pointeur de char ajoute 1 à sa valeur (un caractère est représenté sur 1 octet). Incrémenter un pointeur sur un int ajoute 4 à sa valeur (cela dépend de l architecture, un entier pouvant être représenté sur 4 octets). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 31 / 152
Manipuler des variables Exemple : manipulons un pointeur #include <stdio.h> int main() { int i = 2; // déclaration d un entier i qui a pour valeur 2 int *ptrint; // déclaration d un pointeur (*) sur un entier (int) printf("la variable i a pour valeur %d et pour adresse %p\n", i, &i); ptrint = &i; // affectation du pointeur avec l adresse de la variable i printf("la variable ptrint contient l adresse %p et pointe donc sur i qui contient %d\n", ptrint, * ptrint); *ptrint = 3; // j accède à l adresse contenue dans le pointeur et je pointe dessus (*ptrint) et je modifie son contenu printf("la variable i a maintenant pour valeur %d\n", i); } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 32 / 152
Manipuler des variables Exemple 2 : exécution La variable i a pour valeur 2 et pour adresse 0xbfa56938 La variable ptrint contient l adresse 0xbfa56938 et pointe donc sur i qui contient 2 La variable i a maintenant pour valeur 3 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 33 / 152
Manipuler des variables Les références en C++ En C++, il est possible de déclarer une référence j sur une variable i : cela permet de créer un nouveau nom j qui devient synonyme de i (un alias). On pourra donc modifier le contenu de la variable en utilisant une référence. La déclaration d une référence se fait en précisant le type de l objet référencé, puis le symbole &, et le nom de la variable référence qu on crée. Une référence ne peut être initialisée qu une seule fois : à la déclaration. Une référence ne peut donc référencer qu une seule variable tout au long de sa durée de vie. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 34 / 152
Manipuler des variables Exemple : les références #include <iostream> int main (int argc, char **argv) { int i = 10; // i est un entier valant 10 int &j = i; // j est une référence sur un entier, cet entier est i. //int &k = 44; // ligne7 : illégal std::cout << "i = " << i << std::endl; std::cout << "j = " << j << std::endl; // A partir d ici j est synonyme de i, ainsi : j = j + 1; // est équivalent à i = i + 1! std::cout << "i = " << i << std::endl; std::cout << "j = " << j << std::endl; } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 35 / 152
Manipuler des variables Exemple : exécution i = 10 j = 10 i = 11 j = 11 Si on dé-commente la ligne 7, on obtient cette erreur à la compilation : ligne 7: erreur: invalid initialization of non-const référence of type int& from a temporary of type int tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 36 / 152
Manipuler des variables Intérêt des références Attention : le = dans la déclaration de la référence n est pas réellement une affectation puisqu on ne copie pas la valeur de i. En fait, on affirme plutôt le lien entre i et j. En conséquence, la ligne 7 est donc parfaitement illégal ce que signale le compilateur. Comme une référence établit un lien entre deux noms, leur utilisation est efficace dans le cas de variable de grosse taille car cela évitera toute copie. Les références sont (systématiquement) utilisées dans le passage des paramètres d une fonction (ou d une méthode) dès que le coût d une recopie par valeur est trop important ("gros" objet). Exemple : void truc(const grosobjet& rgo) ; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 37 / 152
Manipuler des variables Variables globale et locale une variable globale est une variable déclarée à l extérieur du corps de toute fonction ou classe, et pouvant donc être utilisée n importe où dans le programme. On parle également de variable de portée globale. une variable locale est une variable qui ne peut être utilisée que dans la fonction ou le bloc où elle est définie. On parle également de variable de portée locale. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 38 / 152
Durée de vie Manipuler des variables Exemple : Variable globale et locale #include <stdio.h> int g = 1; // je suis une variable globale int main() { int x = 5; // je suis une variable locale, ma durée de vie est celle du main printf("la variable globale g a pour valeur %d\n", g); printf("la variable locale x a pour valeur %d\n", x); } return 0; Exemple : exécution La variable globale g a pour valeur 1 La variable locale x a pour valeur 5 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 39 / 152
Visibilité Manipuler des variables Exemple : "Attention, une variable peut en cacher une autre" #include <stdio.h> int i = 1; // je suis une variable globale de nom i int main() { int i = 2; // je suis une variable locale de nom i! aie! printf("la variable i a pour valeur %d\n", i); } return 0; Conclusion : La visibilité s applique à la plus "proche" déclaration. Exemple : exécution La variable i a pour valeur 2 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 40 / 152
typdedef Manipuler des variables Le mot réservé typedef (signifie littéralement «définition de type») permet simplement la définition de synonyme de type qui peut ensuite être utilisé à la place d un nom de type : Exemple d utilisation de typedef typedef int typedef float entier; reel; entier a; // a de type entier donc de type int reel x; // x de type réel donc de type float Conclusion : Le mot-clé typedef permet donc au programmeur de créer de nouveaux noms de types. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 41 / 152
Manipuler des variables Intérêt de typdedef typedef peut être utilisé à la fois pour : donner plus de clarté au code source rendre plus facile les modifications de ses propres types de données permettre la portabilité du code source (sur une autre ou future plateforme) Notez que beaucoup de langages fournissent l alias de types ou la possibilité de déclarer plusieurs noms pour le même type. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 42 / 152
enum Manipuler des variables enum permet de déclarer un type énuméré constitué d un ensemble de constantes appelées énumérateurs. Une variable de type énuméré peut recevoir n importe quel énumérateur (lié à ce type énuméré) comme valeur. Le premier énumérateur vaut zéro (par défaut), tandis que tous les suivants correspondent à leur précédent incrémenté de un. Exemple d utilisation de enum enum couleur_carte { TREFLE = 1, /* un énumérateur */ CARREAU, /* 1+1 donc CARREAU = 2 */ COEUR = 4, /* en C, les énumérateurs sont équivalents à des entiers (int) */ PIQUE = 8 /* il est possible de choisir explicitement les valeurs (ou de certaines d entre elles). */ }; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 43 / 152
Manipuler des variables Exemple : utilisation des typedef et enum précédents typedef int entier; typedef float reel; typedef enum{false,true} booleen; void main() { entier e = 1, reel r = 2.5, booleen fini = FALSE; enum couleur_carte carte = CARREAU; printf("le nouveau type entier possède une taille de %d octets (ou %d bits)\n", sizeof(entier), sizeof( entier)*8); printf("la variable e a pour valeur %d et occupe %d octets\n", e, sizeof(e)); printf("la variable r a pour valeur %.1f et occupe %d octets\n", r, sizeof(r)); printf("la variable fini a pour valeur %d et occupe %d octets\n", fini, sizeof(fini)); printf("la variable carte a pour valeur %d et occupe %d octets\n", carte, sizeof(carte)); } Exemple : exécution Le nouveau type entier possède une taille de 4 octets (ou 32 bits) La variable e a pour valeur 1 et occupe 4 octets La variable r a pour valeur 2.5 et occupe 4 octets La variable fini a pour valeur 0 et occupe 4 octets La variable carte a pour valeur 2 et occupe 4 octets tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 44 / 152
Les instructions conditionnelles et itératives Instruction conditionnelle (1/2) Il en existe deux formes possibles : if (expression) instruction ; if (expression) instruction_1 ; /* SI... ALORS... */ else instruction_2 ; /* SINON... */ expression doit être de type numérique ou de type pointeur. Cela donne le comportement suivant : si l expression est de type numérique et que sa valeur est différente de 0 (faux), alors l instruction_1 est exécutée, sinon c est l instruction_2. si l expression est de type pointeur, cela revient à comparer le pointeur avec la valeur null. Une valeur non null du pointeur provoque l exécution de l instruction_1 et une valeur null, celle de l instruction_2. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 45 / 152
Les instructions conditionnelles et itératives Instruction conditionnelle (2/2) tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 46 / 152
Les instructions conditionnelles et itératives Instruction de sélection (1/2) Une instruction switch sélectionne une des instructions la composant, en fonction de la valeur d une expression, qui doit être de type entier. Elle a la forme suivante : switch (expression) { case constante_scalaire_1 : liste_d_instructions_1; case constante_scalaire_2 : liste_d_instructions_2; // etc... default : liste_d_instructions; } Important : expression doit rendre un résultat de type entier. La valeur exprimée derrière un case est une constante et en aucun cas une instruction. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 47 / 152
Les instructions conditionnelles et itératives Instruction de sélection (2/2) Important : expression est comparée aux constantes derrière les case. A la première correspondance des deux valeurs, la liste d instructions correspondante est exécutée. Si aucune constante n est égale à expression : s il existe une partie default, elle est exécutée sinon, l instruction switch ne fait rien. Attention : Une fois terminée l exécution d une liste d instructions d un case, on continue en séquence dans le case suivant. Si on ne veut pas que ce genre de phénomène se produise, il faut utiliser l instruction break (qui fait sortir du switch). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 48 / 152
Les instructions conditionnelles et itératives Instruction de contrôle d itération : while L instruction while contrôle l exécution répétitive d une autre instruction TANT QUE expression est vraie. Elle a la forme suivante : expression doit être soit de type entier, soit de type pointeur. Au début de chaque itération, expression est évaluée. Si celle-ci a une valeur différente de zéro (ou de null), l itération se poursuit, sinon la boucle est terminée. Cette boucle sera effectuée zéro ou plusieurs fois. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 49 / 152
Les instructions conditionnelles et itératives Instruction de contrôle d itération : do Comme précédemment, expression doit être soit de type entier, soit de type pointeur. Cette suite d instructions sera exécutée au moins une fois. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 50 / 152 La suite d instructions se trouvant entre les symboles do et while est exécutée de manière répétitive, TANT QUE expression est vraie (donc produise une valeur différente de zéro (ou de null). Elle a la forme suivante :
Les instructions conditionnelles et itératives Instruction de contrôle d itération : for (1/2) L instruction for est une forme singulière et pratique de l instruction while. Cette instruction n a été rajoutée dans le langage que pour des raisons de clarté de programme. Elle a la forme suivante : for (expression_1; expression_2; expression_3) instruction; //ou : for (expression_1; expression_2; expression_3) { instruction_1; instruction_2; // etc... } expression_1 : correspond à la partie initialisation de l itération expression_2 : correspond à la condition d arrêt (de type TANT QUE) expression_3 : correspond au pas de l incrémentation ou à une réinitialisation instruction : correspond au corps de l itération tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 51 / 152
Les instructions conditionnelles et itératives Instruction de contrôle d itération : for (2/2) L instruction for est ainsi équivalente à l instruction while suivante : expression_1; while (expression_2) { instruction; expression_3; } Remarque : N importe laquelle des 3 expressions peut donc être omise. Si expression_2 est omise, on a alors affaire à une instruction while(1), c est à dire une boucle infinie. Exemple : for ( ; ;) instruction ; /* je suis une boucle infinie! */ C est dans les parties initialisation et incrémentation que l opérateur virgule, est le plus utile. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 52 / 152
Les instructions conditionnelles et itératives Instructions de rupture de séquence (1/2) Il en existe plusieurs : Instruction break : Cette instruction fournit un mécanisme pour arrêter une boucle prématurément. Elle permet aussi de sortir d un case de l instruction switch, ce qui est nécessaire pour éviter d exécuter en séquence le reste des instructions composant le switch. Elle cause ainsi l arrêt de la première instruction while, do, for ou switch englobante. Instruction continue : Cette instruction est très proche de l instruction break mais ne s applique pas à l instruction switch. Elle ne s applique en effet qu aux instructions permettant le contrôle d itération. Elle provoque l arrêt de l itération courante et le passage au début de l itération suivante dans une boucle contrôlée par une instruction while, do ou for. Dans une boucle for, la partie réinitialisation de la boucle (expression_3) est exécutée. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 53 / 152
Les instructions conditionnelles et itératives Instructions de rupture de séquence (2/2) Il existe aussi : Instruction return : Cette instruction provoque le retour chez l appelant de la fonction courante. Les deux formes de l instruction return sont : return ; ou return expression ; Instruction goto (et étiquettes) : La forme d une instruction goto est goto etiquette ;. Elle a pour effet de provoquer le passage à l exécution de l instruction étiquetée par etiquette. Elle a pour limitation de n autoriser le branchement qu à l intérieur du bloc régi par une fonction. Bien que cette instruction soit bannie de la programmation structurée, elle est parfois utilisée car elle permet un traitement plus facile de certaines situations. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 54 / 152
Les opérateurs Les opérateurs arithmétiques et logiques Les opérateurs arithmétiques (+, -, *, / et %) et les opérateurs relationnels (<, <=, >, >=, == et!=) ne sont définis que pour des opérandes d un même type parmi : int, long int (et leurs variantes non signées), float, double et long double. Mais on peut constituer des expressions mixtes (opérandes de types différents) ou contenant des opérandes d autres types (bool, char et short), grâce aux conversions implicites et explicites. Les opérateurs logiques && (et), (ou) et! (non) acceptent n importe quel opérande numérique (entier ou flottant) ou pointeur, en considérant que tout opérande de valeur non nulle correspond à "faux". Les deux opérateurs && et sont "à court-circuit" : le second opérande n est évalué que si la connaissance de sa valeur est indispensable. Remarque : l opérateur modulo (%) permet d obtenir le reste d une division. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 55 / 152
Les opérateurs Opérateurs logiques à court-circuit Attention : le second opérande n est évalué que si la connaissance de sa valeur est indispensable. int a = 0; // Danger : si le premier opérande suffit à déterminer l évaluation du résultat logique if( 0 < 1 a++!= 5 ) // Attention : a++!= 5 n est pas évalué donc (a n est pas incrémenté) car 0 < 1 et donc toujous VRAI dans un OU printf("vrai!\n"); // Affiche toujours : VRAI! else printf("faux!\n"); if( 1 < 0 && a++!= 5 ) // Attention : a++!= 5 n est pas évalué donc (a n est pas incrémenté) car 1 < 0 et et donc toujous FAUX dans un ET printf("vrai!\n"); else printf("faux!\n"); // Affiche toujours : FAUX! printf("a = %d\n", a); // Affichera toujours : a = 0!!! tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 56 / 152
Les opérateurs Opérateurs logique et bit à bit (1/2) Ne pas confondre les opérateurs logiques avec les opérateurs bit à bit unsigned char a = 1; unsigned char b = 0; unsigned char aa = 20; /* non nul donc VRAI en logique */ unsigned char bb = 0xAA; // Ne pas confondre! /*! : inverseur logique */ /* ~ : inverseur bit à bit */ printf("a = %u -!a = %u - ~a = %u (0x%hhX)\n", a,!a, ~a, ~a); printf("b = %u -!b = %u - ~b = %u (0x%hhX)\n", b,!b, ~b, ~b); printf("aa = %u (0x%hhX) -!aa = %u - ~aa = %u (0x%hhX)\n", aa, aa,! aa, ~aa, ~aa); printf("bb = %u (0x%hhX) -!bb = %u - ~bb = %u (0x%hhX)\n", bb, bb,! bb, ~bb, ~bb); tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 57 / 152
Les opérateurs Opérateurs logique et bit à bit (2/2) Pour les opérateurs bit à bit, il est conseillé d utiliser la représentation en hexadécimale : Exemple a = 1 -!a = 0 - ~a = 4294967294 (0xFE) b = 0 -!b = 1 - ~b = 4294967295 (0xFF) aa = 20 (0x14) -!aa = 0 - ~aa = 4294967275 (0xEB) bb = 170 (0xAA) -!bb = 0 - ~bb = 4294967125 (0x55) tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 58 / 152
Les opérateurs Les opérateurs d affectation Les opérateurs d affectation (=, +=, -=, *=, /=, %=, &=, ^=, =, <<= et >>=) nécessitent une lvalue pour l opérande de gauche. L affectation à la déclaration d une variable est appelée "déclaration avec initialisation", par exemple : int a = 5 ; déclare a et l initialise avec la valeur entière 5. Affectation avec opérateur : Il existe toute une gamme d opérateurs permettant d effectuer une opération avec le contenu d une variable et de mettre le résultat dans cette même variable. Une opération du type : a operateur= expression ; équivaut à : a = a operateur (expression) ; Par exemple : L opérateur "+=" signifie "affecter en additionnant à" : a += 2 ; est équivalent a = a + 2 ; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 59 / 152
Les opérateurs Opération sur les pointeurs Les opérations arithmétiques sur les pointeurs sont bien évidemment réalisées sur les adresses contenues dans les variables pointeurs. Le type du pointeur a une influence importante sur l opération. Supposons un tableau t de 10 entiers (int) initialisés avec des valeurs croissantes de 0 à 9. Si on crée un pointeur ptr sur un entier (int) et qu on l initialise avec l adresse d une case de ce tableau, on pourra alors se déplacer avec ce pointeur sur les cases de ce tableau. Comme ptr pointe sur des entiers (c est son type), son adresse s ajustera d un décalage du nombre d octets représentant la taille d un entier (int). Par exemple, une incrémentation de l adresse du pointeur correspondra à une opération +4 (octets) si la taille d un int est de 4 octets! tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 60 / 152
Les opérateurs Opération de déplacement d un pointeur int t[10] = { 0,1,2,3,4,5,6,7,8,9 }; int *ptr; // un pointeur pour manipuler des int ptr = &t[5]; // l adresse d une case du tableau printf("je pointe sur la case : %d (%p)\n", *ptr, ptr); // Je pointe sur la case : 5 (0xbf8d87b8) ptr++; // l adresse est incrémentée de 4 octets pour pointer sur l int suivant printf("maintenant, je pointe sur la case : %d (%p)\n", *ptr, ptr); // Maintenant, je pointe sur la case : 6 (0xbf8d87bc) ptr -= 4; // en fait je recule de 4 int soit 4*4 octets pour la valeur de l adresse printf("maintenant, je pointe sur la case : %d (%p)\n", *ptr, ptr); // Maintenant, je pointe sur la case : 2 (0xbf8d87ac) tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 61 / 152
Les opérateurs Opérateurs d incrémentation et de décrémentation Les opérateurs unaires (à une seule opérande) d incrémentation (++) et de décrémentation ( ) agissent sur la valeur de leur unique opérande (qui doit être une lvalue) et fournissent la valeur après modification lorsqu ils sont placés à gauche (comme dans ++n) ou avant modification lorsqu ils sont placés à droite (comme dans n ). i++ ; est (à première vue) équivalent à i = i + 1 ; Mais i++ est une right-value donc... int i = 10; int j = i++; // équivalent à int j=i; i=i+1; printf("i = %d\n", i); // Affiche : i = 11 printf("j = %d\n", j); // Affiche : j = 10 Attention c est une post-incrémentation : on augmente i après avoir affecté j. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 62 / 152
Opérateur ternaire :? Les opérateurs L opérateur ternaire? ressemble au if(...) {...} else {...} mais joue un rôle de right-value et pas de simple instruction. La syntaxe est la suivante : (A?B :C) prend la valeur de l expression B si l expression A est vraie, sinon la valeur de l expression C. int age = 1; int parite; /* un booléen */ printf("j ai %d an%c\n", age, age > 1? s : ); // J ai 1 an printf("je suis %s\n", age >= 18? "majeur" : "mineur"); // Je suis mineur parite = (age%2 == 0? 1 : 0 ); printf("parité = %d\n\n", parite); // Parité = 0 age = 20; printf("j ai %d an%c\n", age, (age > 1)? s : ); // J ai 20 ans printf("je suis %s\n", (age >= 18)? "majeur" : "mineur"); // Je suis majeur parite = (age%2 == 0? 1 : 0 ); printf("parité = %d\n", parite); // Parité = 1 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 63 / 152
Les opérateurs Priorité des opérateurs (1/2) Liste des opérateurs du plus prioritaire au moins prioritaire : :: (opérateur de résolution de portée en C++). -> [] (référence et sélection) () (appel de fonction) () (parenthèses) sizeof() ++ ~ (inverseur bit à bit)! (inverseur logique) - (unaire) & (prise d adresse) * (indirection) new delete delete[] (opérateurs de gestion mémoire en C++) () (conversion de type) * / % (multiplication, division, modulo) + - (addition et soustraction) << >> (décalages et envoi sur flots) < <= > >= (comparaisons) ==!= (comparaisons) & (ET bit à bit) ^ (OU-Exclusif bit à bit) (OU-Inclusif bit à bit) && (ET logique) (OU logique) (? : ) (expression conditionnelle ou opérateur ternaire) = *= /= %= += = <<= >>= &= = ~=, (mise en séquence d expressions) tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 64 / 152
Les opérateurs Priorité des opérateurs (2/2) Quelques exemples : 1 y = (x+5) est équivalent à : y = x+5 car l opérateur + est prioritaire sur l opérateur d affectation =. 2 (i++) * (n+p) est équivalent à : i++ * (n+p) car l opérateur ++ est prioritaire sur *. En revanche, * est prioritaire sur +, de sorte qu on ne peut éliminer les dernières parenthèses. 3 moyenne = 5 + 10 + 15 / 3 donnera 20 (/ est plus prioritaire que le +) alors que mathématiquement le résultat est 10! Il faut alors imposer l ordre en l indiquant avec des parenthèses : moyenne = (5 + 10 + 15) / 3 4 Important : Si deux opérateurs possèdent la même priorité, C exécutera les opérations de la gauche vers la droite (sauf pour les opérateurs suivants où l ordre est de la droite vers la gauche : ++ ~ (inverseur bit à bit)! (inverseur logique) - (unaire) & (prise d adresse) * (indirection) new delete delete[] (? :) et = *= /= %= += = <<= >>= &= = ~=). 5 L ordre des opérateurs n est donc pas innocent. En effet : 3/6*6 donnera 0 alors que 3*6/6 donnera 3! Conclusion : comme il est difficile de se rappeler de l ensemble des priorités des opérateurs, le programmeur préfère coder explicitement en utilisant des parenthèses afin de s éviter des surprises. Cela améliore aussi la lisibilité. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 65 / 152
lvalue lvalue et rvalue Une Left-value (valeur gauche) est un élément de syntaxe C/C++ pouvant être écrit à gauche d un opérateur d affectation (=). Exemple : une variable, une case de tableau,... Modèle d une affectation : lvalue = rvalue ; soit left-value right-value Une left-value doit donc être un emplacement de stockage en mémoire possédant un type précis, c est-à-dire la référence à quelque chose de modifiable. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 66 / 152
lvalue et rvalue Affectation d une lvalue #include <stdio.h> int main() { int x; x = 5; // affectation de la valeur entière 5 à la variable x (x est une lvalue) 2 = x + 1; // problème car 2 n est pas une lvalue (2 n est pas " modifiable")! } return 0; Le compilateur détecte l erreur et le message est très clair : "error : lvalue required as left operand of assignment" tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 67 / 152
rvalue lvalue et rvalue Une Right-value (valeur droite) est un élément de syntaxe C/C++ pouvant être écrit à droite d un opérateur d affectation (=). Exemple : une valeur, une constante, une variable, une expression,... Modèle d une affectation : lvalue = rvalue ; soit left-value right-value Une right-value doit être une valeur d un type précis mais n a pas forcement de zone de stockage en mémoire. Une affectation est encore une right-value... ce qui donne le droit d écrire : i = j = 10 ; C est équivalent à i=(j=10) ; c est-à-dire : j=10 ; i=j ; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 68 / 152
lvalue et rvalue Utilisation d une affectation comme rvalue #include <stdio.h> int main() { int i = 0; int j = 0; i = j = 10; printf("i = %d\n", i); // Pas de surprise : i = 10 printf("j = %d\n", j); // Pas de surprise : j = 10 } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 69 / 152
La conversion de types Conversion "forcée" par la lvalue Les opérateurs d affectation (=, -=, +=...), appliqués à des valeurs de type numérique, provoquent la conversion de leur opérande de droite dans le type de leur opérande de gauche. Cette conversion "forcée" peut être "dégradante" (avec perte). #include <stdio.h> int main() { int x = 5; float y = 1.5; int res; res = (x + y); // cela revient à faire (int)(x + y) car res est de type int printf("res = %d\n", res); // Affiche : res = 6 } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 70 / 152
La conversion de types Conversion automatique Soit l opération suivante : short int a = 2 ; a + 2 ; L opération a + 2 revient à faire l addition entre un short int (a) et un int (2). Cela est impossible car on ne peut réaliser que des opérations entre type identique. Une conversion implicite (automatique) sera faite (pouvant donner lieu à un warning de la part du compilateur). Les conversions d ajustement de type automatique réalisées suivant la hiérarchie ci-dessous sont réalisées sans perte : 1 char short int int long float double long double 2 unsigned int unsigned long float double long double tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 71 / 152
La conversion de types Conversion forcée ou cast Une conversion de type (ou de promotion de type) peut être implicite (automatique) ou explicite (c est-à-dire forcée par le programmeur). Lorsqu elle est explicite, on utilise l opérateur de cast : (float)a permet de forcer le short int a en float. Les conversions forcées peuvent être des conversions dégradantes (avec perte). Par exemple : int b = 2.5 ; En effet, le cast (int)b donnera 2 : perte de la partie décimale. Cela peut être dangereux (source d erreur). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 72 / 152
Présentation Types agrégés Dans de très nombreuses situations, les types de base s avèrent insuffisants pour permettre de traiter un problème : il peut être nécessaire, par exemple, de stocker un certain nombre de valeurs en mémoire afin que des traitements similaires leurs soient appliqués. Dans ce cas, il est impensable d avoir recours à de simples variables car tout traitement itératif est inapplicable. D autre part, il s avère intéressant de pouvoir regrouper ensemble plusieurs variables afin de les manipuler comme un tout. Pour répondre à tous ces besoins, le langage C comporte la notion de type agrégé en utilisant : les tableaux, les structures, les unions et les énumérations. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 73 / 152
Les tableaux Types agrégés Un tableau est un ensemble d éléments de même type désignés par un identificateur unique (un nom). Chaque élément est repéré par une valeur entière appelée indice (ou index) indiquant sa position dans l ensemble. Les tableaux sont toujours à bornes statiques et leur indiçage démarre toujours à partir de 0. La forme générique de déclaration d un tableau est la suivante : [classe de mémorisation] type identificateur[dimension 1 ]... [dimension n ] Contrairement à beaucoup d autres langages, il n existe pas en C de véritable notion de tableaux multidimentionnels. De tels tableaux se définissent par composition de tableaux, c est à dire que les éléments sont eux-mêmes des tableaux. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 74 / 152
Types agrégés Exemple : les tableaux #define MAX 20 // définit l étiquette MAX egale à 20 int t[10]; // tableau de 10 éléments entiers (int) // tableau à 2 dimensions de 2 lignes et 5 colonnes : int m[2][5] = { 2, 6, -4, 8, 11, // initialise avec des valeurs 3, -1, 0, 9, 2 }; int x[5][12][7]; // tableau a 3 dimensions, rarement au-delà de cette dimension float f[max]; // tableau de MAX éléments de type float t[2] = 6; // accès en écriture au 3eme élément du tableau t printf("%d", m[1][3]); // affiche la valeur 9 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 75 / 152
Types agrégés Particularités des tableaux L identificateur du tableau désigne non pas le tableau dans son ensemble, mais plus précisément l adresse en mémoire du début du tableau. Ceci implique qu il est impossible d affecter un tableau à un autre : int a[10], b[10] ; a = b ; // cette affectation est interdite L identificateur d un tableau sera donc "vu" comme un pointeur constant. Danger : le plus grand danger dans la manipulation des tableaux est d accéder en écriture en dehors du tableau. Cela provoque un accès mémoire interdit qui n est pas contrôlé au moment de la compilation. Par contre, lors de l exécution, cela provoquera une exception de violation mémoire (segmentation fault) qui se traduit généralement par une sortie prématurée du programme avec un message "Erreur de segmentation". tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 76 / 152
Types agrégés La dimension d un tableau peut être omise dans 2 cas : Exemple : les tableaux 1 le compilateur peut en définir la valeur int t[]={2, 7, 4}; // tableau de 3 éléments char msg[]="bonjour"; // chaîne de caractères 2 l emplacement mémoire correspondant a été réservé // la fonction fct admet en parametre void fct(int t_i[]) // un tableau d entiers qui existe déjà tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 77 / 152
Types agrégés Exemple : tableaux et pointeurs int t[5] = {0, 2, 3, 6, 8}; // un tableau de 5 entiers int *p1 = NULL; // le pointeur est initialisé à NULL (précaution obligatoire) int *p2; // pointeur non initialisé : il pointe donc sur n importe quoi (gros danger) p1 = t; // p1 pointe sur t c est-a-dire la première case du tableau // identique a : p1 = &t[0]; p2 = &t[1]; // p2 pointe sur le 2eme élément du tableau *p1 = 4; // la première case du tableau est modifiée printf("%d ou %d\n", *p1, t[0]); // affiche 4 ou 4 printf("%d ou %d\n", *p2, t[1]); // affiche 2 ou 2 p2 += 2; // p2 pointe sur le 4eme élément du tableau (indice 3) printf("%d ou %d\n", *p2, t[3]); // affiche 6 ou 6 // on peut utiliser les [] sur un pointeur : p1[1] = 8; // identique à : *(p1+1) = 8; ou a : t1[1] = 8; printf("%d\n", t[1]); // affiche 8 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 78 / 152
Types agrégés Les chaînes de caractères Une chaîne de caractères est un tableau de caractères dont le dernier caractère est le caractère nul (valeur 0) qui marque ainsi la fin de la chaîne. En C, un caractère est un code ASCII sur 8 bits (cf. man ascii). De nombreuses fonctions de la librairie standard (les fonctions commençant par str, comme strlen, strcpy, strcat,...) reçoivent en paramètre des chaînes de caractères (et doivent donc posséder le fin de chaîne final). Danger : omettre le fin de chaîne provoquera généralement une "Erreur de segmentation" car le traitement itératif des caractères se poursuivra en dehors du tableau. Il est donc recommandé de ne pas oublier de déclarer son tableau d une taille suffisante pour y stocker la chaîne de caractères agrandie d un caractère pour y placer le fin de chaîne. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 79 / 152
Types agrégés Exemple : les chaînes de caractères #include <stdio.h> #include <string.h> /* pour les fonctions str... */ int main() { char tab[4] = { a, b, c, d }; // un tableau de caractères (il n y a pas de fin de chaînes) char msg[] = "Bonjour"; // chaîne de caractères (le fin de chaîne est ajouté automatiquement ici) printf("msg : %s contient %d caractères\n", msg, strlen(msg)); printf("tab : %s contient %d caractères\n", tab, strlen(tab)); // risque! tab[3] = 0; // maintenant, on place le caractère nul de fin de chaîne printf("tab : %s contient %d caractères\n", tab, strlen(tab)); } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 80 / 152
Types agrégés Traité tab comme une chaîne engendrera un bug à l exécution : $./a.out msg : Bonjour contient 7 caractères tab : abcdp\? contient 8 caractères <---- ici! tab : abc contient 3 caractères tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 81 / 152
Types agrégés string en C++ (1/2) Attention : Ce n est pas un type de base (c est une classe) qui permet de stocker (et de manipuler) une suite de lettres (une chaîne de caractères). Utilisation de string en C++ : #include <stdio.h> #include <iostream> #include <string> using namespace std; int main (int argc, char **argv) { string le0 = "Chuck"; string le1 = "Norris"; cout << "Il n y a que deux éléments en informatique : le " << le0 << " et le " << le1 <<. << endl; if(le0!= le1) cout << "Remarque: Les deux chaînes sont différentes" << endl; printf("une chaîne en C : Si windows existe encore c est parce que %s %s ne s est jamais intéressé à l informatique.\n", le0.c_str(), le1.c_str()); return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 82 / 152
Types agrégés string en C++ (2/2) La définition de variable de type string est très simple : string s1; // s1 contient 0 caractère string s2 = "New York"; // s2 contient 8 caractères string s3(60, * ); // s3 contient 60 étoiles (*) string s4 = s3; // copie de s3 dans la nouvelle s4 (operator=) string s5(s3); // idem mais avec le constructeur de copie string s6(s2, 4, 2); // s6 contient "Yo" Lecture et écriture très simple également : string s; cin >> s; // La taille de s s adapte toute seule à la saisie. cout << s; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 83 / 152
Types agrégés Compromis entre string et iostream Le compromis entre un string et un iostream (istream ou ostream) est stringstream (istringstream ou ostringstream). Cela permet : de lire/écrire dedans avec >> et << et d obtenir un string comme résultat (en utilisant la méthode str()). #include <iostream> #include <sstream> // stringstream! #include <string> using namespace std; int main() { string s("pi"); int n=2; float pi=3.14; ostringstream oss; oss << n << s << = <<2*pi; // on écrit dans oss, sans affichage cout << "resultat : " << oss.str() << endl; // affiche : resultat : 2pi=6.28 return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 84 / 152
struct Types structurés Une structure est un objet agrégé comprenant un ou plusieurs champs (membres) d éventuellement différents types que l on regroupe sous un seul nom afin d en faciliter la manipulation et le traitement. Chacun des champs peut avoir n importe quel type, y compris une structure, à l exception de celle à laquelle il appartient. Déclaration d une structure [classe de memorisation] struct [etiquette] { type champ_1;... type champ_n; } [identificateur]; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 85 / 152
Types structurés Déclaration d une structure Le mot clé struct indique la déclaration d une structure qui correspond à une liste de déclarations entre accolades. Il est possible de faire suivre le mot struct d un nom baptisé etiquette de la structure. Cette etiquette désigne cette sorte de structure et, par la suite, peut servir pour éviter d écrire entièrement toute la déclaration. Déclaration d une structure date : struct date { int jour, mois, annee; }; struct date date_naissance; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 86 / 152
Types structurés Définition de variable structurée Il suffit pour cela de faire suivre la liste entre accolade d identificateurs de variables. Définition de variables de type struct : struct { int jour, mois, annee; } naissancepierre, naissancemarie; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 87 / 152
Types structurés Déclaration et Définition de variable structurée Il est aussi possible de combiner les deux possibilités : date est le nom de la structure (le modèle), tandis que naissancepierre, naissancemarie et mortcoluche sont des variables. Définition de variables de type struct date : struct date { int jour, mois, année; ) naissancepierre, naissancemarie = {7, 11, 1867}; // initialisation de la structure naissancemarie struct date mortcoluche; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 88 / 152
Types structurés Particularités des structures La taille d une structure est la somme des tailles de tous les objets qui la compose (cf. sizeof()). Dans notre exemple, la structure aura une taille de 3*4 (int) soit 12 octets. Pour accéder aux champs dune structure, il faut distinguer 2 cas : 1 la structure est délivrée par une variable : cet accès se fait à l aide de l opérateur. (point) Exemple : naissancemarie.mois désigne le champ mois de la variable naissancemarie (soit 11 dans notre exemple). 2 la structure est délivrée par un pointeur p : cet accès se fait à l aide de l opérateur -> (indirection) Exemple : p->jour désigne le champ jour de la variable p Ou : (*p).jour désigne le champ jour de la variable p tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 89 / 152
Types structurés Définition de synonyme de type Utilisation courante : le programmeur a l habitude de définir un nouveau type lorsqu il déclare une nouvelle structure. La convention habituelle est de mettre ce nom en majuscules. Utilisation de typedef : // directement : typedef struct { int jour, mois, annee; } DATE; // le type DATE // ou apres : typedef struct date DATE_NAISSANCE; // le type DATE_NAISSANCE tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 90 / 152
Types structurés Utilisation des structures : int main() { struct date naissancemarie = {7, 11, 1867}; // initialisation de la structure naissancemarie struct date *p_naissancemarie = &naissancemarie; DATE_NAISSANCE naissancepierre = {15, 5, 1859}; // initialisation de la structure naissancepierre DATE mortcoluche = {19, 6, 1986}; printf("marie Curie est née le %02d/%02d/%4d\n", naissancemarie.jour, naissancemarie.mois, naissancemarie.annee); printf("marie Curie est née le %02d/%02d/%4d\n", p_naissancemarie->jour, p_naissancemarie->mois, p_naissancemarie->annee); printf("pierre Curie est né le %02d/%02d/%4d\n", naissancepierre.jour, naissancepierre.mois, naissancepierre.annee); printf("coluche est mort le %02d/%02d/%4d\n\n", mortcoluche.jour, mortcoluche.mois, mortcoluche.annee); printf("la structure struct date occupe une taille de %d octets\n", sizeof(struct date)); } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 91 / 152
Types structurés L exécution suivante illustre différentes utilisations des structures : Marie Curie est nee le 07/11/1867 Marie Curie est nee le 07/11/1867 Pierre Curie est ne le 15/05/1859 Coluche est mort le 19/06/1986 La structure struct date occupe une taille de 12 octets tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 92 / 152
union Types structurés Une union est conceptuellement identique à une structure mais peut, à tout moment, contenir n importe lequel des différents champs. Rappel : Une structure définit une suite de champs, portant chacun un unique nom et tous présents simultanément. Ils sont rangés dans des emplacements mémoire consécutifs. Une union, d un autre côté, définit plusieurs manières de regarder le même emplacement mémoire. A l exception de ceci, la façon dont sont déclarés et référencées les structures et les unions est identique. Déclaration d une union [classe de memorisation] union [etiquette] { type champ_1;... type champ_n; } [identificateur]; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 93 / 152
Types structurés Définition d une union On va déclarer une union pour conserver une valeur d une mesure issue d un capteur générique, qui peut par exemple fournir une mesure sous forme d un char (-128 à +127), d un int (-2147483648 à 2147483647) ou d un float. Une telle définition définit une variable de nom valeurcapteur et de type union qui peut, à tout moment, contenir SOIT un entier, SOIT un réel, SOIT un caractère. La taille mémoire de la variable valeurcapteur est égale à la taille mémoire du plus grand type qu elle contient (ici c est float). Définition d une union : union mesurecapteur { int ival; float fval; char cval; } valeurcapteur; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 94 / 152
Types structurés Utilisation d une union : #include <stdio.h> typedef union mesurecapteur { int ival; float fval; char cval; } CAPTEUR; int main() { CAPTEUR vitessevent, temperaturemoteur, pressionatmospherique; pressionatmospherique.ival = 1013; /* un int */ temperaturemoteur.fval = 50.5; /* un float */ vitessevent.cval = 2; /* un char */ printf("la pression atmosphérique est de %d hpa\n", pressionatmospherique.ival); printf("la température du moteur est de %.1f C\n", temperaturemoteur.fval); printf("la vitesse du vent est de %d km/h\n", vitessevent.cval); printf("le type CAPTEUR occupe une taille de %d octets\n", sizeof(capteur)); return 0; } L exécution du programme d essai permet de vérifier cela : La pression atmosphérique est de 1013 hpa La température du moteur est de 50.5 C La vitesse du vent est de 2 km/h Le type CAPTEUR occupe une taille de 4 octets tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 95 / 152
Champs de bits Types structurés Les champs de bits ("Drapeaux" ou "Flags"), qui ont leur principale application en informatique industrielle, sont des structures qui ont la possibilité de regrouper (au plus juste) plusieurs valeurs. La taille d un champ de bits ne doit pas excéder celle d un entier. Pour aller au-delà, on créera un deuxième champ de bits. On utilisera le mot clé struct et on donnera le type des groupes de bits, leurs noms, et enfin leurs tailles : Déclaration d un champ de bits [classe de memorisation] struct [etiquette] { { type champ_1 : nombre_de_bits; type champ_2 : nombre_de_bits; [...] type champ_n : nombre_de_bits; } [identificateur]; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 96 / 152
Types structurés Champ de bits : exemple Si on reprend le type structuré date, on peut maintenant décomposer ce type en trois groupes de bits (jour, mois et annee) avec le nombre de bits suffisants pour coder chaque champ. Les différents groupes de bits seront tous accessibles comme des variables classiques d une structure ou d une union. La taille mémoire d une variable de ce type sera égale à 2 octets (5 + 4 + 7 = 16 bits). Le champ de bits date : struct date { unsigned short jour : 5; // 2^5 = 0-32 soit de 1 à 31 unsigned short mois : 4; // 2^4 = 0-16 soit de 1 à 12 unsigned short annee : 7; // 2^7 = 0-128 soit de 0 à 99 (sans les siècles) }; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 97 / 152
Types structurés Utilisation d un champ de bits : typedef struct date DATE_NAISSANCE; int main (int argc, char **argv) { struct date naissanceritchie = {9, 9, 41}; struct date *p_naissanceritchie = &naissanceritchie; DATE_NAISSANCE naissancethompson = {4, 2, 43}; struct date mortritchie = {12, 10, 11}; printf("dennis Ritchie est né le %02d/%02d/%2d\n", naissanceritchie.jour, naissanceritchie.mois, naissanceritchie.annee); printf("dennis Ritchie est né le %02d/%02d/%2d\n", p_naissanceritchie->jour, p_naissanceritchie->mois, p_naissanceritchie->annee); printf("dennis Ritchie est mort le %02d/%02d/%2d\n\n", mortritchie.jour, mortritchie.mois, mortritchie. annee); printf("ken Thompson est né le %02d/%02d/%2d\n", naissancethompson.jour, naissancethompson.mois, naissancethompson.annee); printf("la structure champs de bits date occupe une taille de %d octets\n", sizeof(struct date)); return 0; } L exécution du programme d essai permet de vérifier cela : Dennis Ritchie est né le 09/09/41 Dennis Ritchie est né le 09/09/41 Dennis Ritchie est mort le 12/10/11 Ken Thompson est né le 04/02/43 La structure champs de bits date occupe une taille de 2 octets tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 98 / 152
Conteneur en C++ Les conteneurs en C++ (1/2) Le C++ possède une bibliothèque standard (SL pour Standard Library) qui est composée, entre autre, d une bibliothèque de flux, de la bibliothèque standard du C, de la gestion des exceptions,..., et de la STL (Standard Template Library : bibliothèque de modèles standard). En fait, STL est une appellation historique communément acceptée et comprise. Dans la norme, on ne parle que de SL. Un conteneur (container) est un objet qui contient d autres objets. Il fournit un moyen de gérer les objets contenus (au minimum ajout, suppression, parfois insertion, tri, recherche,...) ainsi qu un accès à ces objets qui dans le cas de la STL consiste très souvent en un itérateur. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 99 / 152
Conteneur en C++ Les conteneurs en C++ (2/2) Les itérateurs permettent de parcourir une collection d objets sans avoir à se préoccuper de la manière dont ils sont stockés. Ceci permet aussi d avoir une interface de manipulation commune, et c est ainsi que la STL fournit des algorithmes génériques qui s appliquent à la majorité de ses conteneurs (tri, recherche,...). Parmi les conteneurs disponibles dans la STL on trouve les tableaux (vector), les listes (list), les ensembles (set), les piles (stack), et beaucoup d autres. Nous allons nous intéresser à la notion de vector. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 100 / 152
Conteneur en C++ vector : les tableaux en C++ Un vector est un tableau dynamique où il est particulièrement aisé d accéder directement aux divers éléments par un index, et d en ajouter ou en retirer à la fin. A la manière des tableaux de type C, l espace mémoire alloué pour un objet de type vector est toujours continu, ce qui permet des algorithmes rapides d accès aux divers éléments. La forme générique de déclaration d un tableau est la suivante : vector<type> identificateur(taille) ; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 101 / 152
Conteneur en C++ Exemple 1 : les vector #include <iostream> #include <vector> using namespace std; int main() { vector<int> v1; // un vecteur d entier vide v1.push_back( 10 ); // ajoute l entier 10 à la fin v1.push_back( 9 ); // ajoute l entier 9 à la fin v1.push_back( 8 ); // ajoute l entier 8 à la fin // enleve le dernier élément v1.pop_back(); // supprime l entier 8 // utilisation d un indice pour parcourir le vecteur v1 cout << "Le vecteur v1 contient " << v1.size() << " entiers : \n"; for(int i=0;i<v1.size();i++) cout << "v1[" << i << "] = " << v1[i] << \n ; cout << \n ; } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 102 / 152
Conteneur en C++ Exemple 2 : les vector #include <iostream> #include <vector> using namespace std; int main() { vector<int> v2(4, 100); // un vecteur de 4 entiers initialisés avec la valeur 100 // utilisation d un itérateur pour parcourir le vecteur v2 cout << "Le vecteur v2 contient " << v2.size() << " entiers : "; for (vector<int>::iterator it = v2.begin(); it!= v2.end(); ++it) cout << << *it; cout << \n ; } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 103 / 152
Conteneur en C++ Ces deux exemples donnent : Le vecteur v1 contient 2 entiers : v1[0] = 10 v1[1] = 9 Le vecteur v2 contient 4 entiers : 100 100 100 100 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 104 / 152
Les fonctions Programmation modulaire Le découpage d un programme en sous-programmes est appelée programmation modulaire. La programmation modulaire se justifie par de multiples raisons : un programme écrit d un seul tenant devient très difficile à comprendre dès lors qu il dépasse une page de texte la programmation modulaire permet d éviter des séquences d instructions répétitives la programmation modulaire permet le partage d outils communs qu il suffit d avoir écris et mis au point une seule fois des fonctions convenablement choisies permettent souvent de dissimuler les détails d un calcul contenu dans certaines parties du programme qu il n est pas indispensable de connaître. D une manière générale, la programmation modulaire clarifie l ensemble d un programme et facilite les modifications ultérieures. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 105 / 152
Procédure et Fonction Les fonctions Dans la plupart des langages évolués, on trouve deux sortes de modules : les procédures et les fonctions. Le langage C n offre pour sa part qu une seule sorte de module : la fonction. Si une fonction ne rend aucun résultat et elle peut alors être considérée comme une procédure. Une fonction est déclarée de la manière suivante : [classe de memorisation] [type] identificateur (parametres) ; La classe de mémorisation et type sont optionnels. Lorsque la classe de mémorisation est absente, la fonction est considérée externe et est donc accessible par tous les modules avec lesquels une édition de liens est faite. Si le type est absent, le résultat de la fonction est par défaut un entier int. Lorsqu il est présent, il définit le type du résultat que produit la fonction. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 106 / 152
Fonction Les fonctions Une fonction est définie de la manière suivante : [classe de memorisation] [type] identificateur (parametres) {... return resultat ; } La liste de paramètres n est en fait qu une liste de couples type/identificateur séparés par des virgules. Derrière cela, se trouve le bloc {} qui définit les traitements effectués dans la fonction. Pour retourner son résultat, la fonction doit utiliser l instruction return. Dans tous les cas, une fonction peut être utilisée : comme opérande dans une expression, à partir du moment où il y a concordance de types comme instruction. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 107 / 152
Instruction return Les fonctions L instruction return provoque le retour chez l appelant de la fonction courante. Les deux formes d utilisation sont : return ou return expression. Dans ce premier cas, il s agit d une fonction ne retournant aucun résultat (void). Elle a été programmée en tant que procédure. void foo() { instruction; return; } Dans ce second cas, il s agit d une fonction qui retourne la valeur de l expression a%2. Si nécessaire la valeur retournée est convertie, comme pour une affectation, dans le type du résultat. int estimpair(int a) { return (a%2); // retourne 0 (pair) ou 1 (impair) } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 108 / 152
Les fonctions Exemple : la fonction multiplier #include <stdio.h> /* La fonction de nom multiplier calcule la multiplication de deux entiers (a et b) et retourne le résultat sous forme d entier */ int multiplier(int a, int b) { return a * b; } int main() { int x = 2; int y = 3; int res1, res2; // Appel de fonction en lui passant les valeurs de x et y res1 = multiplier(x, y); // on récupère la valeur retournée dans res1 printf("la multiplication de %d par %d donne comme résultat : %d\n", x, y, res1); res2 = multiplier(x, y) + 2; // on peut utiliser un fonction dans une instruction printf("et si on ajoute 2, on obtient : %d\n", res2); return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 109 / 152
Les fonctions Exemple : la fonction multiplier Exemple La multiplication de 2 par 3 donne comme résultat : 6 Et si on ajoute 2, on obtient : 8 Remarques : En aucun cas l imbrication de fonctions n est possible. Enfin, l appel de fonctions en C peut se faire de manière récursive (une fonction peut s appeler elle-même). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 110 / 152
Les fonctions Pour conclure, il faut distinguer : la déclaration d une fonction qui est une instruction fournissant au compilateur un certain nombre d informations concernant une fonction. Il existe une forme recommandée dite prototype : int plus(int, int) ; fichier en-tête (.h) sa définition qui revient à écrire le corps de la fonction (qui définit donc les traitements effectués dans le bloc {} de la fonction) int plus(int a, int b) { return a + b ; } fichier source (.c ou.cpp) l appel qui est son utilisation. Elle doit correspondre à la déclaration faite au compilateur qui vérifie. int res = plus(2, 2) ; fichier source (.c ou.cpp) Remarque : La définition d une fonction tient lieu de déclaration. Mais elle doit être "connue" avant son utilisation (un appel). Sinon, le compilateur génère un message d avertissement (warning) qui indiquera qu il a lui-même fait une déclaration implicite de cette fonction : "attention : implicit declaration of function multiplier " tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 111 / 152
Les fonctions Pointeur vers une fonction Le nom d une fonction est une constante de type pointeur : int f(int x, int y) { return x+y; } int (*pf)(int, int); // pointeur vers fonction admettant 2 entiers en paramètres et retournant un entier pf = f; // pointeur vers la fonction f printf("%d\n", (*pf)(3, 5)); // affiche 8 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 112 / 152
Les fonctions Passage de paramètres Lors d un appel de fonction, les paramètres passés à la fonction doivent correspondre aux paramètres déclarés par leur nombre et par la compatibilité de leurs types. En C, il n existe qu un seul mode de passage des paramètres : c est le passage par valeur. On dit aussi par passage par recopie (de valeurs). Il faut donc considérer un paramètre comme une variable locale ayant été initialisée avant l appel de la fonction. int multiplier(int a, int b); int x = 2; int y = 3; int res; // Appel de fonction en lui passant les valeurs de x et y // donc la valeur de x est recopié dans a et la valeur de y est recopié dans b res = multiplier(x, y); Remarque : les noms peuvent être identiques cela ne change rien au mécanisme de recopie! tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 113 / 152
Les fonctions Problème : tentative de permutation de deux variables #include <stdio.h> void permuter(int a, int b) { int c; c = a; a = b; b = c; printf("dans la fonction après permutation : a = %d et b = %d\n", a, b); } int main() { int a = 2; int b = 3; printf("dans le main : a = %d et b = %d\n", a, b); permuter(a, b); printf("après l appel de la fonction : a = %d et b = %d\n", a, b); } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 114 / 152
Les fonctions Mise en évidence du problème de modification de copies Effectivement, cela ne marche pas car la fonction permuter à travailler sur des copies de a et b : La permutation ne fonctionne pas : Dans le main : a = 2 et b = 3 Dans la fonction après permutation : a = 3 et b = 2 Après l appel de la fonction : a = 2 et b = 3 Piste : L exception à cette règle est le cas des tableaux. En effet, lorsque le nom d un tableau constitue l argument d une fonction, c est l adresse du premier élément qui est transmise. Ses élément ne sont donc pas recopiés. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 115 / 152
Les fonctions Permutation d éléments d un tableau (1/3) Ceci reste en effet cohérent avec le fait qu il n existe pas de variable désignant un tableau comme un tout. Quand on déclare int t[10], t ne désigne pas l ensemble du tableau. t est une constante de type pointeur vers un int dont la valeur est &t[o] (adresse du premier élément du tableau). #include <stdio.h> void permuter(int t[]) { int c; } c = t[0]; t[0] = t[1]; t[1] = c; printf("dans la fonction après permutation : t[0] = %d et t[1] = %d\n ", t[0], t[1]); tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 116 / 152
Les fonctions Permutation d éléments d un tableau (2/3) Remarque : c est une très bonne chose en fait car dans le cas d un "gros tableau", on évite ainsi de recopier toutes les cases. Le passage d une adresse (par exemple 32 bits) sera beaucoup plus efficace et rapide. int main() { int t[2] = { 2, 3 }; printf("dans le main : t[0] = %d et t[1] = %d\n", t[0], t[1]); permuter(t); printf("après l appel de la fonction : t[0] = %d et t[1] = %d\n", t [0], t[1]); } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 117 / 152
Les fonctions Permutation d éléments d un tableau (3/3) Effectivement, cela marche car la fonction permuter à travailler avec l adresse du tableau : Exemple Dans le main : t[0] = 2 et t[1] = 3 Dans la fonction après permutation : t[0] = 3 et t[1] = 2 Après l appel de la fonction : t[0] = 3 et t[1] = 2 En conséquence, pour réaliser l effet de passage de paramètre par adresse, il suffit alors de passer en paramètre un pointeur vers la variable. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 118 / 152
Les fonctions Passage par adresse (1/2) Pour qu une fonction puisse modifier le contenu d une variable passée en paramètre, on doit utiliser en C un pointeur sur cette variable. Exemple de déclaration : void permuter(int *pa, int *pb) ; // pa et pb sont des pointeurs sur des int. Le passage se fait toujours par valeur ou par recopie sauf que maintenant, on passe l adresse d une variable. void permuter(int *pa, int *pb); int main() { int a = 2; int b = 3; printf("dans le main : a = %d et b = %d\n", a, b); permuter(&a, &b); // l adresse (&) de a est recopiée dans le pointeur pa, idem pour l adresse de b dans pb printf("après l appel de la fonction : a = %d et b = %d\n", a, b); return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 119 / 152
Les fonctions Passage par adresse (2/2) void permuter(int *pa, int *pb) { int c; c = *pa; *pa = *pb; *pb = c; printf("dans la fonction après permutation : a = %d et b = %d\n", *pa, *pb); } La permutation fonctionne maintenant : Dans le main : a = 2 et b = 3 Dans la fonction après permutation : a = 3 et b = 2 Après l appel de la fonction : a = 3 et b = 2 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 120 / 152
Les fonctions Protection contre le passage par adresse (1/4) Remarque n 1 : lorsqu on passe une adresse par pointeur, il y a un risque que la fonction modifie cette adresse. Dans ce cas, on peut se protéger en déclarant l adresse passée comme constante. #include <stdio.h> void foo(int * const pa) { int un_autre_a; pa = &un_autre_a; // tentative de modification d adresse contenue dans le pointeur } int main() { int a = 2; foo(&a); return 0; } On obtient un contrôle d accès à la compilation : In function foo : erreur: assignment of read-only location pa tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 121 / 152
Les fonctions Protection contre le passage par adresse (2/4) Remarque n 2 : On a vu que lorsque l on doit passer en paramètre des "grosses" tailles de variables, il serait plus efficace et plus rapide à l exécution de passer une adresse. Mais il y aurait un risque que la fonction modifie cette variable. Dans le cas d un accès limité en lecture, on peut se protéger en déclarant le pointeur comme constant. #include <stdio.h> void foo(const long double *pa) { printf("je suis un pointeur de taille plus légère %d octets\n", sizeof(pa)); printf("je contiens l adresse de a : pa = %p\n", pa); printf("et j ai un accès en lecture : a = %.1Lf\n", *pa); /* et non, tu ne peux mas modifier le contenu de la variable pointée! */ printf("mais pas en écriture!\n"); *pa = 3.5; // ligne 10 à enlever! } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 122 / 152
Les fonctions Protection contre le passage par adresse (3/4) On obtient un contrôle d accès à la compilation : In function foo : ligne 10: erreur: assignment of read-only location *pa tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 123 / 152
Les fonctions Protection contre le passage par adresse (4/4) int main() { long double a = 2.0; printf("je suis une grosse variable de taille %d octets\n",sizeof(a)); printf("je suis a et j ai pour valeur : a = %.1Lf\n", a); printf("et pour adresse : &a = %p\n\n", &a); foo(&a); return 0; } Une utilisation de pointeur constant : Je suis une grosse variable de taille 12 octets Je suis a et j ai pour valeur : a = 2.0 et pour adresse : &a = 0xbf8d8240 Je suis un pointeur de taille plus légère 4 octets Je contiens l adresse de a : pa = 0xbf8d8240 et j ai un accès en lecture : a = 2.0 mais pas en écriture! tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 124 / 152
Les fonctions Passage par référence (1/2) En C++, on a aussi la possibilité d utiliser le passage par référence. Intérêt : lorsqu on passe des variables (ou des objets) en paramètre de fonctions ou méthodes et que le coût d une recopie par valeur est trop important ("gros" objet), on choisira un passage par référence. void permuter(int &a, int &b) { int c; c = a; a = b; b = c; printf("dans la fonction après permutation : a = %d et b = %d\n", a,b); } int main() { int a = 2; int b = 3; printf("dans le main : a = %d et b = %d\n", a, b); permuter(a, b); printf("après l appel de la fonction : a = %d et b = %d\n", a, b); return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 125 / 152
Les fonctions Passage par référence (2/2) La permutation fonctionne également en utilisant un passage par référence : Dans le main : a = 2 et b = 3 Dans la fonction après permutation : a = 3 et b = 2 Après l appel de la fonction : a = 3 et b = 2 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 126 / 152
Les fonctions Protection contre le passage par référence Si le paramètre ne doit pas être modifié, on utilisera alors un passage par référence sur une variable constante : void foo(const long double &a) { printf("j ai un accès en lecture : a = %.1Lf\n", a); printf("mais pas en écriture!\n"); //a = 3.5; // interdit car const! (voir message) } int main() { long double a = 2.0; printf("je suis a et j ai pour valeur : a = %.1Lf\n", a); foo(a); return 0; } Le compilateur nous préviendra de toute tentative de modification : In function void foo(const long double&) : erreur: assignment of read-only reference a tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 127 / 152
Bilan Les fonctions En résumé, voici les différentes déclarations en fonction du contrôle d accès désiré sur un paramètre reçu : passage par valeur accès en lecture seule à la variable passée en paramètre : void foo(int a) ; passage par adresse accès en lecture seule à la variable passée en paramètre : void foo(const int *a) ; passage par adresse accès en lecture et en écriture à la variable passée en paramètre : void foo(int *a) ; passage par adresse accès en lecture et en écriture à la variable passée en paramètre (sans modification de son adresse) : void foo(int * const a) ; passage par adresse accès en lecture seule à la variable passée en paramètre (sans modification de son adresse) : void foo(const int * const a) ; passage par référence accès en lecture et en écriture à la variable passée en paramètre : void foo(int &a) ; passage par référence accès en lecture seule à la variable passée en paramètre : void foo(const int &a) ; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 128 / 152
Surcharge Les fonctions Il est généralement conseillé d attribuer des noms distincts à des fonctions différentes. Cependant, lorsque des fonctions effectuent la même tache sur des objets de type différent, il peut être pratique de leur attribuer des noms identiques. Ce n est pas possible de réaliser cela en C mais seulement en C++. L utilisation d un même nom pour des fonctions (ou méthodes) s appliquant à des types différents est nommée surcharge. Remarque : cette technique est déjà utilisée dans le langage C++ pour les opérateurs de base. L addition, par exemple, ne possède qu une seul nom (+) alors qu il est possible de l appliquer à des valeurs entières, virgule flottante, etc... tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 129 / 152
Les fonctions La surcharge de fonctions en action (1/2) #include <iostream> using namespace std; // Un seul nom au lieu de print_int, print_float,... void print(int); void print(float); int main (int argc, char **argv) { int n = 2; float x = 2.5; print(n); print(x); } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 130 / 152
Les fonctions La surcharge de fonctions en action (1/2) void print(int a) { cout << "je suis print et j affiche un int : " << a << endl; } void print(float a) { cout << "je suis print et j affiche un float : " << a << endl; } On obtient : je suis print et j affiche un int : 2 je suis print et j affiche un float : 2.5 tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 131 / 152
Les fonctions Signature et Prototype Remarque importante : la surcharge ne fonctionne que pour des signatures différentes et le type de retour d une fonction ne fait pas partie de la signature. On distingue : La signature d une fonction est le nombre et le type de chacun de ses arguments. Le prototype d une fonction est le nombre et le type de ses arguments (signature) et aussi de sa valeur de retour. Aller plus loin : il est aussi possible de surcharger les opérateurs de base avec des signatures différentes pour ses propres classes. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 132 / 152
Classe d allocation Statique ou automatique La classe d allocation détermine la façon dont l emplacement mémoire attribué à une variable est géré. Elle peut être statique ou automatique. Les variables de classe statique voient leur emplacement alloué une fois pour toutes avant le début de l exécution du programme. C est le cas des variables globales. Les variables de classe automatique voient leur emplacement alloué au moment de l entrée dans un bloc ou une fonction. Il est supprimé lors de la sortie de ce bloc ou de cette fonction. C est le cas des variables locales. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 133 / 152
Déclaration Classe d allocation La syntaxe d une déclaration est : [classe_de_mémorisation] [qualifieurs] type identificateur [=valeur] ; Les crochets [] indiquent ce qui est optionnel donc non obligatoire pour réaliser une déclaration. classe_de_mémorisation peut prendre les valeurs suivantes : static, extern, automatic, register. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 134 / 152
Classe d allocation Cas des variables globales static : Ces variables sont globales au bloc ou au fichier de compilation et ne seront connues que du module dans lequel elles sont définies. Les variables statiques sont initialisées à 0 par défaut. extern : extern permet de déclarer des variables qui sont définies dans un autre fichier source. Une telle variable est accessible par tous les modules avec lesquels une édition de liens est effectuée. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 135 / 152
Classe d allocation Cas des variables locales (1/2) static : La visibilité de ces variables est limitée au bloc dans lequel elles sont définies. Si un programme appelle à nouveau un bloc dans lequel une variable statique est déclarée, celle-ci conserve sa précédente valeur. extern : Comme pour les variables globales, extern permet de déclarer des variables qui sont définies ailleurs. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 136 / 152
Classe d allocation Cas des variables locales (2/2) automatic : Les variables automatiques sont considérées locales au bloc dans lequel elles sont déclarées. Un espace leur est alloué (dans la pile, stack en anglais) à chaque entrée de bloc et est libéré a chaque sortie de bloc. Une variable automatique ne reçoit aucune valeur initiale par défaut. L absence de toute classe de mémorisation dans la déclaration d une variable locale amène le compilateur à le considérer automatic. register : Elles obéissent aux mêmes règles que les variables automatiques. Cependant, la désignation de register indique au compilateur que ces variables doivent être conservées dans des ressources à accès rapide du CPU (généralement dans un registre du microprocesseur). Il faut noter qu une variable register ne possède pas d adresse en mémoire et que par conséquent, l opérateur & ne peut lui être appliqué. D autre part, ce sont des variables limitées en nombre, ainsi qu à certains types (entier, caractère ou pointeur). Son utilisation sera vue plus tard (programmation multi-tâches par exemple) car elle nécessite un bon niveau d expertise. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 137 / 152
Classe d allocation Exemple : variable locale statique #include <stdio.h> int foo() { static int nb = 0; // je suis une variable locale statique // je conserverais ma valeur à chaque appel // mais attention je ne suis initialisée qu une seule fois nb++; // je compte le nombre d appels à la fonction foo printf("la fonction foo a été appelée %d fois\n", nb); } int main() { foo(); foo(); foo(); return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 138 / 152
Classe d allocation Exemple : exécution La fonction foo a été appelée 1 fois La fonction foo a été appelée 2 fois La fonction foo a été appelée 3 fois tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 139 / 152
Classe d allocation Exemple : valeur initiale des variables globales et locales #include <stdio.h> static int i; // je suis une variable globale limitee a ce fichier int foo() { int j; // je suis une variable locale, ma durée de vie est celle de la fonction foo printf("la variable locale j a pour valeur %d\n", j); // la variable locale j n a pas été initialisée, elle contient donc n importe quoi! printf("la variable locale j a pour adresse %p\n", &j); } int main() { printf("la variable globale i a pour valeur %d\n", i); // la variable globale i n a pas été initialisée, elle contient donc 0! printf("la variable globale i a pour adresse %p\n", &i); foo(); return 0; } tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 140 / 152
Classe d allocation Exemple : exécution La variable globale i a pour valeur 0 La variable globale i a pour adresse 0x8049660 La variable locale j a pour valeur 134513960 La variable locale j a pour adresse 0xbfc2facc Conclusion : ne pas initialiser ses variables est une source d erreur (bug). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 141 / 152
Classe d allocation Les variables globales Avantages grande facilité d utilisation gain de temps à l exécution facilite la communication entre des fonctions appelées à des niveaux différents (cf. programmation multi-tâche) pallie les limites de mémoire automatique sur certaines machines Inconvénients trop grande facilité d utilisation! plus de notion de paramètre (nécessite alors de nombreux commentaires pour indiquer qui modifient ces variables globales) risques d effets de bord (le plus dangereux) Conclusion : Les variables globales sont à bannir de la programmation structurée. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 142 / 152
La pile et le tas La mémoire La mémoire dans un ordinateur est une succession d octets (soit 8 bits), organisés les uns à la suite des autres et directement accessibles par une adresse. En C/C++, la mémoire pour stocker des variables est organisée en deux catégories : 1 la pile (stack) 2 le tas (heap) Remarque : Dans la plupart des langages de programmation compilés, la pile (stack) est l endroit où sont stockés les paramètres d appel et les variables locales des fonctions. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 143 / 152
La pile (stack) La mémoire La pile (stack) est un espace mémoire réservé au stockage des variables désallouées automatiquement. Sa taille est limitée mais on peut la régler (appel POSIX setrlimit). La pile est bâtie sur le modèle LIFO (Last In First Out) ce qui signifie "Dernier Entré Premier Sorti". Il faut voir cet espace mémoire comme une pile d assiettes où on a le droit d empiler/dépiler qu une seule assiette à la fois. Par contre on a le droit d empiler des assiettes de taille différente. Lorsque l on ajoute des assiettes on les empile par le haut, les unes au dessus des autres. Quand on les "dépile" on le fait en commençant aussi par le haut, soit par la dernière posée. Lorsqu une valeur est dépilée elle est effacée de la mémoire. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 144 / 152
Le tas (heap) La mémoire Le tas (heap) est l autre segment de mémoire utilisé lors de l allocation dynamique de mémoire durant l exécution d un programme informatique. Sa taille est souvent considére comme illimitée mais elle est en réalité limitée. Les fonctions malloc et free, ainsi que les opérateurs du langage C++ new et delete permettent, respectivement, d allouer et désallouer la mémoire sur le tas. La mémoire allouée dans le tas doit être désallouée explicitement. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 145 / 152
malloc et free Allocation dynamique L allocation d une nouvelle zone mémoire se fait dans un endroit particulier de la mémoire appelée le tas (heap). Elle se fait par la fonction malloc : void *malloc(size_t taille) ; L argument transmis correspond à la taille en octets de la zone mémoire désirée. La valeur retournée est un pointeur void * sur la zone mémoire allouée, ou NULL en cas d échec de l allocation. Si vous devez redimensionner un espace mémoire qui a été alloué dynamiquement, il faudra utiliser la fonction realloc(). La mémoire allouée doit, à un moment ou un autre, être libérée. Cette libération mémoire se fait par la procédure free : void free(void *pointeur) ; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 146 / 152
Allocation dynamique Exemple : allocation mémoire int *p; // pointeur sur un entier int *T; // pointeur sur un entier // allocation dynamique d un entier p = (int *)malloc(sizeof(int)); // alloue 4 octets (= int) en mémoire *p = 1; // ecrit 1 dans la zone mémoire allouée // allocation dynamique d un tableau de 10 int T = (int *)malloc(sizeof(int) * 10); // alloue 4 * 10 octets en mémoire // initialise le tableau avec des 0 (cf. la fonction memset) for(int i=0;i<10;i++) { *(T+i) = 0; // les 2 écritures sont possibles T[i] = 0; // identique à la ligne précèdente } // ou plus directement memset(t, 0, sizeof(int)*10); // il faudra alors inclure string.h free(p); free(t); Une fois qu une zone mémoire a été libérée, il ne faut sous aucun prétexte y accéder, de même qu il ne faut pas tenter de la libérer une seconde fois. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 147 / 152
Allocation dynamique Règles de bonne conduite Un certain nombre de règles, quand elles sont observées, permettent de se mettre à l abri de problèmes : Toute déclaration de pointeur s accompagne de son initialisation à NULL Avant tout appel de malloc(), on doit s assurer que le pointeur à allouer est bien NULL Après tout appel de malloc(), on s assure qu aucune erreur ne s est produite Avant de libérer une zone mémoire, on s assure que son pointeur n est pas NULL Dès qu une zone mémoire vient d être libérée par free(), on réinitialise son pointeur à NULL tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 148 / 152
new et delete Allocation dynamique Pour allouer dynamiquement en C++, on utilisera l opérateur new. Celui-ci renvoyant une adresse où est crée la variable en question, il nous faudra un pointeur pour la conserver. Manipuler ce pointeur, reviendra à manipuler la variable allouée dynamiquement. Pour libérer de la mémoire allouée dynamiquement en C++, on utilisera l opérateur delete. tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 149 / 152
Allocation dynamique Pour allouer dynamiquement en C++, on utilisera l opérateur new. Exemple : allocation dynamique #include <iostream> #include <iostream> #include <new> using namespace std; int main () { int * p1 = new int; // pointeur sur un entier *p1 = 1; // ecrit 1 dans la zone mémoire allouée cout << *p1 << endl; // lit et affiche le contenu de la zone mémoire allouée delete p1; // libère la zone mémoire allouée } return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 150 / 152
Allocation dynamique Exemple : allocation dynamique d un tableau #include <iostream> #include <new> using namespace std; int main () { int * p2 = new int[5]; // alloue un tableau de 5 entiers en mémoire // initialise le tableau avec des 0 (cf. la fonction memset) for(int i=0;i<5;i++) { *(p2 + i) = 0; // les 2 écritures sont possibles p2[i] = 0; // identique à la ligne précèdente cout << "p2[" << i << "] = " << p2[i] << endl; } } delete [] p2; // libère la mémoire allouée return 0; tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 151 / 152
Fuite de mémoire Allocation dynamique L allocation dynamique dans le tas ne permet pas la désallocation automatique. Chaque allocation avec "new" doit impérativement être libérée (détruite) avec "delete" sous peine de créer une fuite de mémoire. La fuite de mémoire est une zone mémoire qui a été allouée dans le tas par un programme qui a omis de la désallouer avant de se terminer. Cela rend la zone inaccessible à toute application (y compris le système d exploitation) jusqu au redémarrage du système. Si ce phénomène se produit trop fréquemment la mémoire se remplit de fuites et le système finit par tomber faute de mémoire. Ce problème est évité en Java en introduisant le mécanisme de "ramasse-miettes" (Garbage Collector). tv (BTS IRIS Avignon) Cours C/C++ tvaira@free.fr v0.2 152 / 152