Tous droits réservés

Une super mémoire

Vous savez désormais afficher des messages à l’écran. C’est un bon début, mais on est quand même assez limités, n’est-ce pas ? Rassurez-vous, C++ nous permet de faire bien plus que ça. Grâce à ce langage, nous allons nous amuser avec cette super calculatrice qu’est notre ordinateur et nous découvrirons qu’il possède aussi une super mémoire !

Ce chapitre va nous faire découvrir les littéraux et les variables.

Les littéraux

Que sont les littéraux ? Une valeur écrite littéralement dans le code, d’où son nom. Nous avons rencontré un littéral dans le chapitre précédent : "Hello World !". Ce littéral est ce qu’on appelle une chaîne de caractères. Mais ce n’est pas le seule littéral possible en C++. Examinons tout ça.

Les caractères

Nous avons fait connaissance avec les chaînes de caractères lors du chapitre précédent. Les chaînes de caractères ne sont rien d’autre que du texte. On les reconnait parce qu’elles commencent et finissent par des doubles guillemets ".

Entraînez-vous donc à afficher des chaînes de caractères, par exemple un littéral souhaitant une bonne journée.

#include <iostream>

int main()
{
    std::cout << "Bonne journée à toi lecteur." << std::endl;
    return 0;
}

Tous comme les mots de la langue française sont des regroupements de lettres, les chaînes de caractères sont une suite de caractères simples. En C++, un caractère est encadré par des guillemets simples '.

#include <iostream>

int main()
{
    // Un caractère peut être une lettre.
    std::cout << 'A' << std::endl;
    // Ou bien un chiffre.
    std::cout << '7' << std::endl;
    // Ou même de la ponctuation.
    std::cout << '!' << std::endl;
        
    return 0;
}

Les chaînes de caractères permettent de regrouper des caractères et nous facilitent ainsi la vie pour écrire du texte.

#include <iostream>

int main()
{
    // Que c'est fastidieux d'écrire ça !
    std::cout << 'S' << 'a' << 'l' << 'u' << 't' << ' ' << 't' << 'o' << 'i' << ' ' << '!' << std::endl;
    // C'est tellement mieux comme ça.
    std::cout << "Salut toi !" << std::endl;

    return 0;
}

J’ai mis plusieurs caractères entre guillemets simples, comme ceci 'abc' et mon code compile. Ça veut dire que ça marche ?

Si vous avez essayé de compiler, vous avez remarqué un message en rouge dans la fenêtre de résultat disant warning: multi-character character constant, ainsi qu’un nombre au lieu de nos caractères.

Le warning est un message d’avertissement que le compilateur vous envoie, car il ne sait pas si ce que vous avez fait est une erreur d’inattention ou bien une manœuvre volontaire de votre part. Dans notre cas, la norme C++ n’interdit pas de faire ça, mais ce n’est pas considéré comme une bonne pratique, comme étant du bon code.

N’ignorez pas les warnings !

Les warnings sont des messages importants signalant d’éventuels problèmes. Il ne faut surtout pas les ignorer sous prétexte que le code compile !

Des caractères spéciaux

Il existe quelques caractères qui sont un peu particuliers et, pour les introduire, je vais vous demander d’afficher un message contenant un chemin de dossier Windows (C:\Program Files (x86) par exemple).

#include <iostream>

int main()
{
    std::cout << "Dossier principal : C:\Program Files (x86)" << std::endl;

    return 0;
}

Boum ba da boum ! Le compilateur vous crache à la figure un message de type warning: unknown escape sequence: '\P'. Que s’est-il passé ?

Il existe en C++ des séries de caractères, appelées séquences d’échappement, qui commencent toutes par \ et dont la liste complète se trouve ici. Et pour comprendre l’intérêt de ces séquences d’échappement, essayez donc de compiler le code suivant.

#include <iostream>

int main()
{
    std::cout << "Il m'a demandé " Comment vas-tu ? "" << std::endl;

    return 0;
}

