precedent retour  sommaire du cours C++ suivant

Le Langage C++

cours n° 3

Patrick TRAU 19/10/04

Cours n° 3

5) Les instructions et leur séquencement

5.1) définitions

Une instruction peut être :

Les instructions seront toujours exécutées dans l'ordre où elles ont été écrites, l'une après l'autre. Ce n'est qu'à l'intérieur des structures de contrôle que l'on peut modifier leur séquencement (leur ordre d'exécution), en permettant de refaire plusieurs fois certaines instructions (boucles), de passer directement d'un endroit à un autre du programme (branchements inconditionnels), de n'exécuter certaines instructions que dans certains cas (branchements conditionnels).

5.2) précision sur le « ; »

Le « ; » sert à indiquer la fin d'une instruction (rappel, le retour à la ligne n'indique rien de plus qu'un simple espace).

La première forme d'instruction est nécessairement terminée par un « ; ». Voici quelques exemples :

	a=5;                                //affectation simple
	x=4+2*(sin(3*PI)-tan(z));      //avec appel de fonctions
	cout<<"le résultat est"<<x<<" pour "<<y<<"\n"      //E/S
	lancer_sous_tache(a,b,c);         //appel sous-programme
	MonObjet.dessiner();         //application d'une méthode

Un cas particulier est l'instruction vide, qui se compose uniquement d'un ; (utilisé là où une instruction est nécessaire d'après la syntaxe).

Dans la seconde forme (structure de contrôle), je préciserai à chaque fois la position du « ; ».

Un bloc quand à lui n'a pas besoin d'être suivi d'un « ; », puisque l'accolade fermante délimite déjà la fin du bloc (par contre les instructions comprises à l'intérieur d'un bloc devront peut-être se terminer par un « ; », même celle juste avant l'accolade fermante).

6) Structures de contrôle : les boucles

Une boucle permet de répéter plusieurs fois un bloc d'instructions.

6.1) boucle while (tant que)

structure : while (expression) instruction

Tant que l'expression est vraie, on effectue l'instruction, qui peut être simple (terminée par ;), bloc (entre {}) ou vide (; seul). Il ne faut donc surtout pas de ; derrière la ), puisque dans ce cas seule l'instruction vide est répétée plusieurs fois, l 'instruction suivante ne sera faite qu'une seule fois. L'expression est au moins évaluée une fois (évaluer signifie « calculer la valeur »). Tant que l'expression est vraie, on effectue l'instruction, dès qu'elle est fausse, on passe à l'instruction suivante (si elle est fausse dès le début, l'instruction n'est jamais effectuée).

exemple :

#include <iostream.h>
int main(void)
 {
  float nombre,racine=0;
  cout<<"entrez un nombre réel entre 0 et 10";
  cin>>nombre;
  while (racine*racine<nombre) racine+=0.01;
  cout<<"la racine de"<<nombre<<"vaut"<<racine<<"à 1\% près\n";
 }

L'expression est le plus souvent formée de tests utilisant les opérateurs < (inférieur), > (supérieur), <= (inférieur ou égal), >= (supérieur ou égal), = = (égal, attention un seul signe = signifie affectation !), != (différent), qui peuvent être combinés par les opérateurs booléens && (et), || (ou) et ! (non). Exemples d'expressions valides :

	(a<100)
	(a>0 && a<=10)
	((a>0 && b>0) || c==0)

A la rigueur, l'expression peut être numérique, auquel cas on considère qu'elle est fausse si elle vaut 0, et qu'elle est vraie dans tous les autres cas.

Exercice (while_puiss) : faire un programme qui affiche toutes les puissances de 2, jusqu'à une valeur maximale donnée par l'utilisateur. On calculera la puissance par multiplications successives par 2.

Exercice (while_err) : que fait ce programme ?

#include <iostream.h>
#include <math.h>
#define debut 100
#define pas 0.01
int main(void)
 {
  float nombre=debut;
  int compte=0,tous_les;
  cout<<("afficher les résultats intermédiaires tous les ? (333 par exemple) ?");
  cin<<tous_les;
  while (fabs(nombre-(debut+(compte*pas)))<pas)
   {
    nombre+=pas;
    if (!(++compte%tous_les))
       cout<<"valeur obtenue"<<nombre<<", au lieu de"
       <<(float)(debut+(compte*pas))
       <<"en"<<compte<<"calculs\n";
   }
  cout<<"erreur de 100\% en"<<compte<<"calculs\n";
 }

