Mercredi 21 novembre 2007 3 21 /11 /Nov /2007 14:00

Un outil à employer avec prudence :

Les macros sont la source de nombreuses erreurs très difficiles à repérer, puisqu’on ne dispose pas de la version étendue du code. Par exemple, on peut se demander pourquoi dans la macro CARRE ci-dessus nous avons placé ces parenthèses. Mais si l’on écrit :

#define CARRE(x) x * x // ... j = CARRE(i+1);

la dernière ligne deviendra :

j = i+1 * i+1;

qui est interprété comme i + (1*i) +1, soit 2*i+1.

Même avec une définition correcte de CARRE, on peut avoir des surprises :

#define CARRE(x) (x)*(x) // ... int i = 3, j = CARRE(i++);

en sortie i vaut 5, et non 4, parce que la macro a été étendue sous la forme (i++)*(i++) et provoque deux incrémentations. Ce genre d’erreur est particulièrement ardu à repérer.

D’une façon générale les macros sont dangereuses, car il n’y a aucun contrôle des types ; ainsi, si l’on utilise la macro AFFICHE définie ci-avant avec un paramètre non entier, on risque de sérieux problèmes.

En C++, les macros qui définissent des constantes diverses seront avantageusement remplacées par des déclarations de constantes :

const Pi = 3.141592; const Errmsg = "Une erreur s'est produite.n";

Les macros qui définissent de courtes actions, avec ou sans paramètres, seront remplacées par des fonctions en ligne :

inline long carre(long l) { return l*l;  } inline double carre(double d) { return d*d; }

qui ne posent pas de problèmes, même avec des effets de bord, et qui vérifient les types de leurs paramètres.

Certaines fonctions en ligne sont d’ailleurs bien plus simples que les macros correspondantes. Essayez d’écrire la macro correspondant à :

inline void echange(int& i, int&  j) {    int k = i; i = j; j = k; }                                  inline void echange(long& i, long& j) {    long k = i; i = j; j = k; }                                  inline void echange(double& i, double& j) {    double k = i; i = j; j = k; }

Inversement, certaines macros ne peuvent pas être évitées. C’est le cas par exemple de AFFICHE dans la section précédente, qui utilise le nom des variables. On peut toutefois la rendre plus sûre en utilisant les flots de sortie :

#define AFFICHE(x) cout << "Valeur  de " #x " = " << ;;

qui peut alors fonctionner quel que soit le type de x.

Les classes génériques fournissent un autre exemple d’utilisation pratique des macros en C++.

Classes génériques :

Il n’y a pas, dans les premières versions de C++, de moyen de définir une classe générique, c’est-à-dire dépendant d’un paramètre comme le type d’un élément contenu dans la classe. On peut toutefois le simuler en utilisant une macro. Dans les versions plus récentes, cette fonctionnalité a été ajoutée sous le nom anglais de template (en français, gabarits).

Revenons à notre exemple de la liste chaînée (chapitre 6). Cette liste utilise une classe element qui peut désigner n’importe quoi ; c’est caractéristique d’une classe générique.

Pour changer facilement le type d’élément de liste, il y a deux possibilités. La première consiste à créer un fichier séparé liste.h ne contenant pas la définition de element, par exemple comme ceci (en simplifiant beaucoup la définition de la classe noeud) :

liste.h

class noeud {      noeud *suivt;      element elm;      public :      noeud(element e, noeud *suivant = 0)          { elm = e; suivt = suivant; }      noeud *suivant(void) { return suivt; }      element &contenu(void) { return elm; }      };

 

Dans ce cas, avant d’inclure liste.h dans votre fichier, il faudra écrire la définition de element :

typedef double element; #include "liste.h"

par exemple. C’est la méthode que nous avons employée jusqu’à présent. Elle a toutefois l’inconvénient de ne permettre l’utilisation que d’un seul type de liste chaînée.

Une seconde méthode consiste à écrire les deux macros suivantes (dans un fichier séparé en général, que nous nommons encore liste.h) :

liste.h