Vous devez certainement avoir une erreur proche de celle-ci : 'error: expected ';' before 'Comment'. C’est tout à fait normal. En effet, nous avons vu plus haut que les chaînes de caractères sont délimitées par les doubles guillemets "". Donc tout ce qui est entre cette paire de guillemets est considéré par le compilateur comme faisant partie de la chaîne de caractères.

Dans notre exemple, cela veut dire que Comment vas-tu ? ne fait plus partie de la chaîne. Donc le compilateur l’interprète comme des instructions C++ et comme il ne les comprend pas, il ne peut pas compiler le programme.

Mais quel rapport avec ces fameux caractères commençant par \ ?

Eh bien, les séquences d’échappement permettent de dire au compilateur « Écoute, ce caractère est spécial, il faut l’afficher et non l’interpréter », ce qui permet d’afficher des doubles guillemets dans une chaîne de caractères par exemple.

#include <iostream>

int main()
{
    std::cout << "Il m'a demandé \" Comment vas-tu ? \"" << std::endl;
    std::cout << "Dossier principal : C:\\Program Files (x86)" << std::endl;

    return 0;
}

De toutes les séquences d’échappement qui existe, les plus utilisées et celles que vous verrez le plus souvent sont les suivantes.

  • \' qui permet d’afficher un guillemet simple '.
  • \" qui permet d’afficher un guillemet double ".
  • \n qui permet d’aller à la ligne, comme std::endl.
  • \t qui permet de faire une tabulation horizontale.
  • \\ qui permet d’afficher un antislash \.

Entraînez-vous donc en affichant un guillemet simple sans échappement, puis avec échappement. Et faites donc mumuse avec les tabulations et les retours à la ligne.

#include <iostream>

int main()
{
    std::cout << "Voici un exemple sans échappement : " << "'" << " est bien affiché." << std::endl;
    std::cout << "Maintenant, je vais l'échapper : " << '\'' << " est bien affiché grâce à l'échappement." << std::endl;
    
    std::cout << "La suite, après la tabulation : \tJe suis loin." << std::endl;
    std::cout << "Ces mots sont sur une ligne.\nEt ceux là sur la suivante.\n";

    return 0;
}

Les nombres

Il existe d’autres types de littéraux en C++ : les nombres entiers et les nombres à virgule, appelés flottants.

std::cout << -1 << std::endl;
std::cout << 0 << std::endl;
std::cout << 1 << std::endl;

std::cout << -1.6027 << std::endl;
std::cout << 3.14159 << std::endl;
std::cout << 2.71828 << std::endl;

On remarque déjà qu’on peut utiliser des nombres négatifs sans aucun problème. Ensuite, dû à l’origine américaine de C++, les flottants s’écrivent avec un point et non une virgule. Même si l’on se fiche des chiffres après la virgule, il faut mettre le point . sinon C++ interprétera le nombre comme un entier.

Sinon, rien de bien étonnant, ce sont des nombres et on peut faire des opérations dessus.

#include <iostream>

int main()
{
    std::cout << "1 + 2 = " << 1 + 2 << std::endl;
    std::cout << "1 - 4 = " << 1 - 4 << std::endl;
    std::cout << "7.5 * 2 = " << 7.5 * 2 << std::endl;
    
    std::cout << "9 / 2 = " << 9 / 2 << std::endl;
    std::cout << "9. / 2 = " << 9. / 2 << std::endl;
    std::cout << "9 % 2 = " << 9 % 2 << std::endl;

    return 0;
}

Attends, qu’est-ce que c’est que ça ? 9 / 2 et 9. / 2 ne donne pas la même chose ? Et c’est quoi % ?

En fait, la raison est très simple : pour C++, si on fait une opération sur deux nombres entiers, le résultat est un nombre entier. Si l’on veut que le résultat soit un nombre à virgule, il faut qu’au moins un des deux nombres soit un flottant.

