Ceci est une ancienne révision du document !
Très schématiquement, un ordinateur peut être décomposé en deux éléments :
On distingue les mémoires de stockage sur le long terme (comme les disques durs) et les mémoires de travail à court terme (mémoire vive ou RAM, Random Access Memory). Cette dernière est utilisée pour conserver les données et les résultats des calculs des programmes.
En C++, les données sont manipulées en utilisant des variables. Ces variables peuvent être utilisées pour réaliser des calculs et divers opérations logiques. Chaque variable dans un programme est désignée par un nom unique, appelé identifiant.
Pour commencer, voyons un exemple de code utilisant une variable :
#include <iostream> int main() { int i { 123 }; // création de i std::cout << i << std::endl; // utilisation de i }
affiche :
123
Ce code permet de créer une variable appelée i
, qui peut contenir un nombre entier (int
correspond à “integer”, qui signifie “entier”) et qui est initialisée avec la valeur 123
. Cette variable i
est ensuite affichée en utilisant std::cout
.
Lors de l'exécution de ce programme, std::cout
ne va pas afficher le caractère i
, mais la valeur contenue dans la variable i
.
std::cout << i << std::endl; // variable i std::cout << 'i' << std::endl; // caractère 'i', entre guillemets simples std::cout << "i" << std::endl; // chaîne de caractères "i", entre guillemets doubles
En pratique, la confusion est rare, la majorité des éditeurs de code utilisent des couleurs différentes pour les variables et les littérales.
La syntaxe générale pour créer une variable peut donc être résumée par le schéma suivant :
TYPE IDENTIFIANT { VALEUR };
Pour créer une variable, vous devez donc donner plusieurs informations, dans l'ordre :
int
dans le code précédent) ;i
dans le code précédent) ;123
dans le code précédent).
On déclare un identifiant, on définie une variable avec un type et un identifiant, on initialise avec variable avec une valeur.
Vous pouvez créer autant de variables que vous le souhaitez dans vos programmes (en fonction des capacités de votre ordinateur. Mais même un ordinateur de bureau basique moderne peut contenir sans problème plusieurs milliards d'entiers en mémoire) :
#include <iostream> int main() { int x { 123 }; int y { 456 }; int y { 789 }; cout << x << endl; cout << y << endl; cout << z << endl; }
affiche :
123 456 789
Nous allons voir en détail chaque élément de la définition et l'initialisation d'une variable.
Il existe en réalité plusieurs syntaxes possibles pour créer une variable. Voici quelques exemples :
int x; // (1) int x = 123; // (2) int x(123); // (3) auto x = 123; // (4)
Vous rencontrez probablement ce type de syntaxe dans des codes existants, par exemple dans des tutoriels en ligne ou dans des livres. Ce cours se focalise sur les syntaxes recommandées en C++ moderne, ces syntaxes ne seront donc pas détaillées par la suite. Mais vous apprendrez sans problème ces syntaxes dans les exercices d'apprentissage que vous réaliserez.
Vous avez déjà rencontré la notion de type dans les chapitres précédents :
2 // littérale entière 2.0 // littérale réelle '2' // littérale caractère "2" // littérale chaîne
Chaque littérale précédente possède un type défini (“entier”, “réel”, “caractère”, “chaîne”, mais il y en a beaucoup d'autres). C'est également le cas avec les variables, elles possèdent toutes un type défini, qui ne peut pas être changer (seule la valeur qu'elles contiennent peut changer).
En C++, les types de base s'écrivent avec des mots-clés définis dans le langage. Vous avez vu que le type int
correspond aux entiers. Il existe beaucoup de types définis en C++ (et il est possible de définir ses propres types, ce qui fait qu'il peut potentiellement exister une infinité de types différents), mais retenez pour le moment les types correspondants aux littérales que vous avez déjà manipulées :
int
(abréviation de integer, “entier” français) correspond à un nombre entier ;double
correspond à un nombre réel (vous verrez par la suite pourquoi le C++ utilise ce terme) ;string
correspond aux chaînes de caractères ; char
correspond à un caractère ;bool
correspond aux booléens.Pour rappel, voici comment s'écrivent les littérales correspondant à chaque type :
int
: par exemple 123
ou 456
;double
: par exemple 123.456
ou 123.456e789
;string
: par exemple “hello, world!”
ou “bonjour!”
;char
: par exemple 'a' ou 'z' ;bool
: uniquement true
ou false
.Si vous essayez, vous pouvez obtenir différents résultats. Par exemple, si vous essayez de mettre dedans une chaîne de caractères, vous aurez une erreur de compilation. Si vous essayez de mettre un nombre réel, celui-ci sera arrondi en valeur entière.
Dans tous les cas, il est important d'accorder une attention particulière aux types des variables et des données dans vos codes C++.
Le type d'une variable en C++ est définitif, il n'est pas possible de le changer une fois que vous avez défini une variable, vous ne pouvez donc pas écrire :
int x { 123 }; // x correspond à un entier x = "Bonjour"; // erreur : vous ne pouvez pas écrire une chaîne de caractères // dans un entier
Vous avez vu dans les chapitres précédents que le C++ accorde une importance particulière aux types des données. Une même valeur, par exemple 3, pourra signifier différentes choses selon le type : 3
pour un nombre entier, 3.0
pour une nombre réel, “3”
pour une chaîne de caractères, '3' pour un caractère.
Ce principe de typage fort s'applique également pour les variables : une variable est créée avec un type défini et ne pourra pas changer de type. Si vous créez une variable de type entier par exemple (i.e. pouvant contenir des nombres entiers), vous ne pourrez pas mettre un autre type dedans.
L'identifiant d'une variable est le nom de cette variable. Vous pouvez utiliser cet identifiant dans vos codes en remplacement d'une valeur dans un calcul par exemple. Si vous utilisez plusieurs variables, chaque identifiant doit être unique, vous ne pouvez pas définir plusieurs variables utilisant le même nom :
int const x { 123 }; // x correspond à un entier int const x { 456 }; // erreur : l'identifiant x est déjà utilisé
Pour écrire un identifiant, vous pouvez utiliser les caractères alphanumériques minuscules et majuscules (a
à z
, A
à Z
et 0
à 9
) et le tiret bas _
(underscore, correspond à la touche 8 sur un clavier français). De plus, un identifiant doit obligatoirement commencer par une lettre.
Par exemple, les noms suivants sont des identifiants valides :
x
;y
;unevariable
;uneVariable
;une_variable
;UnEvArIaBlE
.En revanche, les identifiants suivants ne sont pas valides :
_une_variable
: commence par un tiret bas ;123variable
: commence par un chiffre ;variable_réelle
: contient un caractère interdit (é
).Comme vous le voyez, le langage C++ laisse de grandes libertés pour choisir un identifiant… ce qui peut poser des problèmes. Exemple de mauvais identifiant :
jjfndsfkjgukzv
: ne veut rien dire, n'apporte pas d'information sur le rôle de cette variable ;une_variable
: trop générique ;variable1
, variable2
, etc. : idem ;UnEvArIaBlE
: peu lisible ;une_variable_qui_contient_le_resultat_du_premier_calcul
: trop long.Bonne pratique de codage : règles que vous vous imposez ainsi qu'aux développeurs qui participent à un projet, pour faciliter la lecture du code par tous. Le but est d'avoir des noms homogènes, simples et informatifs.
Il existe déjà des “règles de codage” toutes faites, vous pouvez utiliser vos propres règles. Les conventions de nommage les plus connues : une_variable
(STL, Boost), uneVariable
(Qt)
Une variable contient obligatoirement une valeur. Il est possible de définir une variable sans l'initialiser, mais cette variable pourra alors contenir une valeur aléatoire. Cependant, vous imaginez bien qu'un programme ne va pas forcément fonctionner correctement si certaines variables sont initialisées avec des valeurs aléatoires. Nous n'allons pas voir toutes les syntaxes possibles pour initialiser une variable, mais uniquement celles qui sont recommandées.
Une variable peut être initialisée avec une valeur par défaut (value initialization), avec une littérale (direct initialization) ou avec une expression (copy initialization).
Type Identifiant {};
;Type Identifiant { Valeur };
;Type Identifiant { Expression };
.
Le signe =
utilisé pour attribuer une valeur à une variable s'appelle l'opérateur d'affectation.
Plus concrètement, avec du code :
#include <iostream> #include <string> int main() { // Initialisation par défaut int const i_default {}; double const d_default {}; std::string const s_default {}; char const c_default {}; bool const b_default {}; // Initialisation avec une valeur int const i_value { 123 }; double const d_value { 123.456 }; std::string const s_value { "hello, world!" }; char const c_value { 'a' }; bool const b_value { true }; // Initialisation avec une expression int const i_expression { 123 + 456 }; double const d_expression { 12.34 + 56.78 }; std::string const s_expression { std::string{ "hello, " } + "world!" }; bool const b_expression { 123 > 456 }; }
Comme vous avez vu dans les codes précédents, il est possible d'afficher la valeur d'une variable directement avec cout
. Celui-ci est capable de connaître le type de la variable et d'afficher correctement la valeur, comme si vous aviez écrit une littérale directement avec cout
. Vous pouvez également utiliser directement une variable dans un calcul.
int const x { 123 }; std::cout << "La valeur de x est : " << x << std::endl; int const y { x * 45 };
Une variable existe à partir du moment où vous la créez, pas avant. Vous ne pouvez pas utiliser une variable dans une ligne de code et la définir ensuite.
cout << i << endl; // erreur : la variable x est inconnue à cette ligne, elle est // définie uniquement à partir de la ligne suivante int i { 123 }; // création de i
Modifier une variable non const
: utilisation de l'opérateur d'affectation =
.
Combinaison de l'opérateur d'affectation et d'opérateurs arithmétiques :
a = a + b; a += b; a = a * b; a *= b; etc.
Lorsque vous ne modifiez pas une variable après l'avoir initialisée, on peut considérer que cette variable est constante. Vous pouvez indiquer cette information au compilateur en ajoutant le mot-clé const
(“constant”) comme modificateur de type. Ainsi :
int
: représente un type entier ;int const
(ou const int
) : représente un type entier constant.Indiquer cette information permet au compilateur de faire certaines optimisations (ce que vous ne verrez pas forcément sur un petit programme, mais cela peut avoir un impact sur un programme complexe) et surtout cela permet au compilateur de vérifier que vous ne modifiez pas cette variable par la suite.
int x { 123 }; x = 456; // ok, x n'est pas constant int const y { 123 }; y = 456; // erreur, y est constant
L'utilisation de const
apporte une garantie plus forte sur votre code, vous devez systématiquement réfléchir aux rôles de vos variables et si elles doivent être modifiées durant l'exécution de votre programme ou non. Et donc utiliser le mot-clé const
aussi souvent que nécessaire.
Le typage des variables et l'utilisation de const
est une forme de contrat que vous passez avec le compilateur. Vous lui dites que vous allez respecter un certain nombre de contraintes (les valeurs seront d'un type défini, les variables ne seront pas modifiées), celui-ci pourra alors vérifier que vous respectez ces contraintes et fera éventuellement des optimisations.
La programmation par contrat est une approche qui permet d'améliorer la qualité de votre code et qui est plus complet que ce qui est présenté ici. Vous verrez dans la suite du cours comment utiliser efficacement la programmation par contrat en C++, en particulier pour créer vos propres types.
En pratique, lorsque l'on écrit :
int x = y + z;
Que se passe-t-il en réalité ?
Le résultat du processeur est une variable non nommée (type, valeur) temporaire (pas en mémoire). Cette variable temporaire s'appelle une rvalue (pour right value, du fait qu'une rvalue ne peut être qu'à droite du signe d'affectation).
Au contraire, x, y, et z sont des variables nommées non temporaires. Ces variables s'appellent des lvalue (pour left value, du fait qu'une lvalue qui peut être à gauche).
Bien faire attention à la différence : c'est l'expression (y + z) qui est une rvalue, y et z en eux-mêmes sont des lvalue.
Certains types sont convertibles automatiquement en booléen, pour pouvoir tester s'ils sont valides ou non. C'est le cas par exemple des littérales chaînes de caractères. Il est possible d'écrire le code suivant sans que cela ne produise d'erreur :
bool b { "hello, world" };
Sur les anciennes syntaxes pour initialiser : A case against direct initialisation