Cours C++

Chapitre 1. Introduction

   Le C++ est l'un des langages de programmation les plus utilisés actuellement. Il est à la fois facile à utiliser et très efficace. Il souffre cependant de la réputation d'être compliqué et illisible. Cette réputation est en partie justifiée. La complexité du langage est inévitable lorsque l'on cherche à avoir beaucoup de fonctionnalités. Quant à la lisibilité des programmes, elle dépend essentiellement de la bonne volonté du programmeur.
   Les caractéristiques du C++ en font un langage idéal pour certains types de projets. Il est incontournable dans la réalisation des grands programmes. Les performances des compilateurs actuels en font également un langage de prédilection pour ceux qui recherchent les performances. Enfin, ce langage est, avec le C, idéal pour ceux qui doivent assurer la portabilité de leurs programmes au niveau des fichiers sources (pas des exécutables).
  Les principaux avantages du C++ sont les suivants :
  • grand nombre de fonctionnalités ;
  • performances du C ;
  • facilité d'utilisation des langages objets ;
  • portabilité des fichiers sources ;
  • facilité de conversion des programmes C en C++ ;
  • contrôle d'erreurs accru.

   On dispose donc de quasiment tout : puissance, fonctionnalité, portabilité et sûreté. La richesse du contrôle d'erreurs du langage, basé sur un typage très fort, permet de signaler un grand nombre d'erreurs à la compilation. Toutes ces erreurs sont autant d'erreurs que le programme ne fait pas à l'exécution. Le C++ peut donc être considéré comme un « super C ». Le revers de la médaille est que les programmes C ne se compilent pas directement en C++ : quelques adaptations sont nécessaires. Cependant, celles-ci sont minimes, puisque les syntaxes du C++ est basée sur celle du C. On remarquera que tous les programmes C peuvent être réécrits pour compiler à la fois en C et en C++.
   Ce document est organisé en deux grandes parties. La première partie (chapitres 2 à 7) traite des fonctionnalités communes au C et au C++, en différenciant bien celles qui ne sont disponibles qu'en C++. Cette partie présente essentiellement la syntaxe de base du C et du C++. La deuxième partie (chapitres 8 à 12) ne traite que du C++. Les sujets suivants y sont traités : programmation orientée objet, exceptions, identification dynamique des types, template et régions déclaratives.
   Le C présenté dans la première partie est compilable en C++. Ceci signifie qu'il n'utilise pas certaines fonctionnalités douteuses du C. Ceux qui désirent utiliser la première partie comme un cours de C doivent donc savoir qu'il s'agit d'une version épurée de ce langage. Les appels de fonctions non déclarées ou les appels de fonctions avec trop de paramètres ne sont donc pas considérées comme des pratiques de programmation valables dans ce cours.
   En ce qui concerne la syntaxe, elle sera donnée sauf exception avec la convention suivante : ce qui est entre crochets ('[' et ']') est facultatif. De plus, quand plusieurs éléments de syntaxe sont séparés par une barre verticale ('|'), l'un de ces éléments seulement doit être présent (c'est un ou exclusif). Enfin, les points de suspension désigneront une itération éventuelle du motif précédent.
  Par exemple, si la syntaxe d'une commande est la suivante :
  [fac|rty|sss] zer[(kfl[,kfl[…]])];
les combinaisons suivantes seront syntaxiquement correctes :
   zer;
   fac zer;
   rty zer;
   zer(kfl);
   sss zer(kfl,kfl,kfl,kfl);
mais la combinaison suivante sera incorrecte :
fac sss zer()

pour les raisons suivantes :
  1. Fac et sss sont mutuellement exclusifs, bien que facultatifs tous les deux ;
  2. Au moins un kfl est nécessaire si les parenthèses sont mises ;
  3. Il manque le point virgule finale.

  Rassurez-vous, il n'y aura pratiquement jamais de syntaxe aussi compliquée. Je suis sincèrement désolé de la complexité de cet exemple