Dans le cas de la division, si les deux nombres sont des entiers, on obtient le quotient de la division euclidienne, le reste s’obtenant avec l’opérateur %, appelé modulo (c’est de l’arithmétique). Si l’un des deux nombres est un flottant, alors on obtient le résultat de la division réelle, donc un nombre réel.

Également, comme en maths, les calculs respectent la distributivité, l'associativité, la commutativité et la priorité des opérateurs. L’exemple suivant est tiré du cours de @gbdivers.

#include <iostream>

int main()
{
    std::cout << "Commutativité :" << std::endl;
    std::cout << "2 + 3 = " << 2 + 3 << std::endl;
    std::cout << "3 + 2 = " << 3 + 2 << std::endl;
 
    std::cout << "Associativité :" << std::endl;
    std::cout << "2 + (3 + 4) = " << 2 + (3 + 4) << std::endl;
    std::cout << "(2 + 3) + 4 = " << (2 + 3) + 4 << std::endl;
 
    std::cout << "Distributivité :" << std::endl;
    std::cout << "2 * (4 + 3) = " << 2 * (4 + 3) << std::endl;
    std::cout << "2 * 4 + 2 * 3 = " << 2 * 4 + 2 * 3 << std::endl;
    
    std::cout << "Priorité des opérateurs :" << std::endl;
    std::cout << "2 * 6 + 3 = " << 2 * 6 + 3 << std::endl;
    std::cout << "2 * (6 + 3) = " << 2 * (6 + 3) << std::endl;
    std::cout << "2 * 4 - 6 / 2 = " << 2 * 4 - 6 / 2 << std::endl;

    return 0;
}

Nous nous sommes amusés avec les littéraux, mais on est quand même rapidement limité. Imaginons que nous voulions multiplier le résultat précédent par 2 et ce, trois fois. Nous sommes obligés d’écrire un code comme ceci.

#include <iostream>

int main()
{
    std::cout << "1 * 2 = " << 1 * 2 << std::endl;
    std::cout << "1 * 2 * 2 = " << 1 * 2 * 2 << std::endl;
    std::cout << "1 * 2 * 2  * 2 = " << 1 * 2 * 2 * 2 << std::endl;

    return 0;
}

Maintenant, imaginons qu’on ne multiplie plus par 2 mais par 4, et non plus trois fois mais cinq. Vous visualisez bien tous les changements à faire ? C’est pénible, n’est-ce pas ? N’y a-t-il pas un moyen de se souvenir de valeurs et de calculs ?

Les variables

La réponse à la question soulevée dans la section précédente se trouve dans le titre. Il s’agit des variables. C’est un concept commun à beaucoup de langages de programmation qui permet de stocker une valeur et de lui associer un nom, afin de faciliter tant l’écriture que la lecture du code. On peut les voir comme des enveloppes, des tiroirs, des conteneurs, bref, une zone où est stockée une valeur, à laquelle on associe un nom.

Elle est où, cette « zone de stockage » ?

Toutes les variables sont stockées dans la mémoire vive de l’ordinateur, la RAM. Son fonctionnement est un peu complexe et ne nous intéresse pas (dans le cadre de ce cours tout du moins). Les curieux trouveront beaucoup de ressources sur Internet pour combler leur soif de savoir, allant de la vulgarisation simple à des explications complètes.

Comment créer des variables en C++ ?

Pour déclarer une variable en C++, il faut trois éléments obligatoires.

  • D’abord, un type, qui indique ce que la variable va stocker (un entier, une chaîne de caractères, etc).
  • Ensuite, un identificateur, c’est-à-dire le nom associé à la variable.
  • Enfin, il faut bien donner une valeur à stocker à notre variable. Ceci se fait en mettant cette valeur entre accolades { }.

Examinons un cas réel que nous détaillerons.

#include <iostream>
#include <string>

