La syntaxe du C++

UJF - Licence de Physique

[ Home| Syntaxe C++| Fichiers| Classes I| Classes II| Graphisme ]


1 Affichage à l'écran et lecture du clavier

La gestion des entrée/sortie avec les périphériques standards que sont le clavier et l'écran se fond, en C++, au moyen de 2 classes, istream et ostream. Ces classes sont définies dans le fichier iostream, qu'il faut inclure au début du programme. Nous reviendrons en détail plus tard sur ce qu'est une classe ; mais le but de cette partie est juste de vous monter comment on peut lire le clavier et écrire sur l'écran.

1.1 Affichage

    #include < iostream >
    using namespace std; 
    int main() // entête du programme principal 
    { 
      // début 
      int a,b;  // déclaration des objets a et b de la classe int
      a=1;      // affectation: a prend la valeur 1 
      b=a+1;    // affectation b prend la valeur 2 
      int c;    // déclaration de l'objet c de classe int
      c=a+b;    // affectation: c prend la valeur a+b c'est à dire 3 
      cout<<"la somme de "<< a <<" et "<< b << " = "<< c << endl;// affichage à l'écran. 
      return 0;// fin 
    }  

Remarques

Remarques plus techniques

  1. Jusqu'à il y a peu de temps, il existait un certains nombre de classes prédéfinies qui portaient le même nom, faisaient plus ou moins la même chose mais rendait la portabilité des programmes hasardeuses. Il a été décidé de standardiser ces classes en utilisant la STL (Standard Template Library). Afin de preciser qu'on utilise des classes de la STL, les fichiers inclus (#include <name.h>) n'ont plus l'extension .h (donc #include <name>) et on ajoute using namespace std;
  2. iostream est un fichier déjà présent sur l'ordinateur. Ce fichier contient des informations sur des commandes C++ d'affichage à l'écran et de saisie de touches appuyées au clavier. iostream vient de l'anglais: Input (entrée), Output (sortie), Stream (flux d'information).
  3. Dans la commande #include, si le fichier à inclure est entre < >, cela signifie que le compilateur va chercher ce fichier dans un répertoire particulier, prédéfini. On peut inclure des fichiers en remplaçant les < > par des " " : si aucun chemin n'est spécifié entre les guillemets, alors le fichier doit se trouver dans le répertoire courant.
  4. cout est un objet C++ de la classe ostream. Cet objet est associé à l'écran comme expliqué ci-dessus. Le signe << est un opérateur de cette classe à qui on a donné le sens précis d'afficher à l'écran. Tout cela est contenu dans le fichier iostream qui est lui même un programme C++. L'existence de cette classe simplifie donc la programmation pour les suivants. C'est l'esprit du C++. Vous apprendrez à écrire vous même des classes et des opérateurs par la suite.

1.2 Lire le clavier

Il peut être intéressant que l'utilisateur puisse lui-même entrer les valeurs de a et b. Pour cela l'ordinateur doit attendre que l'utilisateur entre les données au clavier et les valide par la touche "entrée".

    # include < iostream >
    using namespace std; 
    int main()    // entête du programme principal 
    { 
      // début 
      int a,b;                  // déclaration des objets a et b de la classe int 
      cout << "Quelle est la valeur de a ?" << flush; 
      cin >> a;                 // lire a au clavier, attendre return 
      cout << "Quelle est la valeur de b?" << flush; 
      cin >> b;                 // entrer b au clavier puis return 
      int c;                    // déclaration de la objet c de la classe int 
      c=a+b;                    // affectation: c prend la valeur a+b 
      cout << "la somme de " << a << " et " << b << " vaut " << c << endl ;  // affichage à l'écran. 
      return 0;// fin 
    }

Remarques

2 Déclaration et affectation des objets

2.1 Les déclarations d'objets de base

Pour stocker une information dans un programme, on utilise un objet qui est symbolisée par une lettre ou un mot. (Comme a,b,c précédemment). On choisit la classe de cet objet, selon la nature de l'information que l'on veut stocker (nombre entier, ou nombre à virgule, nombre complexe, ou série de lettres, ou matrice,...).

Voici les différentes classes de base qui existent en C++ :



Déclaration: classe objet; signification Limites
int a; entier voir remarque 1
float c; réel ±10±38 à 10-7 près
double d; réel double précision ±10±308 à 10-13 près
char e; caractère 256 caractères
char *f; chaîne de caractère  


Remarques

  1. Les limites des objets dépendent du nombre d'octets (ensemble de 8 bits) utilisés pour leur stockage. Les caractères sont stockés sur 1 octet, soit 28 = 256 caractères au total. Pour les entiers, cela dépend des systèmes d'exploitation ; sur certains (DOS,Windows) ils sont stockés sur 2 octets (limites=-215 à 215 - 1), sur d'autres (HP, linux) sur 4 octets (limites=-231 à 231 - 1) et même parfois sur 8 octets (DEC).
  2. Quel est l'intéret d'utiliser float plutôt que double, puisque ce dernier est de toutes façons plus précis ? Réponse : stocker un objet de la classe double dans la mémoire de l'ordinateur (ou dans un fichier) prend plus de place. Par contre l'ordinateur calcule aussi vite avec float qu'avec double, car les processeurs actuels sont structurés pour cela. Donc si économiser de la place mémoire est crucial pour votre programme, et que la précision n'est pas nécessaire, (mais cela est rare) il est préférable d'utiliser float, sinon utilisez (en général) double. Ayez le réflexe "double"...
  3. Les classes ci-dessus sont les classes de base standard en C++ qui existent en C. Dans le vocabulaire du C, on dirait plutôt type à la place de classe, et variable à la place de objet. Par exemple en écrivant double d; on dirait que d est une variable du type double.
  4. Vous pouvez utiliser d'autres classes plus élaborées (qui ne sont plus des classes de base), comme les entiers à précision illimitée, les complexes, les vecteurs, les matrices,... à condition cette fois ci d'inclure le fichier ".h" approprié au début du programme.

2.2 Portée des objets

Il existe 2 grandes catégories d'objets, les objets globaux et les objets locaux. Les objets globaux se déclarent juste après les includes. Ils sont connus dans tous les blocs qui les suivent. Ils sont, comme nous l'avons déjà dit, à éviter. Les objets locaux ne vivent que dans le bloc ({...}) dans lequel ils ont été déclarés ; ils sont détruits dès la sortie du bloc.

2.3 Initialisation d'un objet de base

On peut déclarer un objet et l'initialiser en même temps, de différentes manières :

    int i=0; 
    float Pi=3.14; 
    double pi=3.1415,x1=3.15e1;
    double y=x1;
    char mon_caractere_prefere = 'X';
    char *texte="que dire de plus?"; 

i est un int qui vaut 0, Pi un float qui vaut 3.14, pi est un double qui vaut 3.1415, x1 est un double qui vaut 31.5, etc.

Remarques

  1. Dans le nom des objets, le langage C++ fait la différence entre les majuscules et les minuscules. On peut choisir tous les caractères de l'alphabet, utiliser des chiffres et le caractère "_". Cependant un nom ne peut commencer par un chiffre et ne peut contenir de caractères spéciaux (lettre accentuée, opérateurs, caractères de ponctuation, ...).
  2. On peut initialiser un objet avec un objet déjà existant comme y=x1. Un caractère est entre le signe quote " ' ", comme 'X'. Un texte (chaîne de caractères) est entre guillemets " " ".
  3. On pourra bien sûr modifier ces valeurs dans la suite du programme.

2.4 Complément : Précision à l'affichage

(il faut inclure <iomanip>)

    double c=3.14159; 
    cout << setprecision(3) << c << endl;  

Affichera : 3.14

2.5 Conversions de classe

On peut affecter un objet à partir d'un objet d'une autre classe, lorsque cela a un sens (i.e. lorsque ça a été prédéfini). On parle de conversion ou cast. Ces cast peuvent avoir lieu sur des objets ou des pointeurs (la conversion d'un pointeur d'une classe en un pointeur d'une autre classe est très utilisée).

    int i; 
    float f=3.14,g; 
    i= int(f);                // convertion de float à int. i contiendra 3.
    cout << i << endl;
    g=float(i);               // conversion de int à float. 
    cout << int('A');         // Conversion de char à int. Affichera 65 à l'écran qui est le code ASCII de A 
    cout << char(65) << endl; // Conversion de int à char. Affichera A à l'écran qui est le caractère qui a le code 65.  

Remarque Importante

Certaines conversions peuvent être faites de façon implicite, c'est-à-dire sans spécifier la classe. Cependant ceci ne peut se faire sans risque dans n'importe quelle situation. Essayer le programme ci-dessous :

    #include < iostream >
    using namespace std; 
    int main() 
    { 
      float f=3.14,f1,f2; 
      int i=1,j,k,l; 
      char c='B',c1,c2;  

      f1=i/2*1.0; 
      f2=1.0*i/2; 
      cout << "f1=f2?" << endl;   
      cout << f1 << " " << f2 << endl;  

      j=f;    
      cout << "Conversion implicite de float->int" << endl; 
      cout << j << endl;  

      k=c; 
      cout << "Conversion implicite de char->int" << endl; 
      cout << k << endl;  

      l=65; 
      c1=l; 
      c2=l+256; 
      cout << "Conversion implicite de int->char" << endl; 
      cout << c1 << " " << c2 << endl; 
    } 

3 Les instructions de base

3.1 Les Conditions

Une condition est quelque chose qui est vrai ou faux. En C++, comme en C, faux est synonyme de 0 et toutes les autres valeurs numériques sont vraies. Souvent, pour des raisons de lisibilité, on utilise 1 pour vrai. Voici la syntaxe générale qui permet d'obtenir une expression vraie ou fausse.

Les opérateurs de comparaison



Signification symbole
supérieur à >
inférieur à <
supérieur ou égal à >=
inférieur ou égal à <=
égal à ==
différent de !=


Attention à la confusion possible : a==2 sert à comparer l'objet a avec 2 (et ne change pas la valeur de a). Par contre a=2 met la valeur 2 dans l'objet a.

Les opérateurs logiques



Signification symbole
et logique &&
ou logique ||
non logique !


Remarque

Lorsqu'une condition est évaluée comme i < 30, la valeur rendue est de classe entier (int). Elle est différente de 0 si la condition est vraie et 0 sinon.

3.2 Les Boucles

for( initialisation ; condition ; incrémentation) {instructions}

La syntaxe de la boucle for dans l'exemple ci-dessous signifie : initialise i à 10 ; si i <= 30 , exécute les instructions du bloc {...} qui suit le for, ajoute 2 à i et si i <= 30 recommence.

    #include <iostream>
    using namespace std; 
    int main( ) 
    { 
      int i; 
      int j; 
      for(i=10;i<=30;i=i+2) 
      { 
        j=i*i; 
        cout << i << "\t" << j << endl; // le caractère \t est une tabulation
      } 
    }

Ce programme produira:

    10   100
    12   144
    14   196

etc...

    30   900 

do { instructions} while ( condition);

signifie : fait le bloc d'instructions tant que la condition est vraie.

    #include <iostream>
    using namespace std;  
    int main( ) 
    { 
      int MAX=11; 
      int i=1,j; 
      do 
      { 
        j=i*i; 
        cout << i << "\t" << j << endl; 
        i=i+1; // Ne pas oublier l'incrémentation 
      } 
      while(i < MAX); // i < MAX est la condition 
    }  

produira :

    1   1
    2   4

etc...

    10  100 

while (condition) { instructions }

signifie : tant que la condition est vraie fait le bloc d'instructions.

    #include <iostream>
    using namespace std;  
    int main ( ) 
    { 
      int MAX=11; 
      int i=1, j; 
      while(i < MAX) // condition 
      { 
         j=i*i; 
         cout << i << "\t" << j << endl; 
         i=i+1; // ne pas oublier l'incrémentation 
       }
    }  

produira :

    1   1
    2   4

etc...

    10  100 

Remarque sur les boucles

On peut forcer la sortie d'une boucle avant que la condition de fin soit réalisée en utilisant la commande break. Nous en verrons un exemple au paragraphe suivant.

3.3 if (condition) {instructions} else {instructions}

    #include <iostream>
    using namespace std;  
    int main() 
    { 
       int i=5,j; 
       char test='N'; 
       while(test!='O') // tant que la valeur de test est différente de 'O' 
       { 
          cout << "entrer un nombre entre 1 et 10 " << endl; 
          cin>>j; 
         if(j>10) 
           {
              cout << "Comme tu ne sais pas lire je refuse de jouer avec toi" << endl;
              break; //on sort du while
           }
           if(i==j) 
           { 
               cout << "Bravo vous avez trouvé le nombre mystérieux!" << endl; 
               test='O'; 
           } 
           else 
             cout << "Non ce n'est pas le bon nombre" << endl; 
       } // fin du while 
    } // du programme  

Exercice : Résolution d'une équation du second degré

Ecrire un petit programme qui

Complément : Exercice sur les nombres aléatoires

Faire un programme qui au départ choisit un nombre au hasard entre 0 et 1000 (se servir de la section suivante), puis demande à l'utilisateur de le trouver, en répondant "trop grand" ou "trop petit" à chaque essai. L'utilisateur a droit a un nombre limité d'essais.

Pour générer un nombre entier p aléatoire, entre 0 et N inclus,

il faut ces 2 fichiers :

    #include <ctime> 
    #include <cstdlib> 

Puis dans le programme, l'instruction suivante ne doit apparaître qu'une seule fois; elle permet d'initialiser les tirages à partir de la date.

    srand(time(NULL));

Puis au moment de choisir un nombre au hasard :

    p=rand()%(N+1);

Explications : rand() génère un nombre entier aléatoire entre 0 et MAX_INT (le plus grand entier). Ensuite % signifie modulo, a % b est le reste de la division de a par b. Ainsi rand()%(N+1) est le reste de la division du nombre choisi par rand() par N+1. C'est donc un entier compris entre 0 et N (inclus), ce que l'on veut.

3.4 switch

L'instruction switch est un peu analogue au if ; elle peut être remplacée par une succession de if...else... mais elle présente l'avantage d'être plus légère à écrire et à relire. La syntaxe générale est de la forme :

    switch(var) 
    { 
       case value1: instruction1; break; 
       case value2: instruction2; break; 
       case value3: instruction3;
       case value4: instruction4; break; 
       default: instruction_defaut; break; 
    } 

var est soit de type int soit de type char et valueX est du type de var. Selon la valeur de var, faire telles ou telles instructions. Dans l'exemple précédant, si var=value1 on ne fera que les instruction1 ; idem pour value2 et value4. Par contre si var=value3 on effectue instruction3 et instruction4 car il n'y a pas de break. Enfin si var n'est égale à aucune des valueX, on fait instruction_defaut. Cette dernière ligne est facultative.

4 Les tableaux

Un tableau est suite d'objets de la même classe.

4.1 déclaration

La façon de déclarer un tableau est la suivante:

    int tab1[100];  // tab1 est un tableau de 100 cases contenant chacune un objet de la classe entier 
    float tab2[20]; // tab2 est un tableau de 20 cases contenant chacune un objet de classe réel 
    char tab3[10];  // tab3 est un tableau de 10 cases contant chacune un objet de classe char. On dit aussi que c'est une chaîne de 10 caractères 

Attention : la taille du tableau doit être un nombre constant ; ça ne peut pas être la valeur d'un objet (int). Si l'on veut créer un tableau de taille variable, voir la section pointeurs.

4.2 Affectation

Pour mettre la valeur 3 dans la case numéro 0, on écrit naturellement :

    tab1[0]=3;

Attention : si on déclare int tab1[N], les N cases sont numérotées de 0 à N-1. Si on se trompe, si on écrit dans une case hors de limites, cela peut être la cause du plantage de votre programme. Sachez que la plupart des bugs que vous risquez de créer proviendront de ce problème.

Exercice

Complétez les signes "?" dans le programme suivant

    //pour calculer le produit scalaire de deux vecteurs
    #include < iostream >
    using namespace std;  
    const int dim=3; //declaration d'une constante de type entier
    int main() 
    { 
      double v1[dim], v2[dim],ps; 
      int i;
      v1[?]=1; v1[?]=2; v1[?]=3;
      v2[?]=3; v2[?]=-2; v2[?]=1;
      ps=0.;
      for (i= ? ;i< ? ;i=i+1) 
          ps=ps+v1[i]*v2[i]; 
       cout << "le produit scalaire est :" << ps << endl ; 
    }  

Remarques

    double v1[3]={1.,2.,3.}

5 Les pointeurs

La notion de pointeur est importante dans le langage C et C++. Elle est réputée comme étant difficile et technique; nous espérons que vous aurez néanmoins les idées claires après la lecture de cette section.

Nous introduisons rapidement la notion de pointeur, et montrons comme exemple, son intérêt pour créer des tableaux de taille variable au cours du programme.

5.1 Déclaration et affectation d'un pointeur

Rappelons déjà ce qu'est un objet. Par exemple :

    double i;

Cette instruction a pour effet de réserver une "case" en mémoire de l'ordinateur, permettant de stocker un nombre reél. Cette case s'appelle i. Bien sûr, cette case se trouve quelque part dans la mémoire de l'ordinateur. Elle a un certain emplacement, caractérisée par son adresse, appelée pointeur. l faut donc retenir que pointeur d'un objet signifie adresse d'un objet dans la mémoire de l'ordinateur.

On peut avoir accès à l'adresse de la "case" i en faisant :

    double i=2.;  // déclare l'objet i et affecte la valeur 2. 
    double *p;   // déclaration du pointeur p sur un double
    p=&i;     // p devient l'adresse de i  

Grâce au signe *, la deuxième ligne déclare p comme étant un pointeur sur un double (désignant une case mémoire qui est sensée contenir un réel). Remarquons que p est en fait un objet de type double*. On peut écrire double* p ou double *p ; cependant par convention on utilise plutôt le seconde notation.

Le signe&i signifie l'adresse de l'objet i. Donc la troisième ligne met dans p l'adresse de l'objet i.


pointeur

Voici une représentation de la mémoire de l'ordinateur :


adresse des cases contenu de la case nom de variable
10000 ...  
10004 10012 p
10008 ...  
10012 2. i
10016 ...  

Pour afficher le contenu de l'objet i on a maintenant deux possibilités :

    cout << i;    // possibilite 1
    cout << (*p); // possibilite 2

qui sont équivalentes car (*p) signifie le contenu de la "case" où pointe p.

    cout << p; 

donne l'adresse pointée par p (i.e. celle de i).

Pour modifier le contenu de l'objet i on a maintenant deux possibilités:

    i=5.3;     // possibilite 1
    (*p)=5.3;  // possibilite 2

qui sont équivalentes.

Attention :

avant d'effectuer l'opération d'écriture : (*p)=5.3, il faut être sur que l'adresse du pointeur correspond à une "case" existante. Vous avez donc compris que avant d'écrire, il faut prendre le soin de réserver de la place mémoire.

Les Tableaux statiques

En fait vous avez déjà utilisé des pointeurs... En effet lors de la déclaration du tableau tab

    float tab[6]; 

vous réservez 6 cases mémoires de type float. Comme la dimension de ces tableaux est forcément constante, on parle de tableau statique. La variable tab n'est rien d'autre qu'un pointeur sur un float. Ainsi l'instruction tab[0]=2.3 est parfaitement équivalente à *tab=2.3 et tab[1]=2.5 est parfaitement équivalente à *(tab+1)=2.5.

Ceci explique pourquoi les tableaux ont leurs cases numérotées de 0 à N-1 :*(tab+i) signifie "je saute i cases" (dont la taille dépend du type du pointeur).

Il est possible de déclarer des tableaux de plusieurs dimensions :

    double tab[10][10];

L'élément tab[i][j] correspond par exemple à la ligne i et la colonne j d'une matrice.

5.2 Allocation dynamique de la mémoire

Il est possible de réserver n cases mémoires, par l'instruction :

    int n; 
    cout << "entrez n" << flush;
    cin >> n;
    float *pValeur; //on déclare le pointeur pValeur
    pValeur=new float[n]; // on réserve n cases mémoire de la classe float  

Cela s'appelle une allocation dynamique de la mémoire, car elle est faite au cours de l'exécution du programme et non par le compilateur comme dans les tableaux statiques. C'est la commande new qui permet de faire ceci.

Après cela, pValeur pointe sur la première case réservée. On peut légitimement écrire dans cette première case par l'instruction :

    (*pValeur)=1;  

ou (ce qui est équivalent)

    pValeur[0]=1;  

On peut de même écrire dans la case suivante par :

    *(pValeur+1)=2;  

ou

    pValeur[1]=2;  

etc... jusqu'à pValeur[n-1].

ponteurs 2

A la fin de l'utilisation, n'oubliez pas de libérer l'emplacement mémoire par l'instruction :

    delete [] pValeur; 

Remarques :