Aller au contenu

C-Mos

Membres
  • Compteur de contenus

    17
  • Inscription

  • Dernière visite

À propos de C-Mos

  • Date de naissance 14/02/1983

Contact Methods

  • Website URL
    http://www.google.fr
  • ICQ
    0

Profile Information

  • Intérêts
    Java, C, C++, C#, Vb.Net, Php,

C-Mos's Achievements

Junior Member

Junior Member (3/12)

0

Réputation sur la communauté

  1. Bonjour à tous, voilà, depuis hier soir j'essaie de formater un PC avec Ubuntu Boot. Je l'ai déjà fais 1.000 fois mais là, quand je lance le CD de Boot, il Boot dessus et au moment de clicker sur l'option Installer Ubuntu au démarrage, le PC s'éteint radicalement ! Pouf, absolument plus rien ... Alors j'ai essayé avec une Mandriva pensant que le CD était défectueux, mais non ! Pareil ! Est-ce une sécurité anti-formatage ? (j'ai jamais vu ca ...) Vous avez une idée ? Merci à tous.
  2. Bonjour, Je vends mon ordinateur Laptop encore sous garantie, Très bonne état, peu servi, il n'a que 7 mois d'utilisation. Laptop EasyNote SJ81-B-002 - Processeur Turion 64 X2 TL-56 (1.8 GHz), 2 Go DDR2 667 MHz, - Disque dur SATA de 80 Go et un autre de 120 Go à 5400 trsmin, - Carte graphique Geforce 8600M GS 256 Mo dédié 1 Go TurboCache, - Ecran 17″ WXGA+ Diamond View d’une résolution de 1440×900, - Webcam 1.3 megapixel, - Graveur DVD double couche, - Lecteur de cartes 4 en 1, un pavé numérique et des connectiques Wi-Fi bg, HDMI (HDCP), Firewire, ExpressCard, Ethernet et USB 2.0 (x4). - Batterie 6 cellules, - Poids 3.5 Kg - OS Windows Vista Familiale Premium Prix d'achat 900€ Je veux le revendre car, je ne suis plus aussi mobile qu'auparavant. Un PC fixe me suffira. Je suis prêt aussi, à l'échanger contre un Pc Mac Fixe d'occasion, quitte à rajouter un peu d'argent en fonction des performances. Prix 800€ à débattre bien sûr ! - Merci - Me contacter par MP ou en m'envoyant un mail par l'intermédiaire du forum
  3. 'lut, vu que y en a pas, j'vais en faire un, ca doit pas etre bien difficile ! donc me faut des idées ! Langage : java ou vb.net. Bienvenue au intéressé!
  4. salut, ouais, mais c'est payant ! rahhh, tout se paye de nos jours ! le malheureux p'tit soft et hop 15€ !!! y a pas une version freeware !
  5. merci, chapeau pour la rapidité. j'm'y attendais pas.
  6. Bonjour, Je voulais savoir comme rediriger une flux video, vers Msn ou Skype(webcam). Genre une video de famille, que je montre au travers de ma webcam, comme si je filmais l'écran quoi ! merci ... car je n'ai pas trouver grand chose sur la redirection. Merci encore !!!
  7. C-Mos

    créer à 100% mon site

    Si tu as quelques notions de Css, html, ect ... : Créer ton site web via interface ! Un rendu pas mal, prise en main rapide, squelette du site déja fait, d'énorme fonctionnalités. N'etant pas très friant de la programmation Web, j'avoue, c'est fort. C'est tres tres complet, coté client que server, si t'as pas envie de te casser la tête à refaire un site et tu veux faire un truc classe ... -> http://www.joomla.fr/
  8. C-Mos

    Programmer en python

    Bonjour IDLE - an Integrated DeveLopment Environment for Python Using IDLE (for Version 0.5) @ bientot
  9. Bonjour : S19 File Format - as it pertains to 68HC11 AS11 output Extrait : J'éspère que cela répondra à ta demande. @+
  10. C-Mos

    Introduction au C++

    Chapitre : Classes et Objets, a suivre ... Reprise d'activité donc les prochains postes seront pour ... je sais pas trop. desolé ... @bientot
  11. C-Mos

    Introduction au C++

    Les arguments par defaut. Introduction. Exemples : Rappelons qu'en C Ansi, il est indispensable que l'appel de fonction contienne autant d'arguments que la fonction en attend. fct(int, int, int); //prototype ... fct(val1,val2,val3); // Appel Il était impossible d'appeler la fonction tout en oubliant un argument, du genre : fct(int, int int); //prototype ... fct(val1,val); // Appel Oui, on avez une erreur, lors de l'appel de la fonction, et ne me dites pas : "ah bon !" Et bien C++, permet d'appliquer un mécanisme d'attribution de valeurs par defaut à des arguments, 'tit exemple : #include <iostream> using namespace std; void fct(int, int=12); // prototype avec valeur par defaut 'int=12'. int main (void) { int x=3, y=4; cout << "Appel normal" << "\n"; fct(x,y); cout << "\n" << "Appel un seul argument" << "\n"; fct(x); return 0; } void fct(int val, int val2) { cout << "premier argument : "<< val << "\n"; cout << "deuxieme argument : "<< val2 << "\n"; } Résultat : Appel normal premier argument : 3 deuxieme argument : 4 Appel avec un seul argument premier argument : 3 deuxieme argument : 12 Explication : La declaration de fonction fct avant le main est réalisé par le prototype : void fct(int, int=12); La déclaration du second argument initialisé par défaut de cette fonction apparaît sous cette forme : int = 12; Chose nouvelle, car celle-ci précise au compilateur que si la fonction est appelée sans second argument, la fonction considérera que le deuxième argument sera de valeur 12, (valeur par défaut). Si nous aurions voulu appeler la fonction par : fct(); alors il aurait fallu initialiser le premier argument par une quelconque valeur defaut. Par exemple : #include <iostream> using namespace std; void fct(int=-1, int=12); int main (void) { int x=3, y=4; cout << "Appel normal" << "\n"; fct(x,y); cout << "\n" << "Appel avec un seul argument" << "\n"; fct(x); cout << "\n" << "Appel sans aucun argument" << "\n"; fct(); return 0; } void fct(int val, int val2) { cout << "premier argument : "<< val << "\n"; cout << "deuxieme argument : "<< val2 << "\n"; } Résultat : Appel normal premier argument : 3 deuxieme argument : 4 Appel avec un seul arguments premier argument : 3 deuxieme argument : 12 Appel sans aucun argument premier argument : -1 deuxieme argument : 12 Fastoche ! Propriétés des arguments par défaut Si le programmeur décide d'établir une déclaration de prototype avec des valeurs par defaut, alors il est OBLIGTOIRE de les déclarer en dernière liste. float fct(int=0, long, double=1); Ceci, est totalement INTERDIT ! Et, en effet, il est évident d'en comprendre pourquoi ? Supposons que le compilateur ait accépté cette déclaration et qu'ensuite nous avions appelé la fonction par fct(10,13); alors le compilateur aurait pû très bien l'interpréter comme étant : fct(0,10,13); ou encore, fct(10,13,1); On constate bien que ce mécanisme proposé par C++ revient à fixer les valeurs par défaut dans la déclaration de la fonction, et non dans sa définition. Nous constaterons lorsque nous aborderons le chapitre : " Constructeur d'une classe ", l'intérêt principal des arguments par défaut. Surdéfinition de fonction De manière globale, nous parlons de "surdéfinition", lorsqu'un même symbole possède plusieurs définitions différentes, le choix de la définition se fera en focntion du contexte. En effet, nous connaissons à titre d'exemple, le symbole *, qui peut soit être considéré comme un opérateur d'indirection ou encore comme une multiplication. L'un des grands atouts du C++ est de surdéfinir la plupart des opérateurs lorsqu'ils sont associés à la notion de classe. Pour pouvoir employer plusieurs fonctions de même nom, il faut bien sûr un critère particulier permettant au compilateur de choisir judicieusement la bonne fonction. C++, basera sont choix, par l'identification du type d'argument. Mise en oeuvre de la surdéfinition de fonction Un peu de pratique ... Nous alons définir deux focntions de même nom, appelé : sosie , la première fonction possèdera un argument de type char, et la seconde possédera un argument de type int, ce qui les differencie bien l'une de l'autre. Nous transmetterons les arguments par référence. #include <iostream> using namespace std; void sosie(int &); // Déclaration des prototypes void sosie(char &); int main (void) { int val = 3; char lettre = 'a'; sosie(lettre); // sosie I : Lettre. sosie(val); // sosie II : Valeur return 0; } void sosie(char &car) { cout << "sosie I : Lettre : " << car << "\n"; } void sosie(int &var) { cout << "sosie II : Valeur : " << var << "\n"; } Résultat : sosie I : Lettre : a sosie II : Valeur : 3 On constate que le compilateur à bien mis en place l'appel de la "bonne fonction" sosie au vu des arguments. Exemple d'une fonction surdéfinie Cette exemple été très simple, du fait que nous appelions toujours la fonction sosie avec un argument ayant exactement l'un des types prévus dans les prototypes( int ou char). Mais que se passera-t-il si nous appelions la fonction avec un argument de type double, long, ou encore pointeur, ou si nous avions à faire face à des fonctions de plusieurs arguments. Nous allons donc aborder les règles de détermination d'une fonction surdéfinie. Examinons avant, quelques exemples intuitifs. Vous souvenez vous des conversions de type ??? si non, retour au chapitre précédent. Exemple 1: void sosie(double); // sosie I void sosie(int); // sosie II char a, float b; ... sosie(a); // appel de sosie II, après conversion de [i]c[/i] en [i]int[/i] sosie(b); // appel de sosie I, après conversion de [i]b[/i] en [i]double[/i] sosie('w'); // appel de sosie II, après conversion de [i]w[/i] en [i]int[/i] Exemple 2: void affichage(char *); // affichage I void affichage(void *); // affichage II char *valeur1; double *valeur2; ... affichage(valeur1); // appel de affichage I affichage(valeur2); // appel de affichage II, après conversion de [i]valeur2[/i] en [i]void *[/i] Exemple 3: void program(double, int); // program I void program(int, double); // program II int a,z; double e,r; char y; ... program(a,e); // appel de program II program(y,r); // appel de program II, après conversion de [i]y[/i] en [i]int[/i] program(a,z); // erreur de compilation Dans Exemple 3:, le compilateur génére une erreur, en effet, il subvient ici, une ambiguïté au niveau de la compilation. Deux cas se présente : Soit, le compilateur décide de convertir la variable 'a'de type int en double et de laisser z en int pour faire appel à program I, ou alors inversement. le compilateur décide de convertir la variable 'z'de type int en double et laisser a en int pour faire appel à program II. Voilà, si vous avez à peu près compris le principe de surdéfinition de fonction, essayez de quel appel il sera quesion dans les exemples suivant. Exemple 4: void test (int n=0, double x=0); // test I void test (double y=0, double p=0); // test II int n; double z; ... test(n,z); test(z,n); test(n); test(z); test(); Correction : test(n,z); // appel de test I test(z,n); // appel de test II test(n); // appel de test I test(z); // appel de test II test(); // erreur de compilation, dû à l'ambiguïté Bun voilà, cela ne demande pas d'énorme effort, vous avez compris le principe ! Voyons un cas particulier : Exemple 5: void term (int); // term I void term (const int); // term II ... int val; term(val); Ici, nous obtiendrons une erreur de compilation, en effet C++ n'a pas prévu de distinguer le int de const int, ceci se justifie par le fait que les deux fonctions term recevront une copie de l'information à traiter, et il n'y aura aucun risque de modification de valeur. A comprendre ici, que l'erreur ne tient qu'à la seul présence des déclarations, indépendamment de la nature des appels. Par contre, regardons l'exemple n°6. Exemple 6: void coco(int *); void coco(const int *); int a=4; const b=5; Cette fois-çi dans cette exemple, la distinction se justifie entre int * et const *, en effet on peut très bien prévoir que coco I modifie la valeur de la lvalue dont elle reçoit l'adresse, tandis que coco II n'en fait rien. Ah oui, vous savez tous ce qu'est une lvalue ? bon bun, je vous ecoute ! ... bizarre, plus personne ne parle ! Donc nous disions, une lvalue est la référence à quelque chose dont on peut modifier. Contraction de left value, qui signifie quelque chose qui peut apparaître à gauche d'un opérateur d'affectation. Nous pouvons réutiliser l'Exemple 6:, pour ne pas travailler avec les pointeurs, mais avec les références : Exemple 6 Bis: void coco(int &); //coco I void coco(const int &); //coco II int a=4; const b=5; ... coco(a); // appel de coco I coco(b); // appel de coco II On notera que le mode de transmission (valeur ou référence), n'intervient pas dans le choix d'une focntion surdéfinie. Par exemple, les déclarations suivantes, engendreront une erreur de compilation, dû à l'ambiguïté : void coco(int ); void coco(int &); Voyons cette exemple, que je vous propose et décrivez les conséquences de chaque appel : void coco(int &); //coco I void coco(const int &); //coco II int a; float b; ... coco(a); coco(3); coco(b); Donc on constate que coco(a); fait appel à coco I, coco(3); fait appel à coco II, ... mais de quelle manière ? En faite, le compilateur fera une copie éventuelle de la valeur '3' dans un entier temporaire dont la référence sera transmise coco. Et coco(b);, fait évidemment appel à coco II, après conversation de la valeur de 'b' en un entier temporaire dont la référence sera transmise à coco. Que se passe-t-il ? Le compilateur établie une recherche de correspondance de type. Pour cela il dispose d'un critère d'évaluation prédéfinie, tout ceci en 3 points : 1 - Conversion à correspondance exacte : Bon, on a compris le principe, on voit tous ce que ca veut dire. le(s) type(s) correspond(ent) aux/à type(s) argument(s) de la fonction. 2 - Conversion à correspondance avec promotions numériques : char et short -> int. float -> double. Rappel : Un argument transmis par référence ne peut être soumis à aucune conversion, aucune aucune, sauf s'il s'agit d'une référence à une constante, vous vous souvenez ?! 3 - Conversions dites standart : Il s'agit des conversions légales en C++, c'est à dire, celles qui peuvent être imposées par une affectation sans opérateur cast. Exception : L'Exception survient lorsque plusieurs fonctions conviennent au même niveau de correspondance, il y a alors : ambiguïté. Et si, aucune fonction ne convient à aucun niveau, alors il y a erreur de compilation. Mécanisme de la surdéfinition de fonctions[/liste] Jusqu'ici nous avons étudié, comment le compilateur procédé pour faire le choix de la "bonne fonction", en agissant sur un seul fichier, mais il sedrait tout à fait possible de compiler dans un premier temps un fichier source contenant la définition des fonctions, et ensuite utiliser ultérieurement ces fonctions dans un autre fichier source en nous contentant juste d'en finir les prototypes. Pour que ceci soit réalisable, l'éditeur de lien doit être en mesure d'effectuer le lien entre le choix opéré par le compilateur et la "bonne fonction" figurant dans un autre module objet. Cette reconnaissance est basée sur la modification, par le compilateur, des noms "externes" des fonctions. Un problème surviendra lorsque l'on souhaitera utiliser dans un programme C++, une fonction écrite et compilée en C, ou en Fortran, ou en Assembleur, ... (de manière globale, qui possède les mêmes conventions d'appels de fonctions). Pour résoudre ce problème, dont on ne verra pas la nature pour le moment, la solution serait de faire précédé son prototype par la mention extern "C". Soit la fonction écrite et compilé en C : float rad(float a, float b, int c); Dans le file C++, il nous suffira de fournir son prototype de cette forme : extern "C" float trame(float a, float b, int c); La forme collective de la déclaration extern est : extern "C" { void coco(int &); void sosie(int &var); void fct(int=-1, int=12); }; Le problème évoqué pour les fonctions C (Assembleur / Fortran) se pose, pour toutes les fonctions de la bibliothèque standart C que l'on réutilise en C++. On pourrait aussi employer au sein d'un même programme C++, une fonction C, ou Fortran, ou Assembleur et une ou plusieurs fonctions C++ de même nom mais d'arguments différents. Par exemple : float rad(float x); float rad(double c); Qui deviendra ainsi : extern "C" void rad(int); void rad(int *); void rad(char); Voili, voilà, la fin de ce paragraphe, bon c'est pas très difficile tout ça, on approfondira plus avec les exos. Les opérateurs new et delete Vous vous rappelez quels sont les fonctions du langage C qui s'occupe de la GDM (gestion dynamique de la mémoire) ? Malloc et free bien sûr, pouvons-nous les utiliser en C++ ? Béh oui, quelle question ! Mais le contexte de la programmation orientée objet, a fait introduire deux nouveaux concepts : new et delete, pariculièrement adaptés à la GDO ( Gestion dynamique d'objets). Par soucis d'homogénéité et de simplicité il serait plus judicieux d'utiliser ces nouveaux opérateurs, et de jeter, le plus loin possible le vieux Malloc et son comparse free. Le new, vous avez dit new ? Pour allouer de la mémoire : exemple 1 : La déclaration, int * case et l'instruction : case = new int; Voilà, c'est tout, pas plus ! Ceci permet d'allouer un espace mémoire pour un élément de type int et d'affecter à case l'adresse correspondante. En C, on faisez quoi dejà ? et bien ca : case = (int*) malloc((int)); // int* étant facultatif. Ah oui, je ne vous l'avais pas dit au début, mais en C++ les déclarations ont des emplacements libres, genre : for(int a; a<12; a++) { ... } // En C, c'est un coup de régle sur les doigts. // mais en C++, c'est possible. Revenons à notre new, donc on aura : int * case = new int; exemple 2 : La déclaration et instruction : char * civil = new char[50]; Permet d'allouer un emplacement nécessaire pour un tableau de 50 caractères, et place l'adresse de début dans : civil. En C, on aura : civil = (char *) malloc(100); Le delete Comme nous l'avons compris, delete, permet de libérer la mémoire, d'un emplacement allouer, bien évidemment. Si l'emplacement est alloué par l'opérateur new, alors on aura pas d'autre choix que de le libérer par delete. Ainsi pour libérer l'emplacement mémoire de nos exemples précédent nous feront : int * case = new int; ... delete case Capiche ?! REMARQUE : Nous verrons lorsque nous aborderons les tableaux d'objets, l'autre syntaxe de delete. L'opérateur new (Nothrow) L'opérateur new, peut générer une exception bad_alloc en cas d'échec. Dans les versions d'avant la norme, l'opérateur new, comme malloc, fournissait un pointeur nul, mais avec la nouvelle norme, on retrouve aussi se comportement en remplaçant le new habituel, par new(std::nothrow),(std:: etant superflu du moment où nous avons déclaré l'espace de nom par using). Pour mieux comprendre, regardons cette exemple, qui permet d'allouer des emplacements mémoire pour des tableaux d'entiers dont la taille est fournie en donnée (prenez une réservation de taille assez grande, sinon vous aurez beaucoup de tableaux de petite taille, et l'éxécution sera très longue, enfin ... cela dependra aussi de la taille restante de votre OS) Voici le code avec new(nothrow) : #include <iostream> #include <cstdlib> // en C :stdlib.h using namespace std; int main (int argc, char *argv[]) { long taille; int * adresse; int nbloc; cout << "Taille souhaitée ? "; cin >> taille; for(nbloc =1;; nbloc++) { adresse = new (nothrow) int [taille]; if (adresse == 0) { cout << "*** Manque Memoire ***\n"; exit (-1); } cout << "Allocation bloc numéro : " << nbloc << "\n"; } delete adresse; return 0; } Exécution : [cmos@localhost test]$ g++ test.cpp -o test [cmos@localhost test]./test Taille souhaitée ? 70000000 Allocation bloc numéro : 1 Allocation bloc numéro : 2 Allocation bloc numéro : 3 Allocation bloc numéro : 4 Allocation bloc numéro : 5 Allocation bloc numéro : 6 Allocation bloc numéro : 7 Allocation bloc numéro : 8 Allocation bloc numéro : 9 Allocation bloc numéro : 10 *** Manque Memoire *** Voici le même code sans new(nothrow) : #include <iostream> #include <cstdlib> // en C :stdlib.h using namespace std; int main (int argc, char *argv[]) { long taille; int * adresse; int nbloc; cout << "Taille souhaitée ? "; cin >> taille; for(nbloc =1;; nbloc++) { adresse = new int [taille]; if (adresse == 0) { cout << "*** Manque Memoire ***\n"; exit (-1); } cout << "Allocation bloc numéro : " << nbloc << "\n"; } delete adresse; return 0; } Exécution : [cmos@localhost test]$ g++ test.cpp -o test [cmos@localhost test]./test Taille souhaitée ? 70000000 Allocation bloc numéro : 1 Allocation bloc numéro : 2 Allocation bloc numéro : 3 Allocation bloc numéro : 4 Allocation bloc numéro : 5 Allocation bloc numéro : 6 Allocation bloc numéro : 7 Allocation bloc numéro : 8 Allocation bloc numéro : 9 Allocation bloc numéro : 10 terminate called after throwing an instance of 'std::bad_alloc' what(): St9bad_alloc Abandon Que faut-il en comprendre ? L'operateur new seul permet d'allouer ou faire une réservation mémoire et de lever une exception std::bad_alloc si il échoue, tandis que new (nothrow) permet aussi d'allouer de la mémoire, mais si il échoue il renverra NULL, et ne lévera pas d'exception. Raison pour laquelle nous avons pû gérer nous-même celle-çi par : [bloc] if (adresse == 0) { cout << "*** Manque Memoire ***\n"; exit (-1); } [/bloc] Nous aurions pû mettre bien évidemment à la place de : if (adresse == 0) ceci, if (adresse == NULL), ce qui revient au même. set_new_handler : Gestion des Débordements de mémoire Comme vu précédemment new déclenche une exception : bad_alloc en cas d'échec de réservation mémoire. Il nous ait permit aussi de définir une fonction de notre choix et de faire appel à celle-çi, en cas de manque mémoire. Pour cela, nous utiliserons la fonction : set_new_handler, en lui fournissant en argument l'adresse de la fonction prévue pour traiter le manque de mémoire. Exemple : /*********************************/ /** GESTION DEBORDEMENT MEMOIRE **/ /** SET_NEW_HANDLER **/ /*********************************/ #include <iostream> #include <new> // utilisation de set_new_handler #include <cstdlib> // en C :stdlib.h pour "exit" using namespace std; void deborde (void); // prototype pour gestion de manque mémoire. void set_new_handler(void(*)); int main (int argc, char *argv[]) { set_new_handler(&deborde); int nbloc; char c; long taille; int * adresse; cout << "taille du bloc souhaité ?"; cin >> taille; cout << "Réservation de : " << taille * sizeof(int) << " octets par bloc \n"; for(nbloc=1;;nbloc++) { adresse = new int [taille]; cout << "Allocation bloc numero : " << nbloc << "\n"; } return 0; } void deborde (void) { cout <<"Mémoire Insuffisante !\n"; cout <<"Abandon de l'éxécution.\n"; exit (-1); } Résultat taille du bloc souhaité ? 123456789 Réservation de : 493827156 octets par bloc Allocation bloc numero : 1 Allocation bloc numero : 2 Allocation bloc numero : 3 Allocation bloc numero : 4 Allocation bloc numero : 5 Mémoire Insuffisante ! Abandon de l'éxécution. Vous avez remarquez que dans le programme main, il n'est question d'appeler deborde(), cette fois, on commence par appeler la fonction set_new_handler, à laquelle on precise l'adresse d'une fonction (nommee ici deborde). Cette derniere sera appelée automatiquement dès qu'une allocation mémoire aura echoué, de sorte qu'il n'est plus necessaire de prévoir un test à chaque appel de new. Interressant n'est ce pas ? Spécification inline ? En C, nous connaissions deux notions très proches l'une de l'autre qui sont : les macros et les fonctions. Lorsque nous appelions une macro nous faisions suivre leur noms d'une liste d'argument, n'est ce pas, et il en allait de même pour une fonction, cependant : Les instructions correspondant à une macro étaient incorporées dans notre programme, au niveau du préprocesseur, à chaque fois que nous l'appelions. Les instructions correspondant à une fonction étaient générées en une seule fois, par le compilateur sous forme de langage machine, à chaque appel donc, il sera mis en place des instructions nécessaires pour établir la liaison entre le programme et la fonction : Sauvegarde de "l'état courant" Recopie des valeurs des arguments. Branchement avec convervation de l'adresse retour. Recopie de la valeur retour. Restauration de l'état courant. Retour dans le programme. Toute ces instructions nécessaires à la mise en oeuvre d'une fonction n'existe pas pour les macros. Ainsi, les fonctions permettent de gagner de la place mémoire par rapport aux macros, mais on perd en contre-partie un temps relativement supérieur. Par contre, les macros peuvent entraîner des "effets de bords" non_désirés. En C, lorsque vous avez besoin de faire appel à une fonction, et que le temps d'exécution est primordiale, on fait appel aux macros. En C++, on utilisera la spécification inline. Exemple : #include <iostream> using namespace std; inline int suite(int nombre) { int i; int resultat=0; for(i=0; i <=nombre; i++) { cout << " " << i; if(i != nombre) cout<< " +"; else cout<< " = "; resultat = resultat + i; } return resultat; } int main (int argc, char *argv[]) { int n; cout << " Donnez n , pour n! : "; cin >> n; cout << suite(n) << "\n"; return 0; } Résultat : Donnez n , pour n! : 5 0 + 1 + 2 + 3 + 4 + 5 = 15 La fonction suite reçoit un argument de type int, pour générer la suite arithmétique de cette valeur. La présence du mot inline, demande au compilateur de traîter la fonction suite différemment d'une fonction ordinaire. A chaque appel de la fonction, le compilateur devra incorporer au sein du programme en langage machine les instructions correspondantes. Le mécanisme de gestion de l'appel et du retour n'existera plus, ce qui permet de gagner du temps. En revanche, vu qu'il n'y aura plus ce mécanisme de sauvegarde, recopies ... il y aura une consommation de quantité mémoire qui accroissera en fonction des appels de la fonction. Chose très importe, une fonction inline, ne peut pas être compilé séparément, en effet, elle doit être dans le même fichier source. Autre exemple : /*************************************/ /* SIMULATION D'UN SIGNAL SINUSOIDAL */ /*************************************/ #include <iostream> #include <cmath> // utilisation de sinus ligne 18 #define PERIODE 20 // 20 : nombre de point correspondant a une période #define AMPLITUDE 10 using namespace std; // utilisation des fichiers d'en-tête standart. inline void affiche(int n) { for(int i=0;i<n;i++) cout << " "; cout << "*"; } inline int sinus_entier(float x) { int p = (sin(x)+1) * AMPLITUDE; return (p); } /***********************/ /* PROGRAMME PRINCIPAL */ /***********************/ int main(int argc, char **argv) { float n_p; cout << "nombre de période "; // évitez la folie des grandeurs ! 1 pour une T(=période) ! cin >> n_p; for(int i=0;i<(n_p * PERIODE); i++) { affiche(sinus_entier(i/3.0)); cout << "\n"; } return 0; } Resultat * * * * * * * * * * * * * * * * * * * * Bon, vous devinez bien sûr à quoi sert ce programme ... On rabache pas. Et vous allez m'dire, pourquoi se casser la tête quand on peut faire simple : int main(int argc, char **argv) { for(int i=0;i<30; i++) { int p = (sin(i/3.0)+1) * 10; for(int f=0;f<p;f++) cout << " "; cout << "*\n"; } return 0; } lol, comme disait un certain, Mister.Cloper, qui se reconnaîtra, ... " Programme de "Pac-Man", "des foutaises", on verra quand vous programmerez des petits trucs genre, 500 milles lignes ... on verra l'allure du main. Non, non, on ne néglige rien, car tout ce que nous voyons sera d'une grande utilité par la suite. Donc, entraînez-vous à comprendre et à utiliser les fonctions inline. Avantages : Macro : Economie du temps d'éxécution. Fonction : Economie d'espace mémoire, compilation séparée possible. F_Inline : Economie du temps d'exécution. Inconvéniant : Macro : Perte d'espace, risque effets de bord, pas de compilation séparée Fonction : Perte de temps d'éxécution. F_inline : perte de mémoire, pas de compilation séparée. L'Opérateur de cast En C++, comme en C, il nous ait possible d'exercer des conversions explicites à l'aide de l'opérateur de cast, ces conversions comportent évidemment les conversions implicites légales (celles que nous avons précédemment vu), auxquelles s'ajoutent quemques autres qui peuvent être dégradantes ou dépendantes de l'implémentation. const_cast, permet l'ajout ou la suppression à un type, l'un de ces modificateurs tel que : const ou volatile reinterpret_cast, permet la conversion dont le résultat dépend de l'implémentation. Conversion d'entiers vers pointeurs par exemple et vis versas. static_cast, permet la conversions indépendantes de l'implémentation. Conversions de pointeurs vers pointeurs. #include <iostream> using namespace std; int main (int argc, char ** argv) { int nombre = 1; const int * ad1 = &nombre; int * ad2; ad2 = (int *) ad1; // ecrire : ad2 = ad1 serait rejeté. ad2 = const_cast<int*>(ad1); // forme conseillée. ad1 = ad2; //légal ad1 = (const int*) ad2; // ancienne forme ad1 = const_cast <const int *> (ad2); // forme ANSI conseillé const int p = 12; const int * const ad3 = &p; int * ad4; ad4 = (int *) ad3; ad4 = const_cast <int*> (ad3); } L'espace de noms. Lorsque l'on doit utiliser plusieurs bibilothèques dans un programme, on peut être confronté au problème de : " Pollution d'espaces noms", lié à ce qu'un même identificateur peut très bien avoir été utilisé par plusieurs bibliothèques, tout simplement. Ce même problème proviendra lorsque nous developperons des grosses applications, raison pour laquel le C++ ANSI à introduit le concept de l'espace nom, qui s'agit de donner à un "espace" de déclarations, un nom, ... de cette manière : namespace une_biblio { // déclarations usuelles } Et pour se définir à des identificateurs définis dans cet "espace de noms", on utilisera l'instruction using. using namespace une_biblio // ici, les identificateurs de une_biblio seront donc connus. On étudiera plus tard, bien en profondeur ce concept d'espace de noms. Le type Bool Ce type simple est formé de deux valeurs notées true et false, ou 1 et 0. Ce type sert surtout à apporter de la clarté au programme sans pour autant modifier le code. Et je vous laisse regarder la taille du type bool ! FIN DE CE COURS ! LES NEXERCICES héhé, dernière étape avant le cours sur les classes / objets, Ces exercices feront appels bien évidemment aux chapitres précédent : _________________________________________ Exercice I : Input-Output Voyez ce code, #include <stdio.h> #include <conio.h> int main(int argc, char **argv) { int age; char nom[10], prenom[10]; printf("Donnez votre nom, votre prenom et ensuite votre age ?\n"); scanf("%s %s %d", nom, prenom, &age); printf(" Vos Informations,\nNom : %s\nPrenom : %s\nAge : %d\n", nom, prenom, age); getch (); return 0; } } Modifier-le pour ne faire appel qu'aux nouvelles fonctionnalitées input-output, c'est à dire, pas de printf ou de scanf, et bien évidemment, ormis les déclarations dans le main et le return 0; il n' y a que 3 lignes de code, votre nouveau code donc, possédera donc que 3 lignes de code ni plus ni moins. _________________________________________ Exercice II : Transmission d'argument Reprendre le programme de l'exercice I, et créer une fonction permettant de transmettre ces informations et de les afficher : 3 mode de transmission : - Transmission par valeur - Transmission par adresse - Transmission par référence Ces codes seront écrit en C++ ! _________________________________________ Exercice III : Surcharge d'une fonction Soit le code suivant : int fonction (int); // fct I int fonction (float); // fct II void fonction (int, float); // fct III void fonction (float, int); // fct IV int a,b; float c,d ; char e; double z; Ces appels sont-ils correct, si oui, expliquez déterminer la fonction appelée, et le processus d'appel. a.fct(a); b.fct©; c.fct(c,a); d.fct(a,c); e.fct(e); f.fct(a,b); g.fct(b,e); h.fct(a,z); _________________________________________ Exercice IV : Opérateur new. #include <iostream> using namespace std; int main(int argc, char **argv) { char *chaine, i; if((chaine = (char *) malloc(100)) == NULL) { printf("Pas assez de Place memoire\nAbandon ! "); system("pause"); exit(-1); } strcpy(chaine,"Utilisation et gestion de debordement d'allocation mémoire en C\n"); printf("%s", chaine); return 0; } Trouvez, corrigez les erreurs et réécrivez correctement ce code en C++. _________________________________________ Exercice V : Opérateur new. Sur un terminal, nous avons ce résultat suivant : taille du bloc souhaité ? 100000000 Réservation de : 400000000 octets par bloc Allocation bloc numero : 1 Allocation bloc numero : 2 Allocation bloc numero : 3 Allocation bloc numero : 4 Allocation bloc numero : 5 Allocation bloc numero : 6 Allocation bloc numero : 7 Mémoire Insuffisante ! Abandon de l'éxécution ligne : 31 Vous rédigerez un code en C++ générant ce résultat, tout en gérant la gestion de débordement mémoire, et ceci de deux manière : - Avec, new (nothrow). - Avec, set_new_handler. Exercice VI : Fonction inline. a) Transformer ce code, pour que la fonction norme devienne une fonction inline. b) Comment procéder pour que cette fonction soit à présent compilé séparemment. #include <iostream> #include <cmath> using namespace std; double norme(double *); int main (int argc, char *argv[]) { double v1[3], v2[3]; int i; for(i=0;i<3;i++) { v1[i]=i; v2[i]=2*i-1; } cout << "Norme de v1 : " <<norme(v1); cout << "\nNorme de v2 : " << norme(v2) << "\n"; return 0; } double norme(double vec[3]) { int i; double res = 0; for(i=0;i<3;i++) res+= vec[i] * vec[i]; return sqrt(res); }
  12. C-Mos

    Introduction au C++

    Sommaire 2ème partie. Les spécificités du C++ Transmission des arguments en C. Transmission des arguments en C++ par référence. Propriétés Risques indirects Absence de conversion Arguments "effectif constant" & "muet constant" [*]Transmission par référence d'une valeur retour. Le lvalue Valeur de retour et sa constance Notion de référence générale Initialisation de référence Conclusion I - Les spécificités du C++ a) Tran s mi s s ion de s argument s en C. En C, les arguments et la valeur retour d'une fonction, sont toujours transmis par valeur (on pourrait toute fois établir une "simulation de transmission par adresse" à l'aide de pointeur), à l'encontre C++ offre une nouveauté, qui consiste à envoyer ces valeurs par référence. L'intérêt, est que cette fois-ci, le compilateur prendra en charge le transfert d'adresse. Voici quelques récapitulatifs : Transmission des arguments par Valeur en C. (valable en C++) #include <stdio.h> int addition(int, int, int); int main (void) { int a = 1, b = 2, c = 3, resultat; resultat = addition(a, b, c); printf("%d + %d + %d = %d\n", a, b , c , resultat); return 0; } int addition(int m, int n, int o) { return (m+n+o); } Résultat : 1 + 2 + 3 = 6 Transmission par adresse en C. (aussi valable en C++) Nous réutiliserons le même programme que précédemment mais cette fois-ci au lieu d'envoyer les valeurs des arguments, nous enverrons les adresses des arguments, on expliquera les deux cas en détail ultérieurement. #include <stdio.h> int addition(int*, int*, int*); int main (void) { int a = 4, b = 5, c = 6, resultat; resultat = addition(&a, &b, &c); //envoi d'adresse printf("%d + %d + %d = %d\n", a, b , c , resultat); return 0; } int addition(int *m, int *n, int *o) { return (*m+*n+*o); } Résultat : [cmos@localhost test]$ gcc test.c -o test [cmos@localhost test]$ ./test 4 + 5 + 6 = 15 Nous constatons donc, que suivant les deux manières d'envoi d'arguments, (soit par valeur soit par adresse), l'utilisateur doit absolument savoir si il transmet une variable ou l'adresse de la variable, et ceci se justifie par ces déclarations et définitions. En revanche, C++ demandera au compilateur de prendre en charge la transmission des arguments par adresse, il utilisera « les bonnes méthodes » qui nous simplifieront « des jonglettes » habituelles de pointeurs que nous faisions d'en-temps. La transmission par référence que nous allons voir, bien sûr est incompatible en C. b) Tran s mi s s ion de s argument s en C++ par r é f é rence. Voici un exemple : #include <iostream> using namespace std; int addition(int &a, int &b, int &c); int main (void) { int a = 1, b = 2, c = 3, resultat; resultat = addition(a,b,c); cout << a << " + " << b << " + " << c << " = " << resultat << "\n"; return 0; } int addition(int &m, int &n, int &o) { return(m+n+o); } Résultat : [cmos@localhost test]$ g++ test.cpp -o test [cmos@localhost test]$ ./test 1 + 2 + 3 = 6 Explication : Dans l'instruction : int addition(int &m, int &n, int &o);, int &m, signifie que 'm' est de type int transmise par référence, on envoie donc l'adresse de 'm' et non sa valeur (attention) !!! Par contre si nous regardions l'appel de la fonction addition dans le main, resultat = addition(a,b,c); nous constatons que ces arguments semblerait être des valeurs ... Détrompez-vous ! Ce sont bien des adresses, et la seul manière pour un programmeur extérieur de savoir de quel type de transmission s'agit-il, serait de regarder le prototype de la fonction, et sa définition. Au final, nous nous préoccuperons plus du mode de transmission des arguments, le compilateur s'en charge tout seul ! Une seconde chose que nous constatons, survient dans le bloc de définition de la fonction : int addition(int &m, int &n, int &o) { return(m+n+o); } en particulier : return(m+n+o); Effectivement bien que l'on ait reçu les adresses des variables, nous ne feront plus appel à l'opérateur d'indirection *, Oubliez les * et les * ! 1. Propriétés. La transmission par référence d'un argument entraine un certain nombre de conséquences qui n'existait pas dans le cas : - Transmission par Valeur. - Transmission par adresse par le biais de pointeur. 2. Risques indirects. Hormis la simplification d'écriture pour une transmission d'arguments par référence, ce type de transmission peut entrainer le risque "d'effet de bord" non désiré. En effet lorsque l'appel de fonction est établie, l'utilisateur ne sait plus si il transmet la valeur ou l'adresse d'un argument. Il risque donc de modifier une variable alors qu'il pensait n'avoir transmis qu'une copie de la valeur ! 3. Absence de conversion. Un point essentiel, je dirais même plus ... essentiel ! Rappelez-vous que lorsque nous utilisions une transmission par valeur, le compilateur exerçait une vérification (comparaison) "des types" d'arguments, entre ceux de la fonction et ceux qui lui seront transmis. Si il y a différence, alors le compilateur établissait une conversion de type. Exemple : #include <iostream> using namespace std; void fct(char ); int main (void) { int val='x'; cout << "Taille de val' avant envoi: " << sizeof(val) << "\n"; fct (val); cout << "Taille de 'val' apres : " << sizeof(val) << "\n"; return 0; } void fct(char m) { cout << "valeur recu: " << m << "\n"; cout << "Taille de 'val' dans fonction: " << sizeof(m) << "\n"; } Résultat : Taille de 'val' avant envoi : 4 valeur recu : x Taille de 'val' dans fonction : 1 Taille de 'val' apres : 4 Le résultat parle de lui-même. Faisons à présent un exemple d'une transmission par référence, et des possibilités de conversions correspondant .. si il y en a. int fct (int &) // la fct reçoit la référence à un entier double x; ... fct(x); Dès lors que la fonction reçoit l'adresse d'un emplacement qu'elle considère comme contenant un entier, qu'elle peut éventuellement modifier, il va de soi qu'il n'est plus possible d'effectuer une quelconque conversion de la valeur qui s'y trouve. fct(x); //appel illégal La transmission par référence impose donc à un argument effectif d'être une Lvalue du type prévu pour l'argument muet. Mais que se passe-t-il si à la place de :fct(x);, nous mettions : fct(&x);, étant donné que nous envoyons une adresse ? Et bien tout simplement, cette appel-çi sera rejeté ! car même si au bout du compte nous envoyons une adresse, la référence n'est pas un pointeur, et l'usage qui est fait dans la traduction des instructions de la fonction n'est pas le même. Dans le cas d'un pointeur on utilise directement sa valeur, quitte à mentionner une indirection avec l'opérateur *; avec une référence, l'indirection est ajouté automatiquement. C'est clair pour tout le monde ! ... Respirez, ... On continue ! 4. Arguments « effectif constant » & « muet constant » Arguments « effectif constant » Supposons que la fonction ait pour prototype : void fct(int &); Le compilateur refusera alors un appel sous cette forme : fct(3); // incorrect : f ne peut pas modifier une constante. ou encore : const int val=123; ... fct(val); // incorrect : f ne peut pas modifier une constance. Pour quelle raison ? Et bien je vous réponds par une autre question : Peut-on modifier une constante au cours d'un programme ? Bien non, si les appels auraient été acceptés, alors ils fourniraient à la fonction les adresses des constantes 3 ou val, et elle pourrait très bien en modifier les valeurs. Arguments « muet constant » Par contre nous allons considérer ce prototype : void fct (const int &); // const int & correspond à une référence à une constante.Les appels suivant seront donc corrects : const int var = 15; ... fct(3); //appel correct fct(var); // appel correct L'acceptation de ces instructions se justifie ici, par le fait que fct a prévu de recevoir une référence à quelque chose de constant. De plus un appel de fonction : fct(expression), sera accepté quelque soit le type de (expression). En effet dans ce cas : [code ] void fct(const int &); float var; ... fct(var); // correct [/code] Il y a ici une création d'une variable temporaire (de type int) qui recevra le résultat de la conversion de expression en int. En définitif, l'utilisation de const pour argument muet transmis par référence, est lourde de conséquence, car il force le compilateur vérifier la constance de l'argument concerné, au sein de la fonction, et de plus, nous autorisons la création d'une copie de l'argument effectif précédé d'une conversion dés lors que ce dernier est constant et de type différent de celui attendu. c) Transmission par référence d'une valeur de retour 1. Introduction Exemple : int & fct() {... return n; } Ici, l'appel de fct provoquera un retour non pas de la valeur de 'n', mais de la référence de 'n', il peut être utilisé aussi de façon plus usuelle : int var; ... var = f(); Ici, on affectera à 'var', la valeur situé à la référence fournie par 'fct'. 1.c.1 ) On obtient une Lvalue Dès lors qu'une fonction renvoie une référence, il nous sera possible d'utiliser son appel comme une Lvalue. Par exemple : int & fct(); int x; float y; ... /* à la référence fournie par fct, on range la valeur de l'expression */ f() = 2*x+5; /* à la référence fournie par fct, on range la valeur de y, après conversion en int.*/ f() = y; Pour le moment nous n'y voyons pas grand intérêt, mais nous en verrons le but principal lors de l'étude de la surdéfinition d'opérateur. 2. Valeur de retour et sa constance Si une focntion prévoit dans son en-tête une retour par référence, elle ne pourra pas mentionner dans l'instruction return une constante. Sinon, on prendrait le rique de modifier la valeur en question : Illustration : int x = 2; // variable global float y = 3.456; // variable global int & fct(...) { ... return 5; // interdit return x; // ok return y; // interdit } Si par contre l'en-tête mentionne une référence à une constante, il y aura exception, on renverra donc la référence d'une copie de cette constante, précédée d'une éventuelle conversion : const int & fct_2() { return 5; // ok : On renvoie la référence à une copie temporaire return x; // ok return y; /* ok : On renvoie la référence à un int temporaire obtenue par conversion de la valeur de y */ } On notera qu'une telle référence à une constante ne pourra plus être utilisé comme une Lvalue : const int & fct(); int x; float y; ... fct() = 2 * x + 5; // erreur : fct(), n est pas une Lvalue f() = y; // erreur : fct(), n est pas une Lvalue 3. Notion de référence générale. De manière générale, on peut déclarer un identificateur comme référence d'une autre variable. #include <iostream> using namespace std; int main (void) { int x=3; int &y = x; cout << "adresse x: "<< &x <<" valeur : " << x <<"\n"; cout << "adresse y: "<< &y <<" valeur : " << y <<"\n"; return 0; } Dans ce cas, 'y' est une référence à la variable x, ainsi, x et y designerons le même emplacement mémoire. Ce que nous constatons : Le résultat : adresse x: 0xbf93d66c valeur : 3 adresse y: 0xbf93d66c valeur : 3 4. Initialisation de référence. Quand nous ecrivons ceci : int & x = y; Nous déclarons une référence 'x', accompagnée d'une initialisation à la référence de 'y'. Attention, il n'est pas possible de déclarer une référence sans l'initialiser : int & x ; // Incorrect. Ou encore, d'initialiser une référence par une constante. int & x = 15 ; // Incorrect Maintenant je vous pose ceci : int y = 3; int & x = y; y = z; // ou y = 10; Que se passe-t-il ? Premièrement, nous avons déclarer une variable de type int de valeur 3, puis une référence 'x' initialiser à la référence y, et secondo, il s'agit obligatoirement d'une affectation à l'emplacement de référence y, et non une modification à la référence z ... vous suivez ??? Une référence une fois déclarée et initialisée, il n'est plus possible de la modifier, PLUS POSSIBLE ! donc le compilateur considérera : y = z; comme une affectation, ... automatiquement. Exemple : #include <iostream> using namespace std; int main (void) { int x=3; int &y=x; cout << "adresse x: "<< &x <<" valeur : " << x <<"\n"; cout << "adresse y: "<< &y <<" valeur : " << y <<"\n"; y = 5; cout << "y = 5" << "\n"; cout << "adresse y: "<< &y <<" valeur : " << y <<"\n"; return 0; } Résultat : adresse x: 0xbfe8b96c valeur : 3 adresse y: 0xbfe8b96c valeur : 3 y = 5 adresse : 0xbfe8b96c valeur : 5 Nous voyons explicitement que le compilateur à effectuer une affectation de valeur 5 à l'emplacement de référence y. y = 5 adresse y: 0xbfe8b96c valeur : 5 Compris tout ça ! Par contre, il est possible de définir des références constantes qui peuvent alors être initialiser par des constantes : int & x = 15 ; // Incorrect const int &x = 15 ; // Correct Ceci génére une variable temporaire contenant la valeur 15, et place sa référence dans x, nous aurions l'écrire de cette manière : int temp = 15; int &x = temp; mais avec const int &x = 15 ;, on n'aura pas accès explicitement à la variable temporaire. Et enfin, float x; const int &y = x; Ces déclaration sont corrects, il y aura création d'une variable temporaire contenant le résultat de la conversion de 'x' en int et placement de sa référence dans 'y' . float x; int temp = x; const int &y = temp; 5. conclusion L'Appel d'une fonction conduit donc à une initialisation des arguments muets. Dans le cas d'une référence, ce sont donc les règles que nous avons décrites qui sont tout aussi utilisées. Il en va de même pour une valeur retour. Voilà, ... pour le moment ... la suite et la fin du chapitre portera sur : Surdéfinitions de fonctions Opérateur : new et delete Fonctions inline Le très attendu opérateur cast le type bool Les notions d'espace
  13. C-Mos

    Introduction au C++

    Correction de l'exercice précédent : La première erreur que révèlera le compilateur C++, sera pour la fonction fct(). En effet, elle devra faire obligatoirement l'objet d'une déclaration sous forme prototype. que vous placerez n'importe ou avant son appel. int fct(int, int ); ou encore int fct(int var1, int var2); Les noms var1 et var2 sont totalement fictifs : ils n'ont aucun absolument aucun rôle dans la suite du programme, et n'interfèreront nullement sur d'autres variables de même type. La seconde erreur se portera sur la fonction "printf", qui devra faire l'objet d'un prototype. C++ étant incapable de distinguer les fonctions de l'utilisateur et celles des bibliothèques. #include <stdio.h> ou comme nous l'avons dit précédemment, avec le préfixe 'c' : #include <cstdio> Voilà, vous avez je pense tous réussi à résoudre ce petit exercice des plus basique. donc, on passe à : III - Les Entrées-Sorties conversationnelles du C++ a) G é n é ralit é s Nous allons voir dans ce chapitre les nouvelles disponibilités offertes du C++ pour les entrées-sorties conversationnelles. La lecture sur l'entrée standard -> Clavier. La lecture sur la sortie standard -> Écran. Les fonctions et macros de la bibliothèque C ANSI sont réutilisables en C++, en particulier celles des input-ouput, raison pour laquelle nos premiers programmes fait précédemment en C++ n'ont rencontré aucun problème de compilation lors de l'utilisation des flux d'entrées sorties ! Chose nouvelle pour le C++, c'est qu'à présent, nous allons préfixer les fichiers d'en-tête habituels, par la lettre « c », ainsi le fichier : « stdio.h » deviendra : « cstdio » et le « .h» disparaitra. Les principales caractéristiques du C++ en ce qui concerne les Input-Output sont : Simplicité d'utilisation : Fini le temps des « printf - scanf ». Extensibilité des types que l'ont définira sous forme de classes. b) Affichage Habituellement en C, nous écrivions : printf("Bonjour"); en C++, nous utiliserons : cout << "Bonjour"; On expliquera plus tard, l'interprétation détaillée de cette instruction, pour le moment il nous sera simplement utile de savoir que "cout" : Désigne un flot de sortie associé à (stdout) << est un opérateur : [opérande gauche] << [opérande droite] l'opérande de gauche est un flot, ici cout ! l'opérande de droite est une expression de type quelconque. On peut donc traduire : cout << "Bonjour"; par : le flot cout reçoit la valeur "Bonjour". 1. Le fichier en-tête « iostream ». Nous savons qu'en C, pour utiliser les flots d'entrées-sorties standard, il fallait incorporer le fichier d'en-tête ''stdio.h'', en C++, il faudra le remplacer par : ''iostream'', et pas seulement ! L'utilisation des symboles introduit dans ce fichier fait appel à la notion « d'espace de noms », Cette notion oblige d'introduire dans le programme une instruction de déclaration using, qui se présente ainsi : using namespace std; Pour certain compilateur, l'utilisation de iostream sans la déclaration de using namespace std pourrait entrainer une certaine erreur. 2. Lecture au clavier. Le flot d'entrée C++ associé à l'entrée standard du C (stdin), est cin. De la même manière que l'opérande << qui permet d'envoyer des informations sur le flot de sortie cout, l'opérande >> permet de recevoir de l'information. Voici un petit programme d'une lecture de suite de caractères, illustrant le défaut majeur de celle ci. #include <iostream> using namespace std; int main (void) { char car[129]; int i = 0; cout << "Donnez une suite de caractères terminée par un point : \n"; do cin >> car[i]; while (car[i++] != '.'); cout << "\n Caractères effectifs lus : \n"; i=0; do cout << car[i]; while (car[i++] != '.'); return 0; } Voilà, je compile/exécute ce bout de programme, et je met des sauts de ligne délibérés : [C-Mos@localhost test]$ g++ test.cpp -o test [C-Mos@localhost test]$ ./test Donnez une suite de caractères terminée par un point : Voyez comme C++ pose quelques soucis lors de la lecture d'une "suite de caractères" . Caracteres effectifs lus : VoyezcommeC++posequelquessoucislorsdelalectured'une"suitedecaractères". [C-Mos@localhost test]$ On constate que C++ à quelques difficultés pour gérer les séparateurs. Voyons à présent que se passe-t-il si par exemple nous exécuterions une boucle sur un caractère invalide !? Soit le programme suivant : [C-Mos@localhost test]$ cat test.cpp #include <iostream> using namespace std; int main (void) { int nbre; do { cout << "Donnez un nombre de type entier : "; cin >> nbre; cout << "voici son cube : " << nbre*nbre*nbre << "\n"; } while (nbre); return 0; } On l'exécute, et nous allons voir comment C++ va gérer cela. [C-Mos@localhost test]$ g++ test.cpp -o test [C-Mos@localhost test]$ ./test Donnez un nombre de type entier : 2 voici son cube : 8 Donnez un nombre de type entier : 3 voici son cube : 27 Donnez un nombre de type entier : p Donnez un nombre de type entier :voici son cube : 27 Donnez un nombre de type entier :voici son cube : 27 Donnez un nombre de type entier :voici son cube : 27 Voilà, le compilateur s'emballe, et boucle infiniment, vous interromprez donc l'exécution du programme suivant la démarche appropriée à votre IDE, si sur une IDE vous êtes. Dans mon cas, CTRL+C, qui émettra le signal SIGINT à tous les processus associés au terminal respectif. 3. Le synchronisme. Nous allons dans ce petit paragraphe illustrer la présence d'un tampon, s'apercevoir qu'une seule lecture peut utiliser une information non exploitée par la précédente. [C-Mos@localhost test]$ cat test.cpp #include <iostream> using namespace std; int main (void) { int nbre, nbre2; cout << "Donnez un nombre de type entier : "; cin >> nbre; cout << "valeur : " << nbre << " récupérée"<< "\n"; cout << "Donnez un nombre de type entier : "; cin >> nbre2; cout << "valeur : " << nbre2 << " récupérée"<< "\n"; return 0; } Compilation : [C-Mos@localhost test]$ ./test Donnez un nombre de type entier : 3 4 valeur : 3 récupérée Donnez un nombre de type entier : valeur : 4 récupérée Voilà, c'est la fin de la première partie Introduction au C++, c'était pas très difficile, c'est juste des révisions. La suite du chapitre portera donc sur : Les notions de référence Arguments par défaut dans les déclarations des fonctions. Surdéfinition de fonctions Opérateur : new et delete Fonctions inline Le très attendu opérateur cast le type bool Les notions d'espace Et tout plein d'exercices. La quantité bien sûr sera relative à la difficulté des leçons. Voilà, Vous vous attendiez à quoi ? A voir le Calcul Vectoriel, les Algorithmes de Fusion, le conteneur multimap ... ben non ! pour cela, il faudra être très attentif au début des cours, et après nous les aborderons avec facilité. En attendant, pourquoi ne pas essayer de programmer ces fonctions en C, et ensuite, de les faire évoluer en C++. la fonction : max(x,y) -> qui retourne la valeur maximum de x et y; la fonction : min (x,y) -> qui retourne la valeur minimum de x et y; la fonction : hypot(x,y) -> qui retourne l'hypoténuse d'un triangle rectangle avec x, et y, les cotés de l'angle droit. Histoire de garder la forme. @bientôt .
  14. C-Mos

    Introduction au C++

    II - LES DIFFÉRENCES ENTRE LE C++ ET LE C (Suite). c) Incompatibilit é s ur le s argument s et valeur de retour. 1 - Point communs entre C et C++. En C++ tout comme en C ANSI, les arguments d'une fonction ainsi que la valeur retour peuvent : être scalaire, d'un type de base (char, int, float, *pointeur) être une valeur de type structurée. ... 2 - Différences entre C et C++. Les différences comme nous les avons vu, ne portent que sur la syntaxe des en-têtes & prototypes des fonctions, et ceci sur deux cas bien spécifique : Fonction sans arguments. En C ANSI on peut employer void pour définir (en-tête) ou déclarer (prototype) une fonction sans argument, en C++, on ne met rien(liste vide). En C : float fct (void); En C++ : float fct ( ); Fonction sans valeur retour. Alors qu'en C ANSI, on serait amené à employer le mot void, pour le type de retour, en C++, on doit obligatoirement le mettre, sinon, ne mettre qu'une liste vide, amènerait le compilateur à considérer que la fonction renverrait un int. void fct ( int ); //en C et C++ renvoie un void. Par contre mettre ceci : fct ( int ); // renvoie un void en C, et un int en C++, si toute fois, votre compilateur veuille bien le compiler ! On pourrait croire à priori, que ceci est totalement fictif, voir sans importance, que ce soit un int, ou un void, puisque nous ne la réutiliserions pas, après tout, on s'en fiche ! Et bien permettez-moi, de rependre une très belle parole, piochée, je ne sais pas trop où : En Vertu du principe de l'économie des moyens, un bon programme ne se contente pas que de marcher ; il marche, tout en évitant de gaspiller les ressources du système. L'Abus de variables, ou de variables surdimensionnées peut entrainer un ralentissement notable du système, voir un plantage pur et simple. (avec une touche personnelle !:°) Imaginons un instant, un programme en C++, de 200.000 lignes (non, non c'est rien de code, et ayant plus de 30.000 fonctions (imaginons ... ), qui sont sensées ne rien renvoyer « à la base. » Déclarées dans ce genre là : fct(int, double, double ); // par exemple. ? En C++, noté ainsi, nous avions dit, que quand le compilateur ne voyait rien (liste vide) pour « type de valeur retour », il considèrerait que cette valeur retour est un int. Donc dans ce cas-ci, il va réserver un emplacement mémoire de la taille d'un int pour un type retour ... et tout ça, tout seul !!! Un int, est codé sur combien d'octet ? (ohhh, la blague eh, c'est quoi cette question ? ). Vous me dites 4 ( et que cela dépend des systèmes), mais on va supposer 4 octets. Ok, donc, 30.000 x 4 = 120.000 octets de réservation mémoire qui ne servent à rien ! Vous voyez l'inattention à quoi ca amène ! En C++ lors d'une déclaration, on met tout ! Type de valeur renvoyée, type des arguments, nom de la fonction ... on laisse rien au hazard. L'assiduité, on ne le dira jamais assez. d) Compatibilit é entre le type void* et le s autre s pointeur s Le type générique void* est compatible avec tous les autres types de pointeurs en C ANSI, et ceci dans les deux sens. Si je déclare ceci : void * x; int * y; et j'affecte de cette manière : x = y; y = x; elles feront intervenir les conversions implicites a savoir int * -> void * //pour la première void * -> int * // pour la seconde En C++ seul la conversion d'un pointeur quelconque en void peut être implicite. Par conclusion seule l'affectation : int * -> void * soit : x = y; Autrement il faudra faire intervenir l'opérateur cast, pour forcer la conversion d'un type de pointeur void, en un tout autre type, la conversion sera donc faite explicitement. Le qualificatif « const » Le qualificatif const permet de spécifier qu'un symbole correspond à « quelque chose » dont la valeur ne doit pas être changer. Lorsque la portée s'applique à des variables locales, il n'y pas de différence entre le C et le C++, la fonction étant limitée par au bloc ou à la fonction concernée par la déclaration. Mais si, le qualificatif s'applique à des variables globales, C++ limitera donc la portée du symbole au fichier, comme si il avait reçu l'attribut « static ». Ainsi il devint beaucoup plus aisé de remplacer certaines instructions #define par des déclarations de constantes. En C, nous faisions : fichier1 #define VA 8 ... fichier2 #define VA 5 ... en C++ on fera : fichier1 const int VA = 8; ... fichier2 const int VA = 5; ... De cette manière, si nous aurions compilé en C, nous aurions eu une erreur au niveau de l'édition de lien, pour éviter cela, il aurait fallu déclarer VA « en static » dans au moins un des fichier ou les deux. De cette manière : fichier1 static const int VA = 8; ... fichier2 static const int VA = 5; ... Voilà. Ce chapitre est terminé ! Pas très long, mais cela suffira à éviter quelques erreurs d'inattention qui vous feront peut être moins perdre de temps à l'avenir. Que diriez-vous d'un peu d'exercice, histoire de stimuler les neurones ! Voici ce programme qui peut très bien être compilé en C, toute fois avec le compilateur C++ il fournira pas mal d'erreurs, interprétez donc les erreurs possibles du compilateur tout en les expliquant. main () { int x, y, resultat; printf("soit (a+b)²\n"); printf("donnez a ? "); scanf("%d", &x); printf("donnez b ? "); scanf("%d", &y); resultat = fct(x,y); printf("Le resultat de (%d+%d)² est : %d\n",x,y,resultat); } fct(int x, int y) { return (x*x + 2*x*y + y*y); } Correction au prochain cours : " Input-Output conversationnelles ", ... alors, à vos copies !
×
×
  • Créer...