Remédiation en C++

C++: Les structures de contrôle

Université de Bordeaux – Licence Ingénierie Mathématique

Les structures de contrôle, pour quoi faire ?

Dans tous les exemples de programmes que l’on a vu jusqu’à présent, les instructions étaient toutes effectuées, les unes après les autres, dans l’ordre de la lecture.

Cependant, ce n’est pas une manière très flexible de retranscrire des algorithmes. En effet, la plupart des algorithmes nécessitent qu’on répète plusieurs fois les mêmes opérations, avec très peu de différence à chaque fois. Aussi, il y a certaines opérations qu’on n’a envie d’effectuer que dans certaines conditions. Par exemple, on n’a pas très envie que le programme effectue une division par 0 en plein milieu du calcul !

C’est donc pour changer l’ordre d’exécution des instructions qu’on a besoin de mettre en place les structures de contrôle dans un programme.

On va passer en revue dans cette partie les structures de contrôle les plus fréquemment rencontrées en C++.

Exécution conditionnelle: if    if... else

Commençons par les blocs if. Ces blocs permettent d’effectuer des instructions uniquement si une condition est remplie.

Ils se présentent comme ceci:

if (expression booleenne)
  instruction ou bloc d'instructions;
  • Attention à ne pas oublier les parenthèses, elles sont obligatoires.
  • L’indentation (les espaces à gauche de l’instruction) n’est pas obligatoire, mais elle permet de mieux visualiser le code. Elle est donc très fortement conseillée.

L’expression booléenne est évaluée au moment où l’exécution arrive au if.

Si cette expression vaut true (ou est un entier différent de 0, souvenez-vous) alors l’instruction (ou le bloc, entre accolades) est réalisée.

Si l’expression vaut false (ou est un entier égal à 0), les instructions ne sont pas effectuées, et on passe à la suite.

Exécution conditionnelle: if    if... else

On peut rajouter une instruction ou un bloc d’instructions à effectuer si l’instruction est fausse. Le bloc est alors précédé du mot clé else:

if (expression booleenne)
  instruction ou bloc d'instructions 1;
else
  instruction ou bloc d'instructions 2;

Si l’expression est vraie, on effectue uniquement les instructions 1.

Si l’expression est fausse, on effectue uniquement les instructions 2.

Nous allons voir quelques exemples.

Exécution conditionnelle: if    if... else

Exemple: ne pas diviser par 0

int a=0,b=0,d;

// imaginez du code qui peut modifier les valeurs de a et b

// On teste si b est égal à 0
if (b==0) {
  // Cet affichage est fait si b est egal a 0
  std::cout << "Erreur: division par 0 impossible!" << std::endl;
}
else {
  // Dans le cas contraire, on peut diviser par b
  d = a/b;
}

Rappel : pour tester si l’entier b est nul, il faut écrire b==0, pas b=0 ! Le code compilera même si vous oubliez d’écrire ==.

“Mais alors, pourquoi ça compile ?” Eh bien, il se trouve que l’assignation est elle-même une opération qui en plus de mettre à jour la variable à gauche du signe =, retourne la valeur assignée.
Dans cet exemple, b=0 retourne donc 0, quelle que soit la valeur précédente de b. Comme 0 est équivalent à false, on ne fera jamais l’affichage d’erreur, et on rentrera dans le else. On divisera par 0, et il y aura très certainement un bug dans la suite du programme. Juste pour ce petit caractère = en moins ! Faites attention !

Exécution conditionnelle: if    if... else

Deuxième exemple: utiliser directement un booléen.

bool isOk = true;
// Imaginez du code, et si quelque chose se passe mal, isOk est changé à false

if (not isOk) {
  std::cout << "Impossible de faire X, car..." << std::endl;
}
else {
  // X
}

C’est assez pratique d’utiliser des booléens dont les noms décrivent une propriété. Ils commencent souvent par is... ou has.... Cela permet une lecture très facile des conditions de if.

Exécution conditionnelle: if    if... else

Dernier exemple : utiliser directement un entier

int count=0;
// ici des operations qui peuvent augmenter la valeur de count
if (count) {
  // est equivalent a
  // if (count !=0)
}