6.2) boucle do while (faire tant que)

structure : do instruction while (expression); (attention au ; final)

Comme pour while, l'instruction est répétée tant que l'expression est vraie. Mais quoi qu'il arrive, l'instruction est au moins faite une fois, avant la première évaluation de l'expression.

exemple :

#include <iostream.h>
int main(void)
 {
  int a;
  do
   {
    cout<<"entrez le nombre 482";
    cin>>a;
   }
  while (a!=482);
  cout<<"c'est gentil de m'avoir obéi\n";
 }

Exercice (do_while) : écrivez un programme de jeu demandant de deviner un nombre entre 0 et 10 choisi par l'ordinateur. On ne donnera pas d'indications avant la découverte de la solution, où l'on indiquera le nombre d'essais. La solution sera choisie par l'ordinateur par la fonction rand() qui rend un entier aléatoire.

6.3) boucle for (pour)

structure : for (expr_initiale; expr_condition; expr_incrémentation) instruction

Cette boucle est surtout utilisée lorsque l'on connaît à l'avance le nombre d'itération à effectuer. L'expr_initiale est effectuée une fois, en premier. Puis on teste la condition. On effectue l'instruction puis l'incrémentation tant que la condition est vraie. L'instruction et l'incrémentation peuvent ne jamais être effectuées. La boucle est équivalente à :

    expr_initiale;
    while (expr_condition)
     {
      instruction
      expr_incrémentation;
     }

On peut remarquer qu'en C/C++ on compte en commençant par 0. Pour dire 10 fois bonjour, on écrira :

for(i=0;i<10;i++)cout<<"bonjour\n";

En fait, pendant qu'il dit la première fois bonjour, i vaut 0 (car il n'a pas encore fini la première fois). Puis, quand il a effectivement dit bonjour, i passe à 1 et il dit pour la seconde fois bonjour. Et ceci tant que i est strictement inférieur à 10 (donc jusqu'à 9 compris). Dès que i passe à 10, la boucle est arrêtée, et on passe à la suite.

Une ou plusieurs des trois expressions peuvent être omises, l'instruction peut être vide. for(;;); est donc une boucle infinie.

autre exemple :

      { char c; for(c='Z';c>='A';c--)cout<<c; }

Exercice (for) : faire un programme qui calcule la moyenne de N notes. N et les notes seront saisies par cin. Le calcul de la moyenne s'effectue en initialisant une variable à 0, y ajoutant progressivement les notes saisies puis division par N).

7) Structures de contrôle : les branchements conditionnels

On a souvent besoin de n'effectuer certaines instructions que dans certains cas. On dispose pour cela du IF et du SWITCH.

7.1) If - Else (Si - Sinon)

structure : if (expression) instruction1
ou : if (expression) instruction1 else instruction2

Si l'expression est vraie on effectue l'instruction1, puis on passe à la suite. Sinon, on effectue l'instruction 2 puis on passe à la suite (dans le cas sans else on passe directement à la suite).

Exercice (jeu) : modifier le jeu de l'exercice (do_while) en précisant au joueur à chaque essai si sa proposition est trop grande ou trop petite.

L'instruction d'un if peut être un autre if (imbriqué). Exemple :

     if(c1) i1;
     else if (c2) i2;
     else if (c3) i3;
     else i4;
     i5;

Explication : si c1 est vrai alors on exécute i1 puis i5, sinon mais si c2 alors i2 puis i5, ... Si ni c1 ni c2 ni c3 alors i4 puis i5.

Le else étant facultatif, il peut y avoir une ambiguïté s'il y a moins de else que de if. En fait, un else se rapporte toujours au if non terminé (c'est à dire à qui on n'a pas encore attribué de else) le plus proche. Une solution pour lever l'mbiguïté est de délimiter le if sans else en l'entourant de {}.

exemples :

7.2) Switch - Case (brancher - dans le cas)