#include <generic.h>                                  #define noeud(typ) _Paste2(noeud_, typ)                                  #define listedeclare(typ)     class noeud(typ) {          noeud(typ) *suivt;          typ elm;              public :          noeud(typ)(typ e, noeud(typ) *suivant = 0)              { elm = e; suivt = suivant; }          noeud(typ) *suivant(void) { return suivt; }          typ &contenu(void) { return elm; }          }

 

Le fichier <generic.h> contient un certain nombre de macros pour coller des éléments, dont voici les principales :

#define _Paste2(x, y) x##y // coller x et y ensemble #define declare(x, y) _Paste2(x, declare)(y) // déclarer l’objet x avec paramètre y #define implement(x, y) _Paste2(x, implement)(y) // définir l’objet x avec paramètre y

À présent, écrivons le programme suivant :

#include "liste.h"  declare(liste, int);                                  declare(liste, double);                  main()     {         noeud(int) *ni = new noeud(int)(0);         noeud(double) *nd = new noeud(double)(3.14);         // ...     }

Ce programme sera transformé ainsi par le préprocesseur :

class noeud_int {         noeud_int *suivt;         int elm;      public :         noeud_int(int e, noeud_int *suivant = 0)                  { elm = e; suivt = suivant; }         noeud_int *suivant(void) { return suivt; }         int &contenu(void) { return elm; }         };                           class noeud_double {         noeud_double *suivt;         double elm;      public :         noeud_double(double e, noeud_double *suivant = 0)                     { elm = e; suivt = suivant; }         noeud_double *suivant(void) { return suivt; }         double &contenu(void) { return elm; }         };                           main()     {         noeud_int *ni = new noeud_int(0);         noeud_double *nd = new noeud_double(3.14);         // ...     }

Avec nos clauses declare, on a donc en fait déclaré deux classes noeud_int et noeud_double. Les noms de ces classes peuvent être utilisés tels quels, ou sous la forme de macros noeud(int) et noeud(double) qui donne l’illusion d’une classe générique. Notons qu’il faut toutefois effectivement une déclaration pour chaque type utilisé, ce qui ne serait pas le cas avec une vraie classe générique comme il en existe dans certains langages comme ADA, ou les versions récentes de C++.

Les implémentations, lorsqu’il y en a (fonctions qui ne sont pas en ligne en particulier), seront définies dans une seconde macro listeimplement(typ) et on écrira implement(typ) dans les fichiers ayant besoin de ces implantations.

On gagne ainsi une certaine facilité d’utilisation, moyennant un surcoût au moment de l’écriture des classes génériques.

Éditeur de liens, fichiers multiples :

Nous avons vu qu’avant d’utiliser une fonction il fallait la déclarer, mais pas forcément la définir. De ce fait, lorsque le compilateur rencontre un appel d’une fonction dont il ne connaît pas la définition, et donc pas l’adresse exacte, il crée une demande de lien entre l’appel et la fonction à joindre.

Lorsque la compilation proprement dite est terminée, l’éditeur de liens prend la relève ; en deux passes, il va réaliser les liens, c’est-à-dire trouver les fonctions dont on ne connaissait pas l’adresse et mettre cette dernière au bon endroit.

Pour cela l’éditeur de liens examine deux types de fichiers compilés : le ou les fichiers du projet courant, et ceux des librairies standard. S’il ne trouve pas la fonction qu’il cherche, il proteste en affichant un message d’erreur.

Notons que l’éditeur de liens exécute une tâche complexe, car il vérifie aussi la cohérence des déclarations multiples, et ne conserve que les fonctions réellement utilisées : les autres, quelle que soit leur provenance, sont éliminées, ce qui garantit un programme de taille (presque) minimale.

Librairies standard :

Une librairie est un regroupement de fichiers objets déjà compilés. Ces fichiers objets (*.obj ou *.o) sont ceux fournis par le compilateur.

Les fonctions standard sont implémentées dans un certain de librairies nommées xxx.lib. Les déclarations des routines sont reproduites dans des fichiers d’en-têtes (*.h) ; ces fichiers sont très nombreux (39 en Turbo C++). Lorsqu’on désire utiliser des routines standard, on doit inclure un ou plusieurs de ces fichiers d’en-têtes dans le fichier courant, comme on l’a déjà vu à plusieurs reprises. L’éditeur de liens se chargera d’aller retrouver l’implantation des routines dans les librairies.

Fichiers multiples :

Lorsqu’un programme devient volumineux, il est peu rentable de le placer dans un seul fichier : la compilation devient très longue, puisqu’il faut tout recompiler chaque fois, et il est difficile de s’y retrouver.

Pour simplifier alors le travail, on répartit les différentes routines dans plusieurs fichiers source (*.cpp). Chacun de ces fichiers peut alors être compilé indépendamment, produisant un fichier objet (*.obj). L’ensemble des fichiers objets est ensuite regroupé par l’éditeur de liens pour former un programme exécutable unique (*.exe, sur PC).

Dans la pratique, les différents fichiers doivent d’abord répondre à une certaine logique. Par exemple, on place fréquemment dans un fichier séparé une classe entièrement implémentée, ou deux ou trois si elles sont reliées. Il ne faut surtout pas répartir au hasard les fonctions dans plusieurs fichiers, car il serait vite très difficile de s’y retrouver.

Comme les fichiers doivent communiquer entre eux, il faut au moins un fichier en-tête qui leur soit commun. En pratique, on utilise même souvent un fichier en-tête pour chaque source, sauf celui qui contient main ; en général on donne à ce fichier le même nom avec le suffixe *.h (mais ce n’est nullement obligatoire). Lorsqu’on utilise cette méthode très fréquente, le fichier en-tête peut être considéré comme l’interface d’un module dont le fichier source est l’implantation. Dans ce fichier d’en-tête on placera toutes les déclarations (de classes, de fonctions, de constantes, de variables, etc.) susceptibles d’être utilisées par les autres. On n’oubliera pas les fonctions en ligne, car le compilateur doit les connaître entièrement pour les placer directement dans le code produit.

Dans le fichier source proprement dit, on trouvera généralement une directive d’inclusion du fichier en-tête correspondant, suivie éventuellement d’autres directives d’inclusion, soit pour les librairies standard, soit pour les autres fichiers en-têtes du même programme utilisés par le fichier courant. On trouvera ensuite l’implantation des fonctions qui ne sont pas en ligne.

Dans le fichier contenant main, on trouvera toutes les inclusions d’en-têtes nécessaires, suivies par main et éventuellement quelques fonctions étroitement liées à elle.

Prenons un exemple simple (et artificiel). Imaginons un programme faisant des calculs sur des matrices de fractions, implantées par une classe spéciale du même genre que la classe liste, mais adaptée aux fractions. Un tel programme pourrait être réparti dans cinq modules différents :