Cette utilisation est moins rencontrée, car moins claire à comprendre à la lecture.

Exécution conditionnelle: if    if... else

Quelques remarques sur l’écriture des if.

  • Quand il n’y a qu’une seule instruction après un if ou un else, on n’est pas obligé de mettre des accolades. Cependant certains préfèrent en mettre quoi qu’il arrive.
    • avantages : certains voient mieux l’algorithme comme ça. C’est plus facile de rajouter des instructions si nécessaire.
    • inconvénient : cela alourdit la lecture. À vous de voir.
  • Un if, ou une suite if ... else, est considéré comme une seule instruction. On peut alors faire des “cascades de if”. Sans les accolades qui devraient suivre immédiatement les mots else, cela peut se présenter comme ça:
if (expression 1) {
  //...
}
else if (expression 2) {
  //...
}
else if (expression 3) {
  //...
}
else {
  //...
}

Exécution conditionnelle: switch

Le switch est une autre façon d’exécuter des instructions sous certaines conditions. On peut notamment s’en servir à la place des casacades de if. Le bloc se présente comme ceci:

switch (expression entiere) {
  case valeur1:
    // instructions A
    break;
  case valeur2:
    // instructions B
    break;
  case valeur3:
    // instructions C
  case valeur4:
  case valeur5:
    // instructions D
    break;
  default:
    // instructions E
}

On ne peut utiliser le switch qu’avec des entiers ou des types associés aux entiers. Pas de réels, ni de chaînes de caractères, malheureusement !

Exécution conditionnelle: switch

La règle de fonctionnement est la suivante: la valeur de l’expression entière est évaluée. Ensuite, on va à la ligne case qui correspond à la valeur trouvée. On effectue alors toutes les instructions depuis cette ligne jusqu’à rencontrer un break, même si on recontre d’autres lignes case ou la ligne default.

switch (expression entiere) {
  case valeur1:
    // instructions A
    break;
  case valeur2:
    // instructions B
    break;
  case valeur3:
    // instructions C
  case valeur4:
  case valeur5:
    // instructions D
    break;
  default:
    // instructions E
}

Exécution conditionnelle: switch

Prenons un exemple (j’ai maintenant précisé les valeurs des case).

int a;
// imaginez qu'on modifie a avant le switch ...
switch (a) {
  case 0:
    // instructions A
    break;
  case 1:
    // instructions B
    break;
  case 2:
    // instructions C
  case 10:
  case 11:
    // instructions D
    break;
  default:
    // instructions E
}
  • Si a vaut 0, alors on effectue les instructions A, et seulement ces instructions, à cause du break à la ligne 6.
  • Si a vaut 1, alors on effectue les instructions B, et seulement ces instructions, à cause du break à la ligne 9.

Exécution conditionnelle: switch

int a;
// imaginez qu'on modifie a avant le switch ...
switch (a) {
  case 0:
    // instructions A
    break;
  case 1:
    // instructions B
    break;
  case 2:
    // instructions C
  case 10:
  case 11:
    // instructions D
    break;
  default:
    // instructions E
}
  • Si a vaut 2, alors on effectue les instructions C et D, car le break suivant n’est qu’à la ligne 15 !
  • Si a vaut 10 ou 11, on effectue les instructions D.
  • Si a a une valeur différente de 0, 1, 2, 10 ou 11, alors on effectue les instructions E.

Exécution conditionnelle: switch

Dernière remarque sur les switch. Ils ont une syntaxe un peu particulière par rapport aux blocs if et à tous ceux qu’on verra par la suite.

Même si il y a plusieurs instructions qui suivent un case, on n’est pas obligé de mettre des accolades. (Peut-être car ce sont les case qui servent de délimiteur ?). SAUF si on doit déclarer des nouvelles variables dans ces instructions. Dans ce cas il faut des accolades:

switch (a) {
  case truc:
    {
      int b;
    }
  default:
    // instructions
}

C’est tout pour les instructions conditionnelles, on va maintenant voir plusieurs structures de contrôle qui permettent de réaliser plusieurs itérations d’un algorithme sans devoir réécrire les instructions. Ces structures, on les appelle de manière générale des boucles.