int main()
{
    int reponse { 42 };
    std::cout << "La réponse à la Grande Question est " << reponse << std::endl;

    double pi { 3.1415926 };
    std::cout << "Voici la valeur du célèbre nombre pi : " << pi << std::endl;
    
    char lettre { 'A' };
    std::cout << "La première lettre de l'alphabet français est " << lettre << std::endl; 
    
    std::string phrase { "Bonjour tout le monde !" };
    std::cout << "En entrant dans la salle, il s'écria : " << phrase << std::endl;
    
    return 0;
}
La réponse à la Grande Question est 42
Voici la valeur du célèbre nombre pi : 3.14159
La première lettre de l'alphabet français est A
En entrant dans la salle, il s'écria : Bonjour tout le monde !
Syntaxe héritée

Il existe une syntaxe alternative, de la forme type identificateur = valeur;. Essayez, vous verrez que ça marche.

#include <iostream>

int main()
{
    int variable = 42;
    std::cout << variable << std::endl;

    return 0;
}

Cette syntaxe est héritée du C. Elle est toujours valable en C++, ne soyez donc pas surpris si vous la voyez un jour dans des codes divers. Dans ce cours, nous utiliserons la forme dite « moderne » utilisant les accolades {}.

C’est tout à fait mon type

Les plus attentifs ont remarqué que l’on retrouve les mêmes littéraux que dans la section précédente.

  • Pour les nombres entiers, nous avons le mot-clé int, abréviation de l’anglais integer signifiant … nombre entier. Grâce à ce type, on peut stocker des entiers négatifs ou positifs.
  • Pour les flottants, les nombres à virgule, nous avons le mot-clé double, qui permet de stocker des nombres réels très grands.
  • Pour les caractères simples, nous avons char, qui est tellement transparent que je ne vous ferai pas l’affront de le traduire.
  • Enfin, pour les chaînes de caractères, nous avons le type std::string. Ce type est un peu particulier car il n’existe pas nativement en C++. Ce sont des programmeurs experts qui l’ont codé afin de manipuler aisément des chaînes de caractères. Afin de pouvoir manipuler des std::string, il faut donc inclure le bon fichier, ce que l’on fait grâce à la ligne #include <string>.

Quel est ton nom, petite variable ?

D’autre, puristes de la langue française, auront remarqué qu’il manque l’accent aigu dans l’identificateur reponse. Ce n’est pas un oubli de ma part. Si je suis obligé de faire cette entorse à notre belle langue, c’est parce que C++ m’y force. Étant un langage inventé aux États-Unis dans les années 1980, C++ n’autorise que les 26 lettres composant l’alphabet anglais (plus les chiffres et l’underscore _), à savoir donc les mêmes que les nôtres, accents non compris. Dans la réalité, les programmeurs utilisent l’anglais pour nommer leurs variables, ce n’est donc pas un problème.

Langue du cours

Je n’utilise le français que dans le cadre de votre apprentissage. Quand vous aurez plus d’expérience, je vous encouragerai à utiliser l’anglais.