  • fraction.cpp : Fichier contenant la classe fraction et les opérations sur elle.

  • listefra.cpp : Fichier contenant les classes noeud et liste nécessaires à la gestion de listes chaînées de fractions.

  • matrfra.cpp : Fichier contenant la classe matrice utilisant les listes de fractions, et les opérations sur elle.

  • iosfract.cpp : Fichier contenant diverses fonctions d’écriture et de lecture de fractions et de matrices.

  • mainfra.cpp : Fichier contenant main et la gestion de base du programme (commandes, etc.).

En réalité, les quatre premiers modules sont répartis chacun en un en-tête et un source. Voici l’allure qu’ils pourraient avoir :

fraction.h

#ifndef _FRACTION_H #define _FRACTION_H  class fraction {      // ....définition de la classe       // avec fonctions en ligne éventuelles      };                                  #endif

fraction.cpp

#include "fraction.h"                                  // ici implantation des fonctions membres de  // la classe fraction qui ne sont pas en ligne.

listefra.h

#ifndef _LISTEFRA_H #define _LISTEFRA_H                                  #include "fraction.h"  class noeud_fra;    // définition  dans listefra.cpp                                  class liste_fra {      // listes de fractions      };                                  #endif

listefra.cpp

#include "fraction.h"                                  class noeud_fra {      // ...classe utilisée seulement par liste_fra      };  // implantations des fonctions de noeud_fra  et // liste_fra qui ne sont pas en ligne

matrfra.h

#ifndef _MATRFRA_H #define _MATRFRA_H                                  #include "fraction.h"  class matrice_fra {      // ...      };                                  #endif

matrfra.cpp

#include "listefra.h" #include "matrfra.h"                                  // implantations des fonctions membres de matrice_fra // qui ne sont pas en ligne

iosfract.h

#ifndef _IOSFRACT_H #define _IOSFRACT_H                                  #include "matrfra.h" // déclarations de routines d’affichage et de lecture // de fractions et de matrices                                  #endif

iosfract.cpp

#include <iostream.h> #include "iosfract.h"                                  // implantations des routines définies dans iosfract.h

mainfra.cpp

#include "iosfract.h"                                  main() {      // ... }

 

Voilà notre programme bien silhouetté. On notera que chaque fichier n’inclut que les en-têtes dont il a besoin. Par exemple, les listes de fractions ne sont utilisées que par l’implantation des matrices ; elles n’apparaissent donc que dans matrfra.cpp, et non dans matrfra.h ni dans les autres. De même, les flots d’entrées-sorties déclarés dans <iostream.h> ne sont employés que par l’implantation des fonctions d’entrées-sorties des fractions et matrices.

On remarquera aussi une écriture classique dans les fichiers en-têtes, avec la clause #ifndef _XXX_H suivie par un #define _XXX_H. Ceci permet de n’inclure qu’une seule fois un même fichier d’en-tête dans un fichier. Cela peut paraître inutile, mais remarquez que listefra.h et matrfra.h incluent tous deux fraction.h, et sont tous deux inclus dans matrfra.cpp. Dès lors, si l’on oublie d’utiliser cette clause conditionnelle, c sera inclus deux fois dans c, ce qui non seulement augmentera le temps de compilation mais aussi risque de provoquer une erreur car le compilateur n’acceptera pas que l’on définisse deux fois certains éléments du fichier (variables globales, etc.).

On peut à présent compiler chacun de ces fichiers, soit séparément, soit ensemble (avec un projet, voir ci-après). On obtient alors sur disque cinq fichiers objets fraction.obj, ..., mainfra.obj. Reste à les relier entre eux, ainsi qu’à la librairie standard, pour obtenir le programme souhaité. Cela est fait par l’intermédiaire d’un projet, que nous allons examiner à présent.

Projets :

Continuant sur notre exemple, il va falloir indiquer à l’éditeur de liens de chercher les fonctions dans les cinq fichiers objets créés. Il n’est pas suffisant de lancer la compilation pour obtenir le programme, car l’éditeur de liens ne trouvera pas les fonctions ; en effet, il faut absolument comprendre que bien que les fichiers en-tête et source correspondants aient généralement le même nom (avec un suffixe différent), il n’en est pas forcément ainsi. De plus, l’inclusion des fichiers est faite par le préprocesseur, l’éditeur de liens ne la connaît pas. Enfin, les directives d’inclusion ne suffisent pas à indiquer quels sont les fichiers objets effectivement utilisés ; par exemple, en regardant mainfra.cpp, on pourrait croire qu’un seul fichier objet est nécessaire ; même en remontant les inclusions successives, on ne trouverait pas iosfract qui est « caché » dans matrfra.cpp. Il est pourtant certain que les cinq fichiers objets doivent être utilisés par l’éditeur de liens.

Sur les système d'exploitations en ligne de commande, on gére un tel ensemble à l’aide d’un fichier de « fabrication » (*.make) géré par l’utilitaire make. Dans les systèmes graphiques, les compilateurs intégrés comme Turbo C++ fournit un système plus simple nommé projet. Il suffit d’ouvrir une fenêtre de projet nommée par exemple calcfra.prj et d’y inclure les cinq fichiers source *.cpp.

Lorsque tout y est, il ne reste plus qu’à lancer la commande adéquate (Make dans le cas de Turb C++). Voici alors ce que l’environnement intégré fait :