Les boucles while et do while

La première boucle qu’on va voir est le while (“tant que” en français).

while (expression booleenne) {
  // instructions
}

Le principe est le même que pour le if, à la différence près qu’on ne sort jamais de la boucle tant que l’expression booléenne est vraie ! Cela veut notamment dire que si la valeur que donne l’expression ne change jamais, on est coincé dans ce qu’on appelle une boucle infinie.

Essayez vous même avec ce programme !

#include<iostream>
int main() {
  while (true) {
    std::cout << "Ha ha! ";
  }
  return 0;
}

Vous pouvez interrompre un programme à tout moment en faisant Ctrl+C.

Les boucles while et do while

Comme pour le bloc if, s’il y a qu’une seule instruction à effectuer dans le while, on peut retirer les accolades. C’est très rare pour les while.

Voici un exemple plus sérieux, on calcule les \(N\) premiers termes de la série harmonique:

  double somme = 0.;
  int    n     = 1;
  int    nMax  = 100;

  while (n<=nMax) {
    somme += 1./n;
    n++;
  }
  
  std::cout << "Somme des " << nMax << " premiers termes de la série harmonique : " << somme << std::endl;

Avant d’entrer dans le while, on intialise les variables qui serviront à contrôler la sortie de la boucle. Ici, n commence à la valeur 1. Comme 1 est inférieur ou égal à nMax qui vaut 100, la condition est remplie, on effectue les instructions.

Les instructions consistent à rajouter \(1/n\) à la somme (notez la division réelle!), et ensuite incrémenter la variable n de 1. somme vaut maintenant 1., et n vaut 2.

n est toujours inférieur à nMax, on effectue donc un passage supplémentaire dans la boucle, et ainsi de suite…

Quand n passe à 101, la condition n’est plus remplie, et on sort de la boucle, en passant à la suite du code.

Les boucles while et do while

La boucle do... while est une variante de la boucle while. La seule différence entre les deux boucles est qu’un do... while effectuera toujours un premier passage dans la boucle avant d’évaluer si la condition autorise à répéter les passages.

Cela se retrouve dans l’écriture:

do {
  // instructions
}  while (expression booleenne);
// attention au point virgule après le while

Les boucles for

Le deuxième type de boucles est le plus rencontré dans les codes C++. Il s’agit des boucles for.

Les boucles while permettent d’effectuer une grande variété de tâches, mais leur syntaxte est un peu lourde et piégeuse:

  • l’initialisation se fait en dehors du bloc d’instructions, avant,
  • on oublie très facilement de mettre à jour la condition, surtout quand la suite d’instructions est très longue. On crée trop facilement des boucles infinies.

La syntaxe du for permet de regrouper les éléments nécessaires à la boucle en une ligne:

for (initialisation ; condition ; incrementation)

Ce sont des points-virgules qui séparent les trois éléments, pas des virgules.

Les boucles for

for (initialisation ; condition ; incrementation)
  • À la place d’initialisation, on met les instructions qui donnent les premières valeurs aux variables qui serviront à contrôler la boucle.
  • La condition est celle que l’on retrouvait en test dans la boucle while.
  • L’incrémentation est ce qui permet de faire en sorte que la condition change de vrai à faux à un moment donné.

Il est toujours possible de convertir une boucle for en une boucle while et inversement.

L’exemple précédent de la série harmonique, mais écrit avec une boucle for, cela donne ça:

  double somme = 0.;
  int    nMax  = 100;

  for (int n=1; n<nMax; n++) 
    somme += 1./n;
  
  std::cout << "Somme des " << nMax << " premiers termes de la série harmonique : " << somme << std::endl;

Les boucles for

  double somme = 0.;
  int    nMax  = 100;

  for (int n=1; n<nMax; n++) 
    somme += 1./n;
  
  std::cout << "Somme des " << nMax << " premiers termes de la série harmonique : " << somme << std::endl;

Ici, on peut voir que j’ai déclaré l’entier n dans la partie initialisation du for. Cet entier n’existe que dans la boucle: une fois sorti du for, n n’est plus connu.