Le switch est aussi appellé le « goto calculé » Cette structure de contrôle permet de remplacer une suite trop longue de if et else if (mais rien ne vous force à l'utiliser, vous pouvez parfaitement vous limiter aux ifs).

structure : 
	switch(expression_entière)
	{
		case cste1:instructions	
		case cste2:instructions
			........
		case csteN:instructions
		default :instructions
	}

L'expression ne peut être qu'entière (char, int, long). L'expression est évaluée, puis on passe directement au "case" correspondant à la valeur trouvée. Le cas default est facultatif, mais si il est prévu il doit être le dernier cas.

exemple : fonction vérifiant si son argument c est une voyelle.

int voyelle(char c)
 {
  switch(c)
   {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
    case 'y':return(1); /* 1=vrai */
    default :return(0)
   }
 }

Remarque : l'instruction break permet de passer directement à la fin d'un switch (au } ). Dans le cas de switch imbriqués on ne peut sortir que du switch intérieur.

exemple :
	switch (a)
           {
            case 1:inst1;inst2;....;break;
            case 2:....;break;
            default:.....
           } /*endroit où l'on arrive après un break */

Exercice (calcul) : faire un programme simulant une calculatrice 4 opérations.

8) Branchements inconditionnels (goto)

Nous en finissons ici avec les structures de contrôle. Ces instructions sont certes utiles, mais moins importantes, c'est pourquoi je ne les ai pas présentées en cours (peut-être s'il reste du temps en fin de semestre ? ). Quand on arrive sur une telle instruction, on se branche obligatoirement sur une autre partie du programme. Ces instructions sont à éviter si possible, car elles rendent le programme plus complexe à maintenir, le fait d'être dans une ligne de programme ne suffisant plus pour connaître immédiatement quelle instruction on a fait auparavant, et donc ne permet plus d'assurer que ce qui est au dessus est correctement terminé. Il ne faut les utiliser que dans certains cas simples.

8.1) goto (aller à)

La pratique des informaticiens a montré que l'utilisation des goto donne souvent des programmes non maintenables (impossibles à corriger ou modifier). Les problèmes qu'ils posent ont amené les programmeurs expérimentés à ne s'en servir qu'exceptionnellement.

structure : goto label;

Label est un identificateur (non déclaré, mais non utilisé pour autre chose), suivi de deux points (:), et indiquant la destination du saut. Un goto permet de sortir d'un bloc depuis n'importe quel endroit. Mais on ne peut entrer dans un bloc que par son { (qui créera proprement les variables locales du bloc).

{.....
 {.....
  goto truc;
  .....
 }
.....
truc:
.....
}

Les goto sont néanmoins acceptables si la destination du saut est un lieu « normal » de sortie (fin de boucle, de fonction...). C'est pourquoi on a créé les quatre instructions spécifiques ci après.

8.2) break (interrompre)

Il provoque la sortie immédiate de la boucle ou switch en cours. Il est limité à un seul niveau d'imbrication.

exemples :

do {if(i==0)break;....}while (i!=0);  /* un while aurait été mieux */
for (i=0;i<10;i++){....;if (erreur) break;} /* à remplacer par for(i=0;(i<10)&&(!erreur);i++){...} */

8.3) continue (continuer)

Cette instruction provoque le passage à la prochaine itération d'une boucle. Dans le cas d'un while ou do while, on saute vers l'évaluation du test de sortie de boucle. Dans le cas d'un for on passe à l'expression d'incrémentation puis seulement au test de bouclage. En cas de boucles imbriquées, il permet uniquement de continuer la boucle la plus interne.

exemple :

for (i=0;i<10;i++) {if (i==j) continue; ...... }

peut être remplacé par

for (i=0;i<10;i++) if (i!=j) { ...... }

8.4) return (retourner)

Permet de sortir de la fonction actuelle (y compris main), en se branchant à son dernier }. Return permet également (et surtout) de rendre la valeur résultat de la fonction.

structure: return; ou return(valeur);

exemple :

int max(int a, int b) {if (a>b) return(a); else return(b);}

8.5) exit (sortir)

Ceci n'est pas un mot clef du C mais une fonction disponible dans la plupart des compilateurs (définie par ANSI, dans stdlib.h). Elle permet de quitter directement le programme (même depuis une fonction). On peut lui donner comme argument le code de sortie (celui que l'on aurait donné à return dans main). Cette fonction libère la mémoire utilisée par le programme (variables + alloc) et ferme (sur beaucoup de compilateurs) les fichiers ouverts.

structure : exit() ou exit(code)

suivant retour  sommaire du cours C++ precedent Patrick TRAU, ULP - IPST octobre 04