  • pour chaque fichier dans la fenêtre de projet il vérifie si une modification a été faite depuis la dernière compilation en comparant les dates et heures du fichier objet et des sources. Il examine aussi les modifications indirectes comme la modification d’un fichier en-tête inclus (directement ou non) ;

  • si une modification a été faite, le compilateur est appelé et recrée le fichier objet correspondant ;

  • une fois tous les fichiers objets à jour, l’éditeur de liens est appelé avec la liste de tous ces fichiers objets ; si aucune erreur ne se produit, un fichier exécutable est alors écrit sur disque. Ce fichier a le même nom que le projet par défaut, avec le suffixe exe (calcfra.exe dans notre exemple).

Le seul travail du programmeur consiste donc à ne pas oublier de fichier dans la fenêtre de projet.

La gestion de programmes ayant de multiples fichiers est considérablement facilitée par ce système. Le gain est évident dès que le programme dépasse quelques centaines de lignes (ce qui arrive très vite).

Par exemple, imaginons que l’on modifie un détail dans iosfract.cpp, puis que l’on relance un Make. Dans ce cas, seul le fichier iosfract.cpp sera recompilé, et l’édition de liens sera refaite.

Si l’on modifie un fichier en-tête, les fichiers qui l’utilisent seront recompilés. Par exemple une modification de fraction.h provoquera la recompilation des cinq fichiers source, car tous utilisent directement ou non ce fichier d’en-tête. Par contre, si l’on modifie listefra.h, seuls listefra.cpp et matrfra.cpp seront recompilés.

Objets externes ou statiques :

Un objet comme une fonction, une variable, etc., peut être automatique, dynamique, statique ou externe. Nous connaissons déjà les variables automatiques et dynamiques. Nous nous intéressons ici plutôt aux variables définies en dehors d’une fonction, et aux fonctions elles-mêmes.

Si l’on souhaite qu’un tel objet soit utilisable dans tous les fichiers du projet, il faut le déclarer externe, en utilisant le mot-clé extern. Si l’on préfère que l’objet ne soit utilisable que pour les fonctions du fichier courant, il faut le déclarer statique avec le mot-clé static. Les fonctions, définitions de types et variables sont externes par défaut, les constantes statiques.

Imaginons par exemple que le fichier iosfract.cpp ait besoin de partager avec le programme principal mainfra.cpp une variable globale glob (indiquant par exemple la dernière erreur d’entrée-sortie produite). Dans ce cas, on n’écrira pas, dans le fichier iosfract.h :

int glob = 1;

sinon la variable serait dupliquée en deux exemplaires différents dans les deux fichiers objets (ce qui n’est pas le but recherché), et l’éditeur de liens signalerait une erreur. Il faut indiquer explicitement au compilateur que la variable glob est partagée entre deux fichiers objets, et que c’est l’éditeur de liens qui s’en occupera. Il faut donc placer dans le fichier d’en-tête une déclaration externe :

extern int glob;

sans initialisation, et dans l’un des deux fichiers objets (n’importe lequel) une déclaration statique avec initialisation (explicite ou implicite puisque les objets statiques sont toujours initialisés) de l’objet :

mainfra.cpp

#include "iosfract.h"                                  int glob = 1;                                  // ...

 

Ainsi, chaque fois que l’on fait référence à glob dans iosfract.cpp, le compilateur sait que la variable est en fait ailleurs, et place un lien que l’éditeur de liens se chargera de résoudre.

Lorsqu’un objet est externe, il ne doit être initialisé que dans le fichier objet qui le contient effectivement, sinon une erreur est produite. Précisons aussi qu’une variable peut être déclarée externe à l’intérieur d’une fonction, bien que ce soit d’un intérêt assez faible.

Déclarés en dehors d’une fonction, les variables et les types (y compris les classes) sont externes par défaut ; les fonctions également. Elles peuvent être déclarées statiques si elles ne sont utilisées que dans le fichier courant :

static void fonc(void) { ...

Dans ce cas, il est possible à plusieurs fichiers objets d’avoir une fonction nommée fonc sans entraîner de conflit, même si les fonctions sont différentes. En fait, l’éditeur de liens ne connaîtra pas le nom de ces fonctions statiques, qui n’est utilisé que par le compilateur et effacé en fin de fichier. La déclaration statique est donc utile pour des fonctions, des variables et des types qui n’ont pas à être utilisés ailleurs, car elle facilite le travail du compilateur et de l’éditeur de liens. Noter aussi qu’une fonction en ligne est inconnue de l’éditeur de liens (elle est toujours statique).

Les constantes sont statiques par défaut ; de la sorte, deux fichiers objets peuvent utiliser chacun leur version d’une constante sans problèmes. Cela permet aussi d’écrire une constante dans un fichier en-tête et d’inclure ce fichier dans plusieurs sources. Une constante peut être déclarée externe, si l’on souhaite l’initialiser ailleurs. Précisons toutefois que les tableaux constants (y compris les chaînes de caractères) ainsi que les classes et structures constantes sont, eux, externes par défaut (afin d’éviter une duplication de leur contenu dans les différents fichiers objets).

Les déclarations de types sont externes par défaut. On peut cependant les répéter dans plusieurs fichiers objets, à condition que ce soit de manière identique (en pratique en passant par un fichier en-tête) ; c’est ce que nous avons fait avec les types fraction et matrice_fra dans notre exemple.

D’une façon générale, on évitera de placer dans un fichier en-tête une définition de fonction (sauf en ligne), de variables (mais une déclaration externe est possible) et de tableaux, classes ou structures constants.

Annexes :

Liste des mots réservés de C++

Les mots suivants sont réservés en C++ :

asm

auto

break

case

catch

char

class

const

continue

default

delete

do

double

else

enum

extern

float

for

friend

goto

if

inline

int

long

new

operator

private

protected

public

register

return

short

signed

sizeof

static

struct

switch

template

this

typedef

union

unsigned

virtual

void

volatile

while

 

Les mots suivants sont de plus réservés en Turbo C++ :

cdecl _cs _ds _es _export far huge
interrupt _loadds near pascal _regparam
_saveregs _seg _ss

Les mots suivants sont réservés par d’autres compilateurs, il est préférable de ne pas les utiliser :

entry fortran handle _handle overload

Liste des opérateurs de C++ :

Les liens renvoient aux sections où ces opérateurs sont décrits. L’ordre indiqué est celui de précédence, indiquée aussi dans la deuxième colonne ; les opérateurs de précédence 15 ont une priorité plus grande que ceux de précédence 14, etc. Les opérateurs de priorité égale sont traités de gauche à droite ou de droite à gauche selon le sens indiqué. La colonne Nb.Op. indique le nombre d’opérandes ; ceux-ci sont également évalués dans le sens indiqué. Une description plus détaillée se trouve aux sections indiquées par les liens dans la colonne des noms.

Les opérateurs qui ne peuvent être redéfinis sont sur fond orange.

Opérateur

Préc.

Sens

Nb. Op.

Nom

Description

()

15

->

varie

Appel de fonction

Appelle la fonction dont le nom se trouve devant les parenthèses, avec les arguments contenus dans celles-ci.

[]

15

->

2

Indice de tableau

Renvoie un élément d’indice calculé entre crochets dans le tableau dont le nom se trouve devant ceux-ci.

->

15

->

2

Déréférencement + adressage

Déréférence le pointeur situé devant l’opérateur puis adresse le membre situé à droite.

::

15

->

1 ou 2

Résolution de portée

En opérateur unaire, devant un identificateur, indique une variable globale.
En opérateur binaire, limite la portée de recherche de l’identificateur situé à droite de l’opérateur à la classe située à gauche.

::*

15

->

2

Résolution de portée pour les pointeurs sur membre

Indique la classe pour un pointeur sur membre.

.

15

->

2

Adressage

Adresse le membre dont le nom suit le point dans la classe dont le nom précède.

!

14

<-

1

Négation logique

Change un booléen en son opposé.

~

14

<-

1

Négation par bits

Change tous les bits d’un entier en leur opposé.

+

14

<-

1

+ unaire

Aucun effet.

-

14

<-

1

- unaire

Change un nombre en son opposé.

++

14

<-

1

Incrémentation

Incrémente un entier ou un pointeur. Peut être placé devant ou derrière (actionavant ou après le reste des calculs).

--

14

<-

1

Décrémentation

Décrémente un entier ou un pointeur. Peut être placé devant ou derrière (actionavant ou après le reste des calculs).

&

14

<-

1

Référence

Renvoie un référence sur l’identificateur dont le nom suit.

*

14

<-

1

Déréférencement

Renvoie une référence sur la variable pointée par le pointeur dont le nom suit.

(type)

14

<-

1

Changement de type

Change le type de la variable située derrière la parenthèse fermante en celui indiqué entre parenthèses.

sizeof

14

<-

1

Taille

Taille du type donné en argument, ou du type de la variable donnée en argument.

new

14

<-

1

Allocation

Crée un bloc mémoire de taille adéquat pour stocker un objet, et renvoie un pointeur sur ce bloc.

delete

14

<-

1

Désallocation

Détruit un bloc créé par new.

.*

13

->

2

Adressage d’un pointeur sur membre

Appel d’une méthode de classe par l’intermédiaire d’un pointeur de fonction.

->*

13

->

2

Déréférencement + adressage d’un pointeur sur membre

Déréférence le pointeur, puis appelle une méthode de classe par l’intermédiaire d’un pointeur de fonction.

*

12

->

2

Multiplication

Multiplie les deux nombres.

/

12

->

2

Division

Divise le nombre de gauche par celui de droite.

%

12

->

2

Reste modulo

Donne le reste de la division euclidienne de l’entier de gauche par celui de droite.

+

11

->

2

Addition

Additionne les deux nombres.

-

11

->

2

Soustraction

Soustrait les deux nombres.

<<

10

->

2

Décalage à droite

Décale à droite les bits de l’entier précédent du nombre de bits indiqué par l’entier suivant l’opérateur, divisant ainsi par 2 puissance de cet entier.

>>

10

->

2

Décalage à gauche

Décale à gauche les bits de l’entier précédent du nombre de bits indiqué par l’entier suivant l’opérateur, multipliant ainsi par 2 puissance de cet entier.

<

9

->

2

Inférieur strict

Renvoie un booléen indiquant si le nombre de gauche est strictement inférieur à celui de droite.

<=

9

->

2

Inférieur large

Renvoie un booléen indiquant si le nombre de gauche est inférieur ou égal à celui de droite.

>

9

->

2

Supérieur strict

Renvoie un booléen indiquant si le nombre de gauche est strictement supérieur à celui de droite.

>=

9

->

2

Supérieur large

Renvoie un booléen indiquant si le nombre de gauche est supérieur ou égal à celui de droite.

==

8

->

2

Égal

Renvoie un booléen indiquant si les deux termes sont égaux.

!=

8

->

2

Différent

Renvoie un booléen indiquant si les deux termes sont différents.

&

7

->

2

ET par bits

Applique l’opération logique ET sur les bits des opérandes entiers. Ne pas utiliser avec les booléens (voir &&).

^

6

->

2

OU EXCLUSIF par bits

Applique l’opération logique OU EXCLUSIF sur les bits des opérandes entiers.

|

5

->

2

OU par bits

Applique l’opération logique OU sur les bits des opérandes entiers. Ne pas utiliser avec les booléens (voir ||).

&&

4

->

2

ET logique

ET logique entre deux booléens. Si le premier opérande est faux, le second n’est pas évalué.

||

3

->

2

OU logique

OU logique entre deux booléens. Si le premier opérande est vrai, le second n’est pas évalué.

? :

2

<-

3

Selon que

Évalue le booléen situé devant ?. Si le résultat est vrai renvoie le terme précédant :, sinon renvoie celui qui suit.

=

1

<-

2

Affectation

Calcule le terme de droite et place la valeur dans la variable désigné à gauche. Renvoie la valeur obtenue.

*= /=
%= += -=
&= ^= |=
<<= >>=

1

<-

2

Affection + opération

Effectue l’opération dont le symbole précède le signe =, entre la variable identifiée à gauche et le terme situé à droite, puis place le résultat dans cette variable. Renvoie la valeur obtenue.

,

0

->

2

Succession

Évalue le terme de gauche, puis celui de droite et renvoie ce dernier.

 

Par Missa Dioma - Publié dans : Informatique
Ecrire un commentaire - Voir les 0 commentaires
Retour à l'accueil

Présentation

Créer un Blog

Recherche

Calendrier

Mai 2012
L M M J V S D
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
<< < > >>
Créer un blog gratuit sur over-blog.com - Contact - C.G.U. - Rémunération en droits d'auteur - Signaler un abus