On peut cependant utiliser une variable qui a déjà été déclarée avant le for. Il suffit de lui donner une valeur de départ.

  double somme = 0.;
  int    n, nMax  = 100;

  for (n=1; n<nMax; n++) 
    somme += 1./n;
  
  // ici, n existe toujours

Les boucles for

Quelques remarques pour finir:

  • Comme pour les if et while, une boucle for avec une seule instruction n’a pas besoin d’accolades.
  • Les parties initialisation, condition et incrémentation du for peuvent être vides, mais ce n’est pas très utile.
  • Le mot clé continue permet de passer directement à l’itération suivante d’un for
  • Le mot clé break, déjà recontré dans les switch, permet aussi de sortir directement des boucles for, while et do while. Je déconseille de l’utiliser cependant, un break est très souvent synonyme d’une boucle mal écrite.

Comment choisir entre un while et un for ?

Il n’y a pas vraiment de règle pour cela, les deux sont équivalents. Cela dit, on voit que la syntaxe du for ne permet pas d’écrire des choses compliquées dans chacun des trois éléments qui le définissent.

J’ai souvent tendance à conseiller les for pour un nombre d’itérations connu, et les while dans le cas contraire. En général, les codes produits en suivant cette façon de faire sont assez clairs. (Mais c’est de toute façon toujours le cas, puisque vous commentez très bien vos codes, n’est-ce-pas ? ;) )

Exemples de programmes avec des structures de contrôle

Premier programme : dates

On va écrire un programme qui va valider ou non une date rentrée par l’utilisateur sous forme de trois entiers. La date est valide si le mois est compris entre 1 et 2, si le numero du jour correspond au mois donné, et on verra si l’année est bissextile ou non pour le 29 février.

Commençons déjà par demander à l’utilisateur de rentrer ces trois entiers.

On démarre toujours avec une fonction main:

#include <iostream>

int main() {
  return 0;
}

Exemples de programmes avec des structures de contrôle

Ensuite demandons les valeurs des entiers:

#include <iostream>

int main() {
  int jour  = -1;
  int mois  = -1;
  int annee = 0;
  std::cout << "Entrez trois entiers correspondant au jour, mois et année " << std::endl;
  std::cin >> jour >> mois >> annee;
  return 0;
}

Exemples de programmes avec des structures de contrôle

Nous allons maintenant tester le jour en fonction du mois. Il y a les mois à 31 jours (1,3,5,7,8,10,12), les mois à 30 jours (4,6,9,11), le mois à 28 ou 29 jours (2), et les mois qui n’existent pas (le reste des entiers).

Le choix le plus indiqué est une structure switch qui comparera la valeur de la variable mois. Pensez à bien mettre des break à la fin de chaque cas.

(Faites défiler pour voir tout le code)

#include <iostream>

int main() {
  int jour  = -1;
  int mois  = -1;
  int annee = 0;
  std::cout << "Entrez trois entiers correspondant au jour, mois et année " << std::endl;
  std::cin >> jour >> mois >> annee;
  
  // On effectue le test suivant le mois
  // mois a 31 jours : jan,mar,mai,juil,aout,oct,dec
  // mois a 30 jours : avr,juin,sept,nov
  // mois a 28/29 jours : fev
  switch (mois) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      // instructions
      break;
      
    case 4:
    case 6:
    case 9:
    case 11:
      // instructions
      break;
      
    case 2:
      // en fevrier c'est complique
      break;
      
    default:
      // non valide
  }
  
  return 0;
}

Exemples de programmes avec des structures de contrôle

Dans tous les cas, il faut déterminer si le jour est strictement plus grand que 0, et inférieur à un nombre max qui dépend du mois. C’est celui qu’on va modifier dans le switch.

Il y a les cas faciles à 30 et 31 jours:

(Faites défiler pour voir tout le code)

#include <iostream>