Chapitre 2. Première approche du C++


    Le C/C++ est un langage procédural, du même type que le Pascal par exemple. Cela signifie que les instructions sont exécutées linéairement et regroupées en blocs : les fonctions et les procédures (les procédures n'existent pas en C/C++, ce sont des fonctions qui ne retournent pas de valeur).
    Tout programme a pour but d'effectuer des opérations sur des données. La structure fondamentale est donc la suivante :
ENTRÉE DES DONNÉES
(clavier, souris, fichier, autres périphériques)
|
TRAITEMENT DES DONNÉES
|
SORTIE DES DONNÉES
(écran, imprimante, fichier, autres périphériques)

    Ces diverses étapes peuvent être dispersées dans le programme. Par exemple, les entrées peuvent se trouver dans le programme même (l'utilisateur n'a dans ce cas pas besoin de les saisir). Notez que ce processus peut être répété autant de fois que nécessaire pendant l'exécution d'un programme. Par exemple, les programmes graphiques traitent les événements système et graphiques au fur et à mesure qu'ils apparaissent. Les données qu'ils reçoivent sont fournies par le système sont couramment appelés des messages, et la boucle de traitement de ces données la boucle des messages.
   Les données sont stockées dans des variables, c'est à dire des zones de la mémoire. Comme leur nom l'indique, les variables peuvent être modifiées (par le traitement des données). Des opérations peuvent être effectuées sur les variables, mais pas n'importe lesquelles. Par exemple, on ne peut pas ajouter des pommes à et des bananes, sauf à définir cette opération bien précisément. Les opérations dépendent donc de la nature des variables. Afin de réduire les risques d'erreurs de programmation, les langages comme le C/C++ donnent un type à chaque variable (par exemple : pomme et banane). Lors de la compilation (phase de traduction du texte source du programme en exécutable), ces types sont utilisés pour vérifier si les opérations effectuées sont autorisées. Le programmeur peut évidemment définir ses propres types.
    Le langage fournit des types de base et des opérations prédéfinies sur ces types. Les opérations qui peuvent être faites sont soit l'application d'un opérateur, soit l'application d'une fonction sur les variables. Logiquement parlant, il n'y a pas de différence.

Seule la syntaxe change :
    a=2+3
  est strictement équivalent à :
    a=ajoute(2,3)
    Évidemment, des fonctions utilisateur peuvent être définies. Les opérateurs ne peuvent être que redéfinis : il est impossible d'en définir de nouveaux (de plus, la redéfinition des opérateurs n'est faisable qu'en C++).
Cette première partie est donc consacrée à la définition des types, la déclaration des variables, la construction et l'appel de fonctions, et aux entrées-sorties de base (clavier et écran).
2.1. Les commentaires en C++
Les commentaires sont nécessaires et très simples à faire. Tout programme doit être commenté. Attention cependant, trop de commentaires tue le commentaire, parce que les choses importantes sont noyées dans les banalités.
Il existe deux types de commentaires en C++ : les commentaires de type C et les commentaires de fin de ligne (qui ne sont disponibles qu'en C++).
Les commentaires C commencent avec la séquence barre oblique - étoile. Les commentaires se terminent avec la séquence inverse : une étoile suivie d'une barre oblique.
   Exemple 2-1. Commentaire C
/* Ceci est un commentaire C */
Ces commentaires peuvent s'étendre sur plusieurs lignes.
En revanche, les commentaires de fin de lignes s'arrêtent à la fin de la ligne courante, et pas avant. Ils permettent de commenter plus facilement les actions effectuées sur la ligne courante, avant le commentaire. Les commentaires de fin de ligne commencent par la séquence constituée de deux barres obliques (ils n'ont pas de séquence de terminaison, puisqu'ils ne se terminent qu'à la fin de la ligne courante). Par exemple :
   Exemple 2-2. Commentaire C++
  action quelconque       // Ceci est un commentaire C++
action suivante
Dans la suite, les programmes d'exemples utiliseront les deux formes de commentaires pour des raisons de clarté. Si les programmes doivent être compilés avec un compilateur C, il faut supprimer les commentaires spécifiques C++.

2.2. Les types prédéfinis du C/C++
Il y a plusieurs types prédéfinis. Ce sont :
  • le type vide : void. Ce type est utilisé pour spécifier le fait qu'il n'y a pas de type. Ceci a une utilité pour faire des procédures (fonctions ne renvoyant rien) et les pointeurs sur des données non typées (voir plus loin) ;
  • les booléens : bool, qui peuvent prendre les valeurs true et false (en C++ uniquement, ils n'existent pas en C) ;
  • les caractères : char ;
  • les caractères longs : wchar_t (en C++ seulement, ils n'existent pas en C) ;
  • les entiers : int ;
  • les réels : float ;
  • les réels en double précision : double ;
  • les tableaux à une dimension, dont les indices sont spécifiés par des crochets ('[' et ']'). Pour les tableaux de dimension supérieure ou égale à 2, on utilisera des tableaux de tableaux ;
  • les structures, unions et énumérations (voir plus loin). Les types entiers (int) peuvent être caractérisés d'un des mots clé long ou short. Ces mots clé permettent de modifier la taille du type, c'est à dire la plage de valeurs qu'ils peuvent couvrir. De même, les réels en double précision peuvent être qualifiés du mot-clé long, ce qui augmente leur plage de valeurs. On ne peut pas utiliser le mot-clé short avec les double. On dispose donc de types additionnels :
  • les entiers longs : long int, ou long (int est facultatif) ;
  • les entiers courts : short int, ou short ;
  • les réels en quadruple précision : long double.

La taille des types n'est spécifiée dans aucune norme, sauf pour le type char. En revanche, les inégalités suivantes sont toujours vérifiées :
  char & #61603; short int & #61603; int & #61603; long int
  float & #61603; double & #61603; long double
où l'opérateur « <= » signifie ici « a une plage de valeur plus petite ou égale que ». La taille des caractères de type char est toujours de un octet. Les types char et int peuvent être signés ou non. Un nombre signé peut être négatif, pas un nombre non signé. Lorsqu'un nombre est signé, la valeur absolue du plus grand nombre représentable est plus petite. Par défaut, un nombre est signé (sauf les type char et wchar_t, qui peuvent être soit signés, soit non signés, selon le compilateur utilisé). Pour préciser qu'un nombre n'est pas signé, il faut utiliser le mot-clé unsigned. Pour préciser qu'un nombre est signé, on peut utiliser le mot-clé signed. Ces mots-clés peuvent être intervertis librement avec les mots clé long et short.
   Exemple 2-3. Types signés et non signés
   unsigned char
signed char
   unsigned int
   signed int
   unsigned long int
   long unsigned int
   Les valeurs accessibles avec les nombres signés ne sont pas les mêmes que celles accessibles avec les nombres non signés. En effet, un bit est utilisé pour le signe dans les nombres signés. Par exemple, puisque le type char est codé sur 8 bits, on peut coder les nombres allant de 0 à 256 avec ce type en non signé (il y a 8 chiffres binaires, chacun peut valoir 0 ou 1, on a donc 2 puissance 8 combinaisons possibles, ce qui fait 256). En signé, les valeurs s'étendent de -128 à 127 (un des chiffres binaires est utilisé pour le signe, il en reste 7 pour coder le nombre, donc il reste 128 possibilités dans les positifs comme dans les négatifs. 0 est considéré comme positif. En tout, il y a autant de possibilités.).
  Le type int doit être capable de représenter les entiers utilisés par la machine sur laquelle le programme tournera. Par exemple, sur les machines 16 bits ils sont codés sur 16 bits (les valeurs accessibles vont donc de -32768 à 32768, ou de 0 à 65535 si l'entier n'est pas signé). C'est le cas sur les PC en mode réel (c'est à dire sous DOS) et sous Windows 3.x. Sur les machines fonctionnant en 32 bits, le type int est stocké sur 32 bits : l'espace des valeurs disponibles est donc 65536 fois plus large. C'est le cas sur les PC en mode protégé 32 bits (Windows 95 ou NT, DOS Extender, Linux), sur la plupart des machines UNIX et sur les Macintosh. Sur les machines 64 bits, le type int est 64 bits (DEC Alpha par exemple). On constate donc que la portabilité des types de base est très aléatoire. En pratique cependant, ils ont souvent la même taille pour toutes les machines 32 bits (la majorité). Sur ces machines, les entiers longs sont codés la plupart du temps sur 32 bits et les entiers courts sur 16 bits. Les caractères sont souvent codés sur 8 bits. Le type wchar_t est équivalent à l'un des types entiers, il est souvent codé sur 16 bits. Enfin, le type float est généralement codé sur 4 octets et les types double et long double sont identiques et codés sur 8 octets.
  Le C++ (et le C++ uniquement) considère le type char comme le type debase des caractères. Les caractères n'ont pas de notion de signe associée. Cependant, les caractères peuvent être considérés comme des entiers à tout instant, mais il n'est pas précisé si ce type est signé ou non. Ceci dépend du compilateur. L'interprétation du type char en tant que type intégral n'est pas le comportement de base du C++, par conséquent, le langage distingue les versions signées et non signées de ce type de la version dont le signe n'est pas spécifié. Ceci signifie que le compilateur traite les types char, unsigned char et signed char comme des types différents. Cette distinction n'a pas lieu d'être au niveau des plages de valeurs si l'on connaît le signe du type char, mais elle est très importante dans la détermination de la signature des fonctions (la signature des fonctions sera définie plus loin dans ce cours).
   Si l'on veut faire du code portable (c'est à dire qui compilera et fonctionnera sans modification du programme sur tous les ordinateurs), il faut utiliser des types de données qui donneront les mêmes intervalles de valeurs sur tous les ordinateurs. Il est donc recommandé de définir ses propres types (par exemple int8, int16, int32) dont la taille et le signe seront fixe. Lorsque le programme devra être porté, seule la définition de ces types sera à changer, pas le programme. En pratique, si l'on veut faire du code portable entre les machines 16 bits et les machines 32 bits, on ne devra pas utiliser le type int seul : il faudra toujours indiquer la taille de l'entier utilisé : short (16 bits) ou long (32 bits).
2.3. Notation des nombres
   Les entiers se notent de la manière suivante :
  • base 10 (décimale) : avec les chiffres de '0' à '9', et les signes '+' (facultatif) et '-'.

       Exemple 2-4. Notation des entiers en base 10
    12354, -2564
  • base 16 (hexadécimale) : avec les chiffres '0' à '9' et 'A' à 'F' ou a à f (A=a=10, B=b=11, … F=f=15). Les entiers notés en hexadécimal devront toujours être précédés de « 0x » (qui indique la base). On ne peut pas utiliser le signe '-' avec les nombres hexadécimaux.    Exemple 2-5. Notation des entiers en base 16
    0x1AE
  • base 8 (octale) : avec les chiffres de '0' à '7'. Les nombres octaux doivent être précédés d'un 0 (qui indique la base). Le signe '-' ne peut pas être utilisé.    Exemple 2-6. Notation des entiers en base 8
    01, 0154

   Les flottants (pseudo réels) se notent de la manière suivante : [signe] chiffres [.[chiffres]][e|E [signe] exposant] où signe indique le signe. On emploie les signes '+' (facultatif) et '-' aussi bien pour la mantisse que pour l'exposant. 'e' ou 'E'permet de donner l'exposant du nombre flottant. L'exposant est facultatif. Si on ne donne pas d'exposant, on doit donner des chiffres derrière la virgule avec un point et ces chiffres. Les chiffres après la virgule sont facultatifs, mais pas le point. Si on ne met ni le point, ni la mantisse, le nombre est un entier décimal.
   Exemple 2-7. Notation des réels

-123.56, 12e-12, 2
« 2 » est entier, « 2. » est réel.
Les caractères se notent entre guillemets simples :
'A', 'c', '('
   On peut donner un caractère non accessible au clavier en donnant son code en octal, précédé du caractère '\'. Par exemple, le caractère 'A' peut aussi être noté '\0101'. Attention à ne pas oublier le 0 du nombre octal. Il est aussi possible d'utiliser certains caractères spéciaux, dont les principaux sont :
    '\a'        Bip sonore
    '\b'        Backspace
    '\f'        Début de page suivante
    '\r'        Retour à la ligne (sans saut de ligne)
    '\n'        Passage à la ligne
    '\t'        Tabulation
    '\v'        Tabulation verticale

   D'autres séquences d'échappement sont disponibles, afin de pouvoir représenter les caractères ayant une signification particulière en C :
    '\\'        Le caractère \
    '\''        Le caractère '
  Note: Attention ! Il n'y a pas de chaînes de caractères. Les chaînes de caractères sont en fait des tableaux de caractères. Cependant, on pourra créer des tableaux de caractères constants en donnant la chaîne entre doubles guillemets :
   "Exemple de chaîne de caractère..."
   Les caractères spéciaux peuvent être utilisés directement dans les chaînes de caractères :
"Ceci est un saut de ligne :\nCeci est à la ligne suivante."
   Si une chaîne de caractère est trop longue pour tenir sur une seule ligne, on peut concaténer plusieurs chaînes en les juxtaposant :
"Ceci est la première chaîne " "ceci est la deuxième."
produit la chaîne de caractères complète suivante :
"Ceci est la première chaîne ceci est la deuxième."
   Note: Attention : il ne faut pas mettre de caractère nul dans une chaîne de caractères. Ce caractère est en effet le caractère de terminaison de toute chaîne de caractères.
Voir plus loin pour de plus amples informations sur les chaînes de caractères et les tableaux.
   Enfin, les versions longues des différents types cités précédemment (wchar_t, long int et long double) peuvent être notées en faisant précéder ou suivre la valeur de la lettre 'L'. Cette lettre doit précéder la valeur dans le cas des caractères et des chaînes de caractères, et la suivre quand il s'agit des entiers et des flottants.    Exemple :
L"Ceci est une chaîne de wchar_t."
   2.3e5L

2.4. La déclaration des variables
    Les variables simples se déclarent avec la syntaxe suivante :
type identificateur;
où type est le type de la variable et identificateur est son nom. Il est possible de créer et d'initialiser une série de variables dès leur création avec la syntaxe suivante :
type identificateur[=valeur][, identificateur[=valeur][…]];
   Exemple 2-8. Déclaration de variables
   int i=0, j=0;     // Déclare et initialise deux entiers à 0
   double somme;     // Déclare une variable réelle
  Les variables peuvent être déclarées quasiment n'importe où dans le programme. Ceci permet de ne déclarer une variable temporaire que là où l'on en a besoin.
   Note: Ceci n'est vrai qu'en C++. En C pur, on est obligé de déclarer les variables au début des fonctions ou des instructions composées (voir plus loin). Il faut donc connaître les variables temporaires nécessaires à l'écriture du morceau de code qui suit leur déclaration.
La déclaration d'un tableau se fait en faisant suivre le nom de l'identificateur d'une paire de crochet, contenant le nombre d'élément du tableau :
   type identificateur[taille]([taille](…));
   Note: Attention ! Les caractères '[' et ']' étant utilisés par la syntaxe des tableaux, ils ne signifient plus les éléments facultatifs ici. Ici, et ici seulement, les éléments facultatifs sont donnés entre parenthèses.
Dans la syntaxe précédente, type représente le type des éléments du tableau.
   Exemple 2-9. Déclaration d'un tableau
      int MonTableau[100];
   MonTableau est un entier de 100 entiers. On référence les éléments des  tableaux en donnant l'indice de l'élément entre crochet :
MonTableau[3]=0;
   Les tableaux à plus d'une dimension sont en fait des tableaux de tableaux. On prendra garde au fait que dans la déclaration d'un tableau à plusieurs dimensions, la dernière dimension indiquée est la dimension du tableau dont on fait un tableau. Ainsi, dans l'exemple suivant :
int Matrice[5][4];

  Matrice est un tableau de dimension 5 dont les éléments sont des tableaux de dimension 4. L'ordre de déclaration des dimensions est donc inversé : 5 est la taille de la dernière dimension et 4 est la taille de la première dimension. L'élément suivant :
int Matrice[2];
Est donc le deuxième élément de ce tableau à 5 dimensions, et est lui-même un tableau à 4 dimensions.
On prendra garde au fait qu'en C/C++, les indices des tableaux varient de 0 à taille-1. Il y a donc bien taille éléments dans le tableau. Dans l'exemple donné ci-dessus, l'élément MonTableau[100] n'existe pas : y accéder plantera le programme. C'est au programmeur de vérifier que ses programmes n'utilisent jamais les tableaux avec des indices plus grand que leur taille.
  Un autre point auquel il faudra faire attention est la taille des tableaux à utiliser pour les chaînes de caractères. Une chaîne de caractères se termine obligatoirement par le caractère nul ('\0'), il faut donc réserver de la place pour lui. Par exemple, pour créer une chaîne de caractères de 100 caractères au plus, il faut un tableau pour 101 caractères (déclaré avec « char chaine[101]; »).
2.5. Les instructions
   Les instructions sont identifiées par le point virgule. C'est ce caractère qui marque la fin d'une instruction.
   Exemple 2-10. Instruction vide
   ; // Instruction vide : ne fait rien !
 Les opérations possibles à l'intérieur d'une instruction sont les suivantes :
  • affectation :
    variable = valeur
      Note: L'affectation n'est pas une instruction. C'est une opération qui renvoie la valeur affectée. On peut donc effectuer des affectations multiples.
       Exemple 2-11. Affectation multiple
    i=j=k=m=0; // Annule les variables i, j, k et m.
  • autres opérations :
    valeur op valeur où op est l'une des opérations suivantes : +, -, *, /, %, &, |, ^, ~, <<, >>.
    Note: '/' représente la division euclidienne pour les entiers, et la division classique pour les flottants.
    '%' représente la congruence (modulo). '|' et '&' représentent respectivement le ou et le et binaire (c'est à dire bit à bit :
    1 et 1 = 1, 0 et x = 0, 1 ou x = 1 et 0 ou 0 = 0). '^' représente le ou exclusif (1 xor 1 = 0 xor 0 = 0 et 1 xor 0 = 1). '~' représente la négation binaire (1 <-> 0). '<<' et '>>' effectuent un décalage binaire vers la gauche et la droite respectivement, d'un nombre de bits égal à la valeur du second opérande.
  • affections composées. Ces opérations permettent de réaliser une opération normale et une affectation en une seule étape : variable op_aff valeur avec op_aff l ´un des opérateurs suivants : '+=', '-=', '*=', etc…
    Cette syntaxe est strictement équivalente à :
    variable = variable op valeur

   Exemple 2-12. Affectation composée
      i*=2;        // Multiplie i par 2 : i = i * 2.
Il est possible de créer un bloc d´'instructions, en entourant les instructions de ce bloc avec des accolades. Un bloc d'instructions est considéré comme une instruction unique. Il est inutile de mettre un point virgule pour marquer l'instruction, puisque le bloc lui-même est une instruction.
   Exemple 2-13. Instruction composée
               {
             i=1;
                  j=i+3*g;
}

2.6. Les fonctions

  Le C++ ne permet de faire que des fonctions, pas de procédures. Une procédure peut être faite en utilisant une fonction ne renvoyant pas de valeur, ou en ignorant la valeur retournée.

2.6.1. Définition des fonctions
 La définition des fonctions se fait comme suit :
type identificateur(paramètres)
             {
             ;… /* Instructions de la fonction. */
            }
type est le type de la valeur renvoyée, identificateur est le nom de la fonction, et paramètres est une liste de paramètres. La syntaxe de la liste de paramètres est la suivante :
type variable [= valeur] [, type variable [= valeur] […]]
où type est le type du paramètre variable qui le suit et valeur sa valeur par défaut. La valeur par défaut d'un paramètre est la valeur que ce paramètre prend lors de l'appel de la fonction si aucune autre valeur n'est fournie.
   Note: L'initialisation des paramètres de fonctions n'est possible qu'en C++, le C n'accepte pas cette syntaxe.
La valeur de la fonction à renvoyer est spécifiée en utilisant la commande return, dont la syntaxe est :
   return valeur;
   Exemple 2-14. Définition de fonction
      int somme(int i, int j)
         {
          return i+j;
         }

  Si une fonction ne renvoie pas de valeur, on lui donnera le type void. Si elle n'attend pas de paramètres, sa liste de paramètres sera void ou n'existera pas. Il n'est pas nécessaire de mettre une instruction return à la fin d'une fonction qui ne renvoie pas de valeur.
   Exemple 2-15. Définition de procédure
   void rien()          // Fonction n'attendant pas de paramètres
         {        // et ne renvoyant pas de valeur.
return; // Cette ligne est facultative.
         }
2.6.2. Appel des fonctions

  L'appel d'une fonction se fait en donnant son nom, puis les valeurs de ses paramètres entre parenthèses. Attention ! S'il n'y a pas de paramètres, il faut quand même mettre les parenthèses, sinon la fonction n'est pas appelée.
   Exemple 2-16. Appel de fonction
    int i=somme(2,3);
    rien();

  Si la déclaration comprend des valeurs par défaut pour des paramètres (C++ seulement), ces valeurs sont utilisées lorsque ces paramètres ne sont pas fournis lors de l'appel. Si un paramètre est manquant, alors tous les paramètres qui le suivent doivent être eux aussi manquants. Il en résulte que seuls les derniers paramètres d'une fonction peuvent avoir des valeurs par défaut. Par exemple :

      int test(int i = 0, int j = 2)
       {
         return i/j;
       }
L'appel de la fonction test(8) est valide. Comme on ne précise pas le dernier paramètre, j est initialisé à 2. Le résultat obtenu est donc 4. De même, l'appel test() est valide : dans ce cas i vaut 0 et j vaut 2. En revanche, il est impossible d'appeler la fonction test en ne précisant que la valeur de j. Enfin, l'expression « int test (int i=0, int j) {…} » serait invalide, car si on ne passait pas deux paramètres, j ne serait pas initialisé.
2.6.3. Déclaration des fonctions
  Toute fonction doit être déclarée avant d'être appelée pour la première fois. La définition d'une fonction peut faire office de déclaration.
  Il peut se trouver des situations où une fonction doit être appelée dans une autre fonction définie avant elle. Comme cette fonction n'est pas définie au moment de l'appel, elle doit être déclarée. Le langage C/C++ permet de faire des déclarations de fonctions sans les définir et donc de reporter la définition des corps de ces fonctions plus loin.
  La syntaxe de la déclaration d'une fonction est la suivante :
type identificateur(paramètres);
où type est le type de la valeur renvoyée par la fonction, identificateur est son nom et paramètres la liste des types des paramètres que la fonction admet, séparés par des virgules.
Exemple 2-17. Déclaration de fonction
    int min(int, int);      // Déclaration de la fonction minimum
      // définie plus loin.
/* Fonction principale. */
    int main(void)
      {
     int i = min(2,3);       // Appel à la fonction min, déjà
            // déclarée.
     return 0;
      }
/* Définition de la fonction min. */
      int min(int i, int j)
          {
       if (i         else return j;
          }
En C++, il est possible de donner des valeurs par défaut aux paramètres dans une déclaration, et ces valeurs peuvent être différentes de celles que l'on peut trouver dans une autre déclaration. Dans ce cas, les valeurs par défaut utilisées sont celles de la déclaration visible lors de l'appel de la fonction.

2.6.4. Surcharge des fonctions
   Il est interdit en C de définir plusieurs fonctions qui portent le même nom. En C++, cette interdiction est levée, moyennant quelques précautions. Le compilateur peut différencier deux fonctions en regardant le type des paramètres qu'elle reçoit. La liste de ces types s'appelle la signature de la fonction. En revanche, le type du résultat de la fonction ne permet pas de l'identifier, car le résultat peut être converti en une valeur d'un autre type avant d'être utilisé après l'appel de cette fonction.
   Il est donc possible de faire des fonctions de même nom (on dit que ce sont des fonctions surchargées) si et seulement si toutes les fonctions portant ce nom peuvent être distinguées par leurs signatures. La fonction qui sera appelée sera choisie parmi les fonctions de même nom, et ce sera celle dont la signature est la plus proche des valeurs passées en paramètre lors de l'appel.
   Exemple 2-18. Surcharge de fonctions

    float test(int i, int j)
     {
      return (float) i+j;
        }
    float test(float i, float j)
     {
      return i*j;
     }
   Ces deux fonctions portent le même nom, et le compilateur les acceptera toutes les deux. Lors de l'appel de test(2,3), ce sera la première qui sera appelée, car 2 et 3 sont des entiers. Lors de l'appel de test (2.5,3.2), ce sera la deuxième, parce que 2.5 et 3.2 sont réels.
Attention ! Dans un appel tel que test(2.5,3), le flottant 2.5 sera converti en entier et la première fonction sera appelée. Il convient donc de faire très attention aux mécanismes de surcharges du langage, et de vérifier les règles de priorité utilisées par le compilateur.
   On veillera à ne pas utiliser des fonctions surchargées dont les paramètres ont des valeurs par défaut, car le compilateur ne pourrait pas faire la distinction entre ces fonctions. D'une manière générale, le compilateur dispose d'un ensemble de règles (dont la présentation dépasse le cadre de ce cours) qui lui permettent de déterminer la meilleure fonction étant donné un jeu de paramètres. Si, lors de la recherche de la fonction à utiliser, le compilateur trouve des ambiguïtés, il générera une erreur.
2.6.5. Fonctions inline
   Le C++ dispose du mot-clé inline, qui permet de modifier la méthode d'implémentation des fonctions. Placé devant la déclaration de la fonction, il donne l'autorisation au compilateur de ne pas instancier cette fonction. Cela signifie qu'il a le droit de remplacer l'appel d'une fonction par le code correspondant. Si la fonction est grosse ou si elle est appelée souvent, le programme devient plus gros, puisque la fonction est réécrite à chaque fois qu'elle est appelée. En revanche, il devient nettement plus rapide, puisque les mécanismes d'appel de fonctions, de passage des paramètres et de la valeur de retour sont ainsi évités. De plus, le compilateur peut effectuer des optimisations additionnelles qu'il n'aurait pas pu faire si la fonction n'était pas inlinée. En pratique, on réservera cette technique pour les petites fonctions appelées dans du code devant être rapide (à l'intérieur des boucles par exemple), ou pour les fonctions permettant de lire des valeurs dans des variables.
   Cependant, il faut se méfier. Le mot-clé inline donne l'autorisation au compilateur de faire des fonctions inline. Il n'y est pas obligé. La fonction peut donc très bien être implémentée classiquement.
Pire, elle peut être implémentée des deux manières, selon les mécanismes d'optimisation du compilateur.
   De plus, il faut connaître les restrictions des fonctions inline :
  • elles ne peuvent pas être récursives ;
  • elles ne sont pas instanciées, donc on ne peut pas faire de pointeur sur une fonction inline.

   Si l'une de ces deux conditions n'est pas vérifiée pour une fonction, le compilateur l'implémentera classiquement (elle ne sera donc pas inline).
   Exemple 2-19. Fonction inline
   inline int max(int i, int j)
     {
      if (i>j)
     return i;
     else
     return j;
     }
   Pour ce type de fonction, il est tout à fait justifié d'utiliser le mot-clé inline.

2.6.6. Fonctions statiques
   Par défaut, lorsqu'une fonction est définie dans un fichier C/C++, elle peut être utilisée dans tout autre fichier pourvu qu'elle soit déclarée. Dans ce cas, la fonction est dite externe. Il peut cependant être intéressant de définir des fonctions locales à un fichier, soit afin de résoudre des conflits de noms (entre deux fonctions de même nom et de même signature de deux fichiers différents), soit parce que la fonction est uniquement d'intérêt local. Le C et le C++ fournissent donc le mot-clé static, qui, une fois placé devant la définition et les éventuelles déclarations d'une fonction, la rend unique et utilisable uniquement dans ce fichier. À part ce détail, les fonctions statiques s'utilisent exactement comme des fonctions classiques.
   Exemple 2-20. Fonction statique
  // Déclaration de fonction statique :
  static int locale1(void);

  // Définition de fonction statique :
    static int locale2(int i, float j)
     {
     return i*i+j;
     }

2.6.7. Fonctions prenant un nombre de paramètres variable
  En général, les fonctions ont un nombre constant de paramètres. Pour les fonctions qui ont des paramètres par défaut en C++, le nombre de paramètres peut apparaître variable à l'appel de la fonction, mais en réalité, la fonction utilise toujours le même nombre de paramètres.
  Cependant, le C et le C++ disposent d'un mécanisme qui permet au programmeur de réaliser des fonctions dont le nombre et le type des paramètres est variable. Nous verrons plus loin que les fonctions d'entrée - sortie du C sont des fonctions dont la liste des arguments n'est pas fixée, ceci afin de pouvoir réaliser un nombre d'entrées - sorties arbitraire, et ce sur n'importe quel type prédéfini.
  En général, les fonctions dont la liste des paramètre est arbitrairement longue disposent d'un critère pour savoir quel est le dernier paramètre. Ce critère peut être le nombre de paramètres, qui peut être fourni en premier paramètre à la fonction, ou une valeur de paramètre particulière qui détermine la fin de la liste par exemple. On peut aussi définir les paramètres qui suivent le premier paramètre à l'aide d'une chaîne de caractère.
  Pour indiquer au compilateur qu'une fonction peut accepter une liste de paramètres variable, il faut simplement utiliser des points de suspensions dans la liste des paramètres :
type identificateur(paramètres, ...)
dans les déclarations et la définition de la fonction. Dans tous les cas, il est nécessaire que la fonction ait au moins un paramètre classique. Ces paramètres doivent impérativement être avant les points de suspensions.
  La difficulté apparaît en fait dans la manière de récupérer les paramètres de la liste de paramètres dans la définition de la fonction. Les mécanismes de passage des paramètres étant très dépendants de la machine (et du compilateur), un jeu de macros a été défini dans le fichier d'en-tête stdarg.h pour faciliter l'accès aux paramètres de la liste. Pour en savoir plus sur les macros et les fichiers d'en-tête, consulter le Chapitre 6. Pour l'instant, sachez seulement qu'il faut ajouter la ligne suivante :

  #include
  au début de votre programme. Ceci permet d'utiliser le type va_list et les expressions va_start, va_arg et va_end pour récupérer les arguments de la liste de paramètres variable, un à un.
Le principe est simple. Dans la fonction, vous devez déclarer une variable de type va_list. Puis, vous devez initialiser cette variable avec la syntaxe suivante :

va_start(variable, paramètre);
  où variable est le nom de la variable de type va_list que vous venez de créer, et paramètre est le dernier paramètre classique de la fonction. Dès que variable est initialisée, vous pouvez récupérer un à un les paramètres à l'aide de l'expressions suivantes :

va_arg(variable, type)
qui renvoie le paramètre en cours avec le type type et met à jour variable pour passer au paramètre suivant. Vous pouvez utiliser cette expression autant de fois que vous le désirez, elle retourne à chaque fois un nouveau paramètre. Lorsque le nombre de paramètres correct a été récupéré, vous devez détruire la variable variable à l'aide de la syntaxe suivante :
va_end(variable);
   Il est possible de recommencer les étapes suivantes autant de fois que l'on veut, la seule chose qui compte est de bien faire l'initialisation avec va_start et de bien terminer la procédure avec va_end à chaque fois.
  Note: Il existe une restriction sur les types des paramètres des listes variables d'arguments. Lors de l'appel des fonctions, un certain nombre de traitements sur les paramètres a lieu. En particulier, des promotions implicites ont lieu, ce qui se traduit par le fait que les paramètres réellement passés aux fonctions ne sont pas du type déclaré. Le compilateur continue de faire les vérifications de type, mais en interne, un type plus grand peut être utilisé pour passer les valeurs des paramètres. En particulier, les types char et short ne sont pas utilisés : les paramètres sont toujours promus aux type int ou long int. Ceci implique que les seuls types que vous pouvez utiliser sont les types cibles des promotions et les types qui ne sont pas sujets aux promotions (pointeurs, structures et unions). Les types cibles dans les promotions sont déterminés comme suit :
  • les types char, signed char, unsigned char, short int ou unsigned short int sont promus en int si ce type est capable d'accepter toutes leurs valeurs. Si int est insuffisant, unsigned int est utilisé ;
  • les types des énumérations (voir plus loin pour la définition des énumérations) et wchar_t sont promus en int, unsigned int, long ou unsigned long selon leurs capacités. Le premier type capable de conserver la plage de valeur du type à promouvoir est utilisé ;
  • les valeurs des champs de bits sont converties en int ou unsigned int selon la taille du champ de bit (voir plus loin pour la définition des champs de bits) ;
  • les valeurs de type float sont converties en double.

   Exemple 2-21. Fonction à nombre de paramètres variable
#include
 // Fonction effectuant la somme de "compte" paramètres :

   double somme(int compte, ...)
    {
     double resultat=0;         // Variable stockant la somme.
   va_list varg;         // Variable identifiant le prochain
        // paramètre.
    va_start(varg, compte);        // Initialisation de la liste.
    do         // Parcours de la liste.
     {
     resultat=resultat+va_arg(varg, double);
     compte=compte-1;
     } while (compte!=0);
    va_end(varg);         // Terminaison.
    return resultat;
      }
  La fonction somme effectue la somme de compte flottants (float ou double) et la renvoie dans un double. Pour plus de détails sur la structure de contrôle do … while, voir Section 3.1.4.
2.7. La fonction main
  Lorsqu'un programme est chargé, son exécution commence par l'appe d'une fonction spéciale du programme. Cette fonction doit impérativement s'appeler « main » (principal en Anglais) pour que le compilateur puisse savoir que c'est cette fonction qui marque le début du programme. La fonction main est appelée par le système d'exploitation, elle ne peut pas être appelée par le programme, c'est à dire qu'elle ne peut pas être récursive.
   Exemple 2-22. Programme minimal
   int main()         // Plus petit programme C/C++.
    {
     return 0;
    }

La fonction main doit renvoyer un code d'erreur d'exécution du programme, le type de ce code est int. Elle peut aussi recevoir des paramètres du système d'exploitation. Ceci sera expliqué plus loin. Pour l'instant, on se contentera d'une fonction main ne prenant pas de paramètres.
   Note: Il est spécifié dans la norme du C++ que la fonction main ne doit pas renvoyer le type void. En pratique cependant, tous les compilateurs l'acceptent aussi.

2.8. Les fonctions d'entrée-sortie de base

   Les deux fonctions de base d'entrée-sortie du C sont printf et scanf.
printf (« print formatted » en Anglais) permet d'afficher des données à l'écran. scanf (« scan formatted ») permet de les lire à partir du clavier. Elles attendent toutes les deux une chaîne de caractères en premier paramètre. Cette chaîne est appelée chaîne de format, et elle permet de spécifier le type, la position et le format (précision, etc…) des données à traiter.

2.8.1. La fonction printf
   Elle s'emploie comme suit :
   printf(chaîne de format [, valeur [, valeur […]]])
  Elle renvoie le nombre de caractères affichés.   On peut passer autant de valeurs que l'on veut, pour peu qu'elles soient toutes référencées dans la chaîne de format. La chaîne de format peut contenir du texte, mais surtout elle doit contenir autant de formateurs que de variables à afficher. Si ce n'est pas le cas, le programme plantera. Les formateurs sont placés dans le texte là où les valeurs des variables doivent être affichées.
La syntaxe des formateurs est la suivante :
%[[indicateur]…][largeur][.précision][taille] type
   Un formateur commence donc toujours par le caractère %. Pour afficher ce caractère sans faire un formateur, il faut le dédoubler (%%).
Le type de la variable à afficher est obligatoire lui aussi. Les types utilisables sont les suivants :
vvvvv Type de données à afficher Caractère de formatage
Numériques Entier décimal signé    d
vvvvv Entier décimal non signé u ou i
vvvvv Entier octal non signé    o
vvvvv Entier héxadécimal non signé x (avec les caractères 'a' à 'f') ou X (avec les caractères 'A' à 'F')
vvvvv Flottants f, e, g, E ou G
Caractères Caractère isolé    c
vvvvv Chaîne de caractères    s
Pointeurs Pointeur    p
Tableau 2-1. Types pour les chaînes de format de printf
        F         Pointeur Pointeur FAR (PC uniquement)
        N         Pointeur Pointeur NEAR (PC uniquement)
        H         Entier short int
        L         Entier ou flottant long int ou double
        L         Flottant long double
Tableau 2-2. Options pour les types des chaînes de format

   Exemple 2-23. Utilisation de printf

 #include    /* Ne pas chercher à comprendre cette ligne */                /* Elle est nécessaire pour utiliser les */                /* fonctions printf et scanf. */
     int main(void)
       {
      int i = 2;
      printf("Voici la valeur de i : %d.", i);
      return 0;
       }
Les paramètres indicateurs, largeur et précisions sont moins utilisés.
Il peut y avoir plusieurs paramètres indicateurs, ils permettent de modifier l'apparence de la sortie. Les principales options sont :

• '-' : justification à gauche de la sortie, avec remplissage à droite par des 0 ou des espaces ; • '+' : affichage du signe pour les nombres positifs ; • espace : les nombres positifs commencent tous par un espace.
Le paramètre largeur permet de spécifier la largeur minimum du champ de sortie, si la sortie est trop petite, on complète avec des 0 ou des espaces. Enfin, le paramètre précision spécifie la précision maximum de la sortie (nombre de chiffres à afficher).

2.8.2. La fonction scanf
   La fonction scanf permet de faire une ou plusieurs entrées. Comme la fonction printf, elle attend une chaîne de format en premier paramètre. Il faut ensuite passer les variables devant contenir les entrées dans les paramètres qui suivent. Sa syntaxe est la suivante :
    scanf(chaîne de format, &variable [, &variable […]]);
  Elle renvoie le nombre de variables lues.
Ne cherchez pas à comprendre pour l'instant la signification du symbole & se trouvant devant chacune des variables. Sachez seulement que s'il est oublié, le programme plantera.
La chaîne de format peut contenir des chaînes de caractères. Toutefois, si elle contient autre chose que des formateurs, le texte saisi par l'utilisateur devra correspondre impérativement avec les chaînes de caractères indiquées dans la chaîne de format. scanf cherchera à reconnaître ces chaînes, et arrêtera l'analyse à la première erreur.
   La syntaxe des formateurs pour scanf diffère un peu de celle de ceux de  printf :
    %[*][largeur][taille]type
   Seul le paramètre largeur change par rapport à printf. Il permet de spécifier le nombre maximum de caractères à prendre en compte lors de l'analyse du paramètre. Le paramètre '*' est facultatif, il indique seulement de passer la donnée entrée et de ne pas la stocker dans la variable destination. Cette variable doit quand même être présente dans la liste des paramètres de scanf.
2.9. Exemple de programme complet
   Pour utiliser les fonctions printf et scanf, il faut mettre au début du programme la ligne suivante :
   #include
   Sans entrer dans les détails, disons simplement que cette ligne permet d'inclure un fichier contenant les déclarations des fonctions printf et scanf. Voir le chapitre sur le préprocesseur pour de plus amples détails.
   Le programme suivant est donné à titre d'exemple. Il calcule la moyenne de deux nombres entrés au clavier et l'affiche :
Exemple 2-24. Programme complet simple
   #include       /* Autorise l'emploi de printf et de scanf. */

    long double x, y;


    int main(void)
         {
    printf("Calcul de moyenne\n");       // Affiche le titre.
     printf("Entrez le premier nombre : ");
     scanf("%Lf", &x);       // Entre le premier nombre.
     printf("\nEntrez le deuxième nombre : ");
     scanf("%Lf", &y);       // Entre le deuxième nombre.
     printf("\nLa valeur moyenne de %Lf et de %Lf est %Lf.\n",
     x, y, (x+y)/2);
      return 0;
         }
Dans cet exemple, les chaînes de format spécifient des flottants (f) en quadruple précision (L).
Commentaires (5)

1. jamal el atmani 05/01/2006

azul ait tmazirt matt3nam thanna tanmirt khf ouydkh tscarm bon chance bon courage

tarmouchte boumalne dades ouarzazate

2. arjaz 18/02/2006

istlla tagot j temezirt

3. Ahmed (site web) 09/08/2008

azul ayouznk fmayad tskrt kml fmayan hati ghikan aguigh ta tmazighte nki guigh yan omazigh gh tassort (essaouira) ligh ghyat tmsmont ismas tigzirt ightrit ant3awn f tmazighte mrhba

4. mkoutleter 21/05/2013

fake louboutin Jerseys at SFGate Fan christian louboutin sale Shop:

5. mkout14 07/08/2013

louboutin shoes outlet,

Ajouter un commentaire

Vous utilisez un logiciel de type AdBlock, qui bloque le service de captchas publicitaires utilisé sur ce site. Pour pouvoir envoyer votre message, désactivez Adblock.

MOBILISONS NOUS POUR LE DEVELOPPEMENT DE SARGHINE EN AIDANT L'ASSOCIATION AMSIRAR.

Créer un site gratuit avec e-monsite - Signaler un contenu illicite sur ce site

×