Mais que ce soit en anglais ou en français, un identificateur doit respecter des règles bien précises.

  • Il doit commencer par une lettre. Il ne peut pas commencer par un chiffre, c’est interdit. Il ne doit pas commencer non plus par un underscore _ : leur utilisation répond à des règles précises, donc il est plus simple de ne pas les employer comme premier caractère.
  • Les espaces et les signes de ponctuation sont interdits (', ?, etc).
  • On ne peut pas utiliser un mot-clé du langage comme identificateur. Ainsi, il est interdit de déclarer une variable s’appelant int ou return, par exemple.
#include <iostream>

int main()
{
    // Bon exemple.
    int avec_underscore { 0 };
    // Erreur : espace interdit.
    int avec espace { 0 };
    
    // Bon exemple.
    int variable1 { 42 };
    // Erreur : ne peut pas commencer par un chiffre.
    int 1variable { 42 };
    
    // Bon exemple.
    char lettre { 'A' };
    // Erreur : ponctuation interdite.
    char autre_lettre! { 'B' };
    
    // Bon exemple.
    double retour { 2.71 };
    // Erreur : mot-clé réservé par C++.
    double return { 2.71 };
    
    return 0;
}

De l’importance d’un beau nom

Donner un nom clair et précis à une variable est un grand défi auquel sont confrontés même les programmeurs ayant de l’expérience. En effet, même si un identificateur respecte les règles imposées par C++, cela ne veut pas dire que la variable est bien nommée.

  • variable : ne veut rien dire. Que stocke-t-elle ? Quel est son but ? Pourquoi existe-t-elle ?
  • variable_contenant_la_multiplication_de_pi_par_e_au_cube : beaucoup trop long.
  • cIrCoNfErEnCe_CeRcLe : le nom définit bien le pourquoi de l’existence de notre variable, mais le mélange majuscule / minuscule le rend illisible.
  • Brzęczyszczykiewicz : aucun sens, sauf si vous aimez l’humour polonais.

Avec le temps, en progressant, vous arriverez à trouver plus rapidement et plus facilement des identificateurs qui soient clairs et simples. Dans le cours, je m’efforcerai de le faire afin de vous donner des exemples concrets.

Sinon, par défaut…

Abordons un dernier point. Je vous ai dit qu’on donne une valeur entre accolades pour initialiser notre variable. Mais que se passe-t-il si on écrit simplement les accolades, sans aucune valeur dedans ? Le code va-t-il toujours compiler ?

#include <iostream>
#include <string>

int main()
{
    int entier {};
    std::cout << "Mon entier vaut " << entier << std::endl;

    double reel {};
    std::cout << "Mon réel vaut " << reel << std::endl;

    char lettre {};
    std::cout << "Mon caractère vaut " << lettre << std::endl;

    std::string phrase {};
    std::cout << "Ma chaîne vaut " << phrase << std::endl;

    return 0;
}
Mon entier vaut 0
Mon réel vaut 0
Mon caractère vaut
Ma chaîne vaut

Non, le programme n’a pas de bugs, il fonctionne très bien. En fait, si aucune valeur n’est spécifiée, nos variables sont initialisées à une valeur par défaut. Pour les entiers et les réels, il s’agit de zéro. Pour les caractères, c’est une valeur spéciale signifiant « pas de caractère », de même que pour les chaînes de caractères, initialisées avec du vide.

À vous de choisir

C++ vous laisse libre de vos choix. Dans ce cours, pour commencer, nous écrirons toujours les valeurs explicitement. Au fur et à mesure que vous progresserez, nous utiliserons de plus en plus souvent l’initialisation par défaut.

Un peu de constance, voyons !

Terminons cette partie en présentant un mot-clé important qui a pour nom const. Il permet d’empêcher toute modification de la variable sur laquelle ce mot-clé s’applique. On dit qu’on crée une constante. Et si l’on essaye de modifier une constante, le compilateur refuse clair et net.

int main()
{
    double const pi { 3.141592 };
    pi = 4; // Ouch, badaboum ça ne compile pas.

    return 0;
}
[Visual Studio]
C3892 'pi' : vous ne pouvez pas assigner une variable const.

------------------------------------------------------------

[GCC]
prog.cc: In function 'int main()':
prog.cc:4:8: error: assignment of read-only variable 'pi'
    4 |     pi = 4; // Ouch, badaboum ça ne compile pas.
      |     ~~~^~~

------------------------------------------------------------

[Clang]
prog.cc:4:8: error: cannot assign to variable 'pi' with const-qualified type 'const double'
    pi = 4; // Ouch, badaboum ça ne compile pas.
    ~~ ^
prog.cc:3:18: note: variable 'pi' declared const here
    double const pi { 3.141592 };
    ~~~~~~~~~~~~~^~~~~~~~~~~~~~~
1 error generated.

Certains choisissent de les écrire entièrement en majuscule pour bien les différencier des autres variables. Ce n’est absolument pas une obligation et nous n’imposons sur ce point aucune règle.

Quelle en est l’utilité ? Il sert pour la qualité du code. Tout ce qui n’est pas destiné à être modifié est ainsi protégé. Cela nous permet également de donner des garanties fortes à notre code. C’est pour ça que nous allons l’utiliser dans tous les exemples. Vous verrez plus tard dans le cours d’autres cas utiles.

Ordre du const

Vous verrez souvent des codes qui inversent l’ordre de const et écrivent const int constante {};. Cela est légal et possible en C++, car const respecte une règle simple : il s’applique à ce qui est à sa gauche immédiate, sauf s’il n’y a rien, auquel cas il s’applique à ce qu’il y a à droite. Ici, cela ne change rien, mais plus tard cela aura des conséquences. Nous verrons cela en temps voulu.

Manipulation de variables

Nous ne sommes absolument pas obligés de nous contenter d’afficher nos variables. Au contraire, nous pouvons faire beaucoup d’opérations dessus, notamment combiner les variables entre elles et modifier leur valeur.

#include <iostream>

int main()
{
    int entier { 4 };
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    // Je peux tout à fait changer la valeur de ma variable.
    entier = 4 * (8 + 9) - 1;
    std::cout << "Finalement non, il vaut : " << entier << std::endl;
    
    // Je peux même utiliser la valeur de ma variable et la réaffecter à la même variable.
    entier = entier + 7;
    std::cout << "Et si j'additionne 7 ? " << entier << std::endl;
    
    int autre_entier { entier * 2 };
    // Je peux utiliser d'autres variables également.
    entier = autre_entier - 4;
    std::cout << "Finalement, je décide que mon entier a une autre valeur qui est : " << entier << std::endl;
    
    return 0;
}

Pour modifier la valeur d’une variable, on utilise l’opérateur d’affectation =, précédé de l’identificateur de la variable et suivi de la valeur à affecter : identificateur = valeur;. Et comme vous l’avez noté, cette valeur peut être un simple littéral, un calcul plus complexe, une autre variable voire la variable actuelle elle-même.

En effet, C++ s’occupe d’abord de tout ce qui se trouve à droite du signe égal. Ainsi, dans la ligne entier = entier + 7, il récupère l’ancienne valeur de entier, lui ajoute 7 et assigne ce nouveau résultat à entier. Essayez donc pour voir.

#include <iostream>

int main()
{
    int entier { 4 };
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier = entier + 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier = entier - 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier = entier * 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier = entier / 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    return 0;
}

Vous souvenez-vous de l’exemple que nous avions vu à la fin de la section sur les littéraux ? Nous faisions des multiplications, mais le fait de devoir répéter de nombreuses fois les mêmes instructions était lourd. Tenez, entraînez-vous donc en faisant un programme qui utilise les variables.

#include <iostream>

int main()
{
    int nombre { 1 };
    int const multiplicateur { 2 };
    
    nombre = nombre * multiplicateur;
    std::cout << "Maintenant, mon nombre vaut " << nombre << std::endl;
    
    nombre = nombre * multiplicateur;
    std::cout << "Maintenant, mon nombre vaut " << nombre << std::endl;
    
    nombre = nombre * multiplicateur;
    std::cout << "Maintenant, mon nombre vaut " << nombre << std::endl;
    
    return 0;
}

Vous remarquerez qu’on répète quand même plusieurs fois les mêmes instructions. Ce n’est pas grave. Nous verrons très bientôt comment remédier à ce problème. L’essentiel est que vous ayez pu voir l’avantage d’utiliser des variables.

Prenons un raccourci

Les programmeurs étant fainéants par nature, ils n’aiment pas la répétition. Ils ont donc créé des raccourcis pour aller plus vite. Ainsi, plutôt que d’écrire a = a + b, on peut écrire directement a += b. Cette nouvelle syntaxe s’applique aux opérateurs d’addition +, de soustraction -, de multiplication *, de division / et de modulo %.

#include <iostream>

int main()
{
    int entier { 4 };
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier += 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier -= 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier *= 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier /= 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    entier %= 2;
    std::cout << "Mon entier vaut : " << entier << std::endl;
    
    return 0;
}

Pire, non contents de ça, ils ont raccourci encore la syntaxe dans le cas où l’on souhaite ajouter ou soustraire 1 à un nombre. On parle d'incrémentation et de décrémentation. Il suffit de faire ainsi.

  • a++ ou ++a pour incrémenter de 1 la valeur de la variable.
  • a-- ou --a pour décrémenter de 1 la valeur de la variable.

Les deux syntaxes sont quasiment équivalentes, subsiste une différence mineure que je passe sous silence car elle ne nous intéresse pas à l’heure actuelle.

Qu'en déduisez-vous ?

Lorsque nous déclarons une variable, comme vous le savez maintenant, nous précisons, entre autres, son type et sa valeur. Sauf que cette information est parfois redondante. Regardez le code d’illustration ci-dessous.

int main()
{
    // Comme 0 est déjà un littéral entier, pourquoi donc préciser 'int' ?
    int entier { 0 };
    
    // Comme 3.1415 est déjà un littéral flottant, pourquoi donc préciser 'double' ?
    double reel { 3.1415 };

    return 0;
}

Le compilateur peut en effet déduire tout seul le type d’une variable et cela grâce à un nouveau mot-clef : auto. Celui-ci s’utilise en lieu et place du type de la variable. Notre code précédent peut ainsi être réécrit de la manière qui suit.

int main()
{
    // Hop, le compilateur déduit que 'entier' est de type 'int'.
    auto entier { 0 };
    
    // Hop, le compilateur déduit que 'reel' est de type 'double'.
    auto reel { 3.1415 };

    return 0;
}

Avec const

Tout comme dans le cas d’une variable dont le type est explicitement écrit, si vous voulez déclarer une constante, il suffit de rajouter const.

int main()
{
    auto const constante { 40 };
    return 0;
}

Le cas de std::string

Dans le cas des chaînes de caractères, c’est un peu particulier. Déjà, il faut rajouter un s après les guillemets fermants ", et ce pour chaque chaîne de caractères que vous voulez écrire. Ensuite, il faut rajouter une ligne spéciale. Nous expliquerons pourquoi plus loin dans ce cours. L’exemple suivant va vous aider à comprendre.

#include <string>

int main()
{
    // Écrivez cette ligne une seule fois.
    using namespace std::literals;

    // Puis vous pouvez déclarer autant de chaînes de caractères que vous voulez.
    auto chaine { "Du texte."s };
    auto autre_chaine { "Texte alternatif."s };
    auto encore_une_chaine { "Allez, un dernier pour la route."s };

    return 0;
}
Histoire et héritage

Si nous sommes obligés de rajouter le s en fin de littéral, c’est parce que nous avons affaire ici à un héritage du C. Sans celui-ci, le compilateur déduit que notre littéral est une chaîne de caractères comme en C et non comme étant une std::string. Nous en reparlerons au moment voulu, mais retenez que ce s est très important dès que vous utilisez auto.

Les avantages

Utiliser auto présente deux avantages majeurs.

  • D’abord, utiliser auto permet de raccourcir le code. Là, remplacer double par auto n’est pas un gain de place énorme, mais, plus tard dans ce cours, nous verrons des types longs à écrire et lourds à lire, qu’il sera bien plus agréable de remplacer par un concis auto.
  • Ensuite, cela permet un code plus évolutif. Imaginez des calculs complexes avec des types écrits explicitement. Si vous voulez changer l’un des types, il faut en changer chaque occurrence. Avec auto, vous laissez au compilateur le soin de s’occuper de cette tâche.

Je suis perdu. Qu’est-ce que je dois choisir entre auto ou le type explicite ?

C++ vous autorise à faire les deux, donc le choix est vôtre. Certains programmeurs ne jurent que par auto et l’utilisent partout. D’autres préfèrent le réserver aux types longs ou complexes et écrire explicitement les types simples et courts. Dans le cadre de ce cours, j’utiliserai les deux façons de faire.

Les entrées

Dès le premier code C++ que vous avez étudié, il y avait la notion de sortie standard. Il est temps de voir le concept inverse en manipulant l’entrée standard. Maintenant que nous connaissons le concept des variables, demander des informations à l’utilisateur est à notre portée. Il faut en effet pouvoir stocker quelque part la valeur qu’a tapée l’utilisateur, ce que permettent justement les variables.

Manipuler les entrées est un peu particulier avec Wandbox. Il faut cliquer sur l’encadré stdin et taper la valeur que vous souhaitez rentrer. C’est moins pratique qu’avec un IDE. Peu importe ce que vous préférez, l’essentiel est que vous puissiez manipuler pour comprendre la suite du chapitre.

#include <iostream>

int main()
{
    std::cout << "Entre ton age : " << std::endl;
    int age { 0 };
    std::cin >> age;
    std::cout << "Tu as " << age << " ans.\n";

    return 0;
}

Avez-vous remarqué à quel point cin est semblable à cout ? Déjà, pour l’utiliser, il faut le préfixer par std::, car cin est un objet appartement à la bibliothèque standard. Et on utilise les chevrons dans le sens inverse de cout.

Gestion des erreurs

Et si vous rentrez une valeur qui n’a rien à voir ? Que se passe-t-il si je décide de taper Maurice au lieu de 5 ? Essayez donc.

Entre ton age :
Maurice
Tu as 0 ans.
Appuyez sur une touche pour continuer...

Comme on veut récupérer un entier et qu’on a écrit une chaîne de caractères, notre variable age n’est pas modifiée et vaut donc toujours 0, sa valeur par défaut.

Donc c’est bon alors ? Y’a pas de soucis particulier, puisque std::cin ignore nos bêtises.

Eh bien nous allons voir que si. Essayons donc de demander deux informations à l’utilisateur : son âge et son nom.

#include <iostream>
#include <string>

int main()
{
    std::cout << "Entre ton age : ";
    int age { 0 };
    std::cin >> age;
    std::cout << "Tu as " << age << " ans.\n";

    std::cout << "Entre ton nom : ";
    std::string nom { "" };
    std::cin >> nom;
    std::cout << "Tu t'appelles " << nom << ".\n";

    return 0;
}

Lancez ce code en faisant exprès de taper une chaîne de caractères plutôt qu’un entier. Vous devriez obtenir le même résultat que si dessous.

Entre ton age : Mishka
Tu as 0 ans.
Entre ton nom : Tu t'appelle .
Appuyez sur une touche pour continuer...

Vous n’avez rien eu le temps de taper que le programme avait fini. C’est parce que, lorsque std::cin rencontre une erreur (comme le fait de rentrer du texte alors qu’on attend un entier), il passe dans un état invalide, tous les caractères invalides restent mémorisés et toutes les utilisations suivantes de std::cin sont foireuses.

Comment allons-nous gérer ça ? Comment nettoyer std::cin s’il a échoué ? Le chapitre suivant vous apprendra un mécanisme basique mais puissant, qu’on trouve tout le temps en programmation et qui nous aidera à régler nos problèmes.

Un mot sur Windows

La gestion des accents et autres sous Windows, avec C++, est compliquée. Malheureusement, la seule solution que j’ai à vous offrir est de vous passer des accents et autres caractères français spéciaux. Tant pis pour la beauté de la langue française.

Dans le cadre professionnel, l’anglais est majoritairement utilisé, donc il n’y a pas de problème.


En résumé

  • C++ nous permet de manipuler des caractères simples, des chaînes de caractères, des nombres entiers et des nombres réels.
  • Il existe des caractères spéciaux, comme le retour à la ligne ('\n') ou le caractère nul ('\0').
  • Les variables nous permettent d’associer un nom à une valeur, mais il faut respecter certaines règles.
  • On peut utiliser auto pour laisser le compilateur déduire tout seul le type d’une expression.
  • Nous pouvons demander des informations à l’utilisateur grâce à std::cin.
  • Nous n’avons aucun mécanisme de protection s’il rentre n’importe quoi.