int main() {
  int jour  = -1;
  int mois  = -1;
  int annee = 0;
  std::cout << "Entrez trois entiers correspondant au jour, mois et année " << std::endl;
  std::cin >> jour >> mois >> annee;
  
  int jourMax = -1;
  
  // jourMax dépend du mois
  // mois a 31 jours : jan,mar,mai,juil,aout,oct,dec
  // mois a 30 jours : avr,juin,sept,nov
  // mois a 28/29 jours : fev
  switch (mois) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      jourMax = 31;
      break;
      
    case 4:
    case 6:
    case 9:
    case 11:
      jourMax = 30;
      break;
      
    case 2:
      // en fevrier c'est complique
      break;
    default:
      ;
      // on ne fait rien jourMax est a -1, le test ci-dessous sera faux
  }
  
  if (jour>0 and jour<=jourMax)
    std::cout << "La date est valide." << std::endl;
  else
    std::cout << "La date n'est pas valide." << std::endl;
  
  return 0;
}

Exemples de programmes avec des structures de contrôle

Il ne reste plus que février, et ses années bisextiles. Ce sont les années qui sont

  • soit multiple de 4 et pas multiple de 100,
  • soit multiple de 400.

On peut mettre toutes ces conditions dans le test du if, en faisant attention aux opérateurs logiques et aux parenthèses.

Quand un entier \(n\) est un multiple de \(p\), le reste de la division par \(p\) vaut 0. On utilise alors l’opérateur %.

Voilà donc le programme complet (Faites défiler pour voir tout le code)

#include <iostream>

int main() {
  int jour  = -1;
  int mois  = -1;
  int annee = 0;
  std::cout << "Entrez trois entiers correspondant au jour, mois et année " << std::endl;
  std::cin >> jour >> mois >> annee;
 
  int jourMax = -1; 
  
  // On effectue le test suivant le mois
  // mois a 31 jours : jan,mar,mai,juil,aout,oct,dec
  // mois a 30 jours : avr,juin,sept,nov
  // mois a 28/29 jours : fev
  switch (mois) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      jourMax = 31;
      break;
      
    case 4:
    case 6:
    case 9:
    case 11:
      jourMax = 30;
      break;
      
    case 2:
      // annees bisextiles, multiples de 400 ou multiple de 4 execpte multiple de 100
      if (annee%400==0 or (annee%4==0 and annee%100!=0))
        jourMax = 29;
      else
        jourMax = 28;
      break;
      
    default:
      ;
      // on ne fait rien, jourMax est a -1, le test ci-dessous sera faux
  }
  
  if (jour>0 and jour<=jourMax)
    std::cout << "La date est valide." << std::endl;
  else
    std::cout << "La date n'est pas valide." << std::endl;
  
  return 0;
}

Exemples de programmes avec des structures de contrôle

Règle d’or : toujours tester ses programmes !

Ceci est un exemple de mauvais test, j’aurai dû conscieusement tester tous les cas possibles !

Exemples de programmes avec des structures de contrôle

Second programme : notes

Ce second programme lira des notes rentrées par l’utilisateur, entre 0 et 20. Dès l’instant que l’utilisateur aura rentré une note qui n’est pas entre 0 et 20, le programme affichera la moyenne des notes rentrées précédemment, ainsi que la note minimum et la note maximum.

Petit problème. Nous n’avons pas encore vu les tableaux, on ne pas encore stocker toutes les variables que l’utlisateur va rentrer, d’autant plus qu’on ne sait pas combien il va en rentrer ! Mais on va pouvoir mettre à jour au fur et à mesure les informations dont on a besoin.

Pour la moyenne, on initialisera une somme, et on ajoutera chaque saisie à cette somme. Il nous faudra aussi un compteur pour diviser le total à la fin.

C’est parti.

Second programme : notes

Comme d’habitude, on commence avec un main. On met aussi l’affichage basique dont on aura ici besoin. On déclare aussi un réel qui contiendra la note actuellement saisie.

#include <iostream>

int main() {
  double note;
  
  std::cout << "Entrez svp une serie de notes entre 0 et 20. Une note en dehors de l'intervalle finit la serie." << std::endl;
  std::cin >> note;
  
  return 0;
}

Second programme : notes

Ensuite, on peut déclarer et initialiser le compteur de notes valides. On peut aussi déjà traiter le cas pathologique où aucune note correcte n’a été saisie. N’oubliez jamais de traiter ces cas dans vos programmes. Une règle de prudence est de ne jamais faire confiance à l’utilisateur! À vous de voir jusqu’où vous poussez ces tests.

#include <iostream>

int main() {
  double note;
  int    nNotes = 0;
  
  std::cout << "Entrez svp une serie de notes entre 0 et 20. Une note en dehors de l'intervalle finit la serie." << std::endl;
  std::cin >> note;
  
  // ici on testera les notes
  // ....
  
  if (nNotes==0)
    std::cout << "Aucune note entre 0 et 20 n'a ete rentree! Impossible d'afficher les resultats." << std::endl;  
  else {
    // affichage des resultats
  }
  
  return 0;
}

Second programme : notes

Initialisons maintenant les autres valeurs qui serviront au suivi des notes. Si la première note est valide, alors elle est plus petite que 20. Initialisons donc la note minimale à une valeur plus grande. Et inversement pour la note max. Pour la moyenne, on peut initialiser la somme à 0. (Les sommes s’initialisent à 0, les produits à 1). (Faites défiler pour voir tout le code)

#include <iostream>

int main() {
  double note;
  int    nNotes = 0;
  
  double noteMin = 21.;
  double noteMax = -1.;
  double somme   = 0.;
  
  std::cout << "Entrez svp une serie de notes entre 0 et 20. Une note en dehors de l'intervalle finit la serie." << std::endl;
  std::cin >> note;
  
  // ici on testera les notes
  // ....
  
  if (nNotes==0)
    std::cout << "Aucune note entre 0 et 20 n'a ete rentree! Impossible d'afficher les resultats." << std::endl;  
  else {
    // affichage des resultats
  }
  
  return 0;
}

Second programme : notes

Maintenant testons les notes valides à l’aide d’un bloc if. (Faites défiler pour voir tout le code)

#include <iostream>

int main() {
  double note;
  int    nNotes = 0;
  
  double noteMin = 21.;
  double noteMax = -1.;
  double somme   = 0.;
  
  std::cout << "Entrez svp une serie de notes entre 0 et 20. Une note en dehors de l'intervalle finit la serie." << std::endl;
  std::cin >> note;
  
  if (note>0 and note<20.) {
    // on fait des trucs 
    
  }
    
  if (nNotes==0)
    std::cout << "Aucune note entre 0 et 20 n'a ete rentree! Impossible d'afficher les resultats." << std::endl;  
  else {
    // affichage des resultats
  }
  
  return 0;
}

Second programme : notes

Dans ce bloc if, la note est valide. Mettons à jour les infos. Pour le min et le max, on compare à la valeur précédente du min et du max:

#include <iostream>

int main() {
  double note;
  int    nNotes = 0;
  
  double noteMin = 21.;
  double noteMax = -1.;
  double somme   = 0.;
  
  std::cout << "Entrez svp une serie de notes entre 0 et 20. Une note en dehors de l'intervalle finit la serie." << std::endl;
  std::cin >> note;
  
  if (note>0 and note<20.) {
    nNotes++;
    somme += note;
    if (note < noteMin)
      noteMin = note;
    if (note > noteMax)
      noteMax = note;
  }
    
  if (nNotes==0)
    std::cout << "Aucune note entre 0 et 20 n'a ete rentree! Impossible d'afficher les resultats." << std::endl;  
  else {
    // affichage des resultats
  }
  
  return 0;
}

Second programme : notes

La première note est rentrée, on lit maintenant la suivante, et on a envie de répéter les opérations tant que la note est valide. C’est une indication qu’il faut utiliser un while ! On peut en fait transformer le if qu’on a écrit portant sur la valeur de la note, et faire mettre à jour la valeur de celle-ci dans la boucle:

#include <iostream>

int main() {
  double note;
  int    nNotes = 0;
  
  double noteMin = 21.;
  double noteMax = -1.;
  double somme   = 0.;
  
  std::cout << "Entrez svp une serie de notes entre 0 et 20. Une note en dehors de l'intervalle finit la serie." << std::endl;
  std::cin >> note;
  
  // On continue tant que la note est correcte
  while (note>0 and note<20.) {
    // On met a jour les infos
    nNotes++;
    somme += note;
    if (note < noteMin)
      noteMin = note;
    if (note > noteMax)
      noteMax = note;
    // On lit une nouvelle valeur de la note
    std::cin >> note;
  }
    
  if (nNotes==0)
    std::cout << "Aucune note entre 0 et 20 n'a ete rentree! Impossible d'afficher les resultats." << std::endl;  
  else {
    // affichage des resultats
  }
  
  return 0;
}

Second programme : notes

Il n’y a plus qu’à finaliser l’affichage des résultats dans le cas où il y a quelque chose à calculer. (Faites défiler pour voir tout le code)

#include <iostream>

int main() {
  double note;
  int    nNotes = 0;
  
  double noteMin = 21.;
  double noteMax = -1.;
  double somme   = 0.;
  
  std::cout << "Entrez svp une serie de notes entre 0 et 20. Une note en dehors de l'intervalle finit la serie." << std::endl;
  std::cin >> note;
  
  // On continue tant que la note est correcte
  while (note>0 and note<20.) {
    // On met a jour les infos
    nNotes++;
    somme += note;
    if (note < noteMin)
      noteMin = note;
    if (note > noteMax)
      noteMax = note;
    // On lit une nouvelle valeur de la note
    std::cin >> note;
  }
    
  if (nNotes==0)
    std::cout << "Aucune note entre 0 et 20 n'a ete rentree! Impossible d'afficher les resultats." << std::endl;  
  else {
    std::cout << "Les notes sont comprises entre " << noteMin << " et " << noteMax << std::endl;
    std::cout << "et la note moyenne est de " << somme/nNotes << std::endl; // somme est un double: division reelle
  }
  
  return 0;
}

Second programme : notes

Passons au test:

Maintenant quelques petites QCM.

QCM

Dans la parenthèse suivant for, on trouve les éléments suivants:

(incrémentation ; condition ; initialisation)

(initialisation ; condition ; indentation)

(initialisation ; condition ; incrémentation)

(initialisation ; incrémentation ; condition)


QCM

Dans la parenthèse suivant for, on trouve les éléments suivants:

(incrémentation ; condition ; initialisation)

(initialisation ; condition ; indentation)

(initialisation ; condition ; incrémentation)

(initialisation ; incrémentation ; condition)


QCM

int k=0;
for (int i=0;i*i<1000;i++)
  k++;
  std::cout << k << std::endl;

Que fait le code ci-dessus ?

Affiche le carré des nombres entiers inférieurs à 1000.

Affiche les entiers dont le carré est inférieur à 1000

Affiche le nombre d’entiers dont le carré est inférieur à 1000


QCM

int k=0;
for (int i=0;i*i<1000;i++)
  k++;
  std::cout << k << std::endl;

Que fait le code ci-dessus ?

Affiche le carré des nombres entiers inférieurs à 1000.

Affiche les entiers dont le carré est inférieur à 1000

Affiche le nombre d’entiers dont le carré est inférieur à 1000


Attention au piège de l’indentation. Il n’y a pas d’accolades après le for, donc seule l’instruction k++ est effectuée dans la boucle.

QCM

int nStars = 0;
for (int nStars=0; nStars<20; nStars++)
  std::cout << "*";
std::cout << std::endl << "Number of stars " 
          << nStars << std::endl;

 

Pourquoi cette partie de programme me dit-elle qu’aucune étoile n’a été affichée ?

Il y a deux variables nStars.
 
La variable incrémentée revient à son état initial à la sortie du for.

Il n’y a que de l’affichage qui est fait, rien n’est modifié.


 

QCM

int nStars = 0;
for (int nStars=0; nStars<20; nStars++)
  std::cout << "*";
std::cout << std::endl << "Number of stars " 
          << nStars << std::endl;

 

Pourquoi cette partie de programme me dit-elle qu’aucune étoile n’a été affichée ?

Il y a deux variables nStars.
 
La variable incrémentée revient à son état initial à la sortie du for.

Il n’y a que de l’affichage qui est fait, rien n’est modifié.

Le bloc d’initialisation du for contient la déclaration d’une variable. Même si une variable avec le même nom existait déjà, c’est une nouvelle variable qui sert pendant la boucle. Cette nouvelle variable est détruite à la sortie du bloc. L’ancienne est restée inchangée.

Retour au cours Moodle