Big Tuto : Apprenez le C++

Chapitre 2 : Types de données - Variables et Constantes
 

Tutoriel présenté par : Robert Gillard (Gondulzak)
Date d'écriture : 30 juillet 2015
Date de révision : 9 mai 2016

 

      1 - C++ et SFML 

   Lors de notre premier chapitre, nous avons vu comment très simplement écrire une chaîne de caractères dans la console. L'objet cout du flux de sortie ostream remplit très rapidement et très simplement cette opération. wink

   Ceux d'entre-vous qui ont déjà utilisé une plateforme graphique (SDL, SDL2, Xna, ou autre) ont remarqué qu'il en va tout autrement pour écrire du texte à l'aide de tels supports et que le travail pour y arriver est autrement fastidieux. cheeky

   Voici à titre d'exemple, la phrase « Le plus long voyage commence par un premier pas ! » écrite sous la plateforme SFML dans une fenêtre de 640 x 240 pixels et à laquelle j'ai ajouté un background pour avoir un rendu plus agréable.

 

 

   Je n'écris pas ici le code C++ / SFML de cet exemple, il sort du cadre de ce tutoriel et je vous renvoie à ce sujet à l'excellent Big Tuto SFML 2 de Jay.

   Ce code vous sera cependant livré en même temps que les fichiers correspondants aux exemples de ce second chapitre. wink

   Cette parenthèse étant faite, on en revient à notre tutoriel.

 

         2 – Types de données

   Complémentairement aux types de données issus du langage C, d'autres, spécifiques au C++, sont venus s'ajouter. 

   Un type spécial, void, hérité du C, ne renvoie aucune valeur. Nous verrons qu'il est principalement utilisé comme type de retour dans certaines fonctions.

   Les types suivants sont dénommés ''types arithmétiques''et parmi ceux-ci les types «entiers» peuvent être «signés» ou «non signés».

   Les variables signées peuvent s'étendre sur une plage de valeurs négatives et positives tandis que les valeurs non signées sont toutes positives.

   Elles occupent une certaine place en mémoire, différente l'une de l'autre, cette place étant calculée en bytes (octets) et leurs plages peuvent atteindre des valeurs très élevées.

   Tout ceci est rassemblé dans le tableau suivant :

 

Types de données C++

Type

Signification

Taille

Valeurs

unsigned char

caractère

1 byte

de 0 à 255

char

caractère

1 byte

de – 128 à 127

bool

booléen

1 byte

true / false (vrai / faux)

short int

entier court signé

2 bytes

de – 32768 à + 32767

unsigned short int

entier court non signé

2 bytes

de 0 à 65535

int

entier signé (32 bits)

4 bytes

de – 2 147 483 648 à
+ 2 147 483 647

unsigned int

entier non signé (32 bits)

2 bytes

de 0 à 4 294 967 295

long int

entier long signé

4 bytes 

de – 2 147 483 648 à
+ 2 147 483 647

unsigned long int

entier long non signé 

4 bytes

de 0 à 4 294 967 295

long long int

entier très long signé 

8 bytes

de – 9 223 372 036 854 775 807 à
+ 9 223 372 036 854 775 806

unsigned long long int

entier très long non signé

8 bytes

de 0 à
18 446 744 073 709 551 615

float

nombre décimal

4 bytes

de 1,2e – 38 à 3,4e 38

double

nombre double

8 bytes

de 2,2e – 308 à 1,8e 308

 

   Pour des raisons évidentes de simplification, tous les types signés ont été emputés de leur qualificatif, ainsi par exemple signed int est simplement devenu int.

   Et rassurez-vous, il n'y a rien à apprendre par coeur ici... indecision

   Il faut juste savoir que ces types existent en C++, c'est tout. wink Nous verrons plus loin lesquels préférer et dans quelles circonstances. smiley

 

Les projets complets de ce chapitre :

 

 

         Projet007 : Sizeof

    Nous savons que la taille des variables dépend souvent d'un compilateur ou du type de microprocesseur (32 ou 64 bits) d'un PC.

   Je vous propose ci-après un petit programme que vous pouvez essayer sur votre propre PC afin de vérifier par vous-même la taille des types de notre tableau. 

 

#include <iostream>
#include <conio.h>
 
using namespace std;
 
int main()
{
cout << "La taille d'un type 'char' est \t\t\t\t" << sizeof(char) << " byte" << endl;
cout << "La taille d'un type 'bool' est \t\t\t\t" << sizeof(bool) << " byte" << endl;
cout << "La taille d'un type 'short int' est \t\t\t" << sizeof(short int) << " bytes"<< endl;
cout << "La taille d'un type 'unsigned short int' est \t\t" << sizeof(unsigned short int) << " bytes" << endl;
cout << "La taille d'un type 'int' est \t\t\t\t" << sizeof(int) << " bytes" << endl;
cout << "La taille d'un type 'unsigned int' est \t\t\t" << sizeof(unsigned int) << "bytes" << endl;
cout << "La taille d'un type 'long int' est \t\t\t" << sizeof(long int) << " bytes"<< endl;
cout << "La taille d'un type 'unsigned long int' est \t\t" << sizeof(unsigned long int) << " bytes" << endl;
cout << "La taille d'un type 'long long int' est \t\t" << sizeof(long long int) << "bytes" << endl;
cout << "La taille d'un type 'unsigned long long int' est \t" << sizeof(unsigned long long int) << " bytes" << endl;
cout << "La taille d'un type 'float' est \t\t\t" << sizeof(float) << " bytes" << endl;
cout << "La taille d'un type 'double' est \t\t\t" << sizeof(double) << " bytes" << endl;
 
_getch();
 
return 0;
} 

 

   Dans ce programme, vous pouvez voir que j'utilise la séquence d'échappement (\t) de tabulation horizontale afin d'aligner la taille des types sur la même colonne.

   Voici le résultat dans la console de mon PC :

 

 

   Les séquences d'échappement utilisées par C++ sont les suivantes :

\a                   alerte sonore
\b                   retour arrière
\f                    saut de page
\n                   nouvelle ligne
\r                    retour chariot
\t                    tabulation horizontale
\v                   tabulation verticale
\'                    guillemet simple
\''                   guillemet double
\?                   point d'interrogation
\\                    barre oblique inverse
\x000             notation octale
\xhhh             notation hexadécimale

 

   Le langage C++ utilise également d'autres types de données : wchar_t, char16_t et char32_t, utilisés pour les séries de caractères étendus.

   wchar_t est un type qui garantit être assez grand pour contenir n'importe quel caractère d'une série de caractères étendus.

   wchar16_t et wchar32_t sont, quant à eux, utilisés pour la représentation des caractères Unicode (Unicode étant le standard utilisé pour représenter les caractères de tout langage naturel wink).

 

         3 – Création d'Alias avec typedef ou using (C++ 11)

 

   Un alias est un type pouvant être considéré comme synonyme d'un autre type. Le mot-clé «typedef» qui provient du langage C convient parfaitement pour créer un alias, bien qu'un autre, nommé «using» a été introduit par le nouveau standard C++ 11 pour réaliser le même genre d'opération.

   Mais pourquoi créer des alias ? surprise

   Il y a plusieurs raisons de créer des alias mais l'une d'entre-elles me semble intéressante. Remarquez la longueur des types suivants :

unsigned short int
unsigned long int
unsigned long long int 

   Un compilateur ne se fâchera pas si nous allégeons quelque peut la longueur de ces types en écrivant :

unsigned short
unsigned long
unsigned long long

 

   Notez également que unsigned int peut simplement s'écrire unsignedwink

   Mais les types écrits en «gras» restent encore longs à écrire et nous allons les transformer en un alias qui utilise «typedef» ou «using» des deux façons suivantes :

typedef ''alias'' ''type_à_modifier''
                       ou
using ''alias'' = ''type_à_modifier''

 

   Vous remarquerez le signe ''='' dans la création d'un alias avec le mot-clé using. Mais voyons cela dans le programme suivant (Projet 008 Alias) :  

 
#include <iostream>
 
using namespace std;
 
//Création d'alias utilisant using...
using Ushort = unsigned short int;
using Ulong = unsigned long int;
 
//Création d'alias utilisant typedef...
typedef unsigned long long ULL;
 
int main()
{
Ushort a = 10;
Ulong b = 125000;
 
//Forme moderne d'initialisation d'un nombre.
ULL c(36978547);
 
cout << "La valeur de la variable 'a' de type Ushort est " << a << endl;
cout << "La valeur de la variable 'b' de type Ulong est " << b << endl;
cout << "La valeur de la variable 'c' de type ULL est " << c << endl;
 
cin.get();
}

 

   Remarquez que j'ai mis ULL en majuscules pour ne pas confondre le l minuscule avec le chiffre un. Si vous optez pour la création d'alias dans vos programmes, choisissez un nom qui vous rappelle immédiatement le nom du type modifié, mais le principal est de rester constant dans ce que vous écrivez et que vos alias soient toujours définis de la même manière. wink

   Voici le résultat dans la console : 

 

   Quels types préférer ? Voici quelques règles de préférence à respecter :

- Pour des valeurs ne pouvant pas être négatives : le type unsigned.
- Pour les entiers arithmétiques : le type int.
- char et bool ne doivent pas être attribués à des expressions arithmétiques.
- char doit être réservé aux types ''caractère'' et bool aux valeurs vrai/faux.
- Pour des valeurs de type ''flottant'', préférez double à float.
- Pour les très grands nombres, long ou long long.
  

         4 – Variables

   La définition d'une variable n'est plus à expliquer. Au cours d'un programme, une variable est sensée changer constamment de valeur.

   Comme en C, une variable possède un type, il s'agit de l'un de ceux vus dans les paragraphes précédents. wink

 

   Initialisation des variables :

   En C++, une variable peut-être déclarée et initialisée à n'importe quel endroit d'un programme, mais nous touchons ici au domaine appelé «Portée d'une variable», que nous verrons dès notre première étude sur les fonctions. En attendant, nous initialiserons nos variables en début de programme, mais notez que le C++ permet également les déclaration et initialisations de variables sur une même ligne.

   Voyons cela dans l'exemple suivant (projet 009 - Variables) : 

 
#include <iostream>
#include <conio.h>
 
using namespace std;
 
//Création d'alias utilisant using...
using Ulong = unsigned long int;
 
 
int main()
{
//Nous calculons le volume d'un parallélépipède rectangle dont des valeurs de
//deux côtés sont connus. Le troisième sera entré par l'utilisateur.
//Nous allons également utiliser une variable booléenne dont le résultat
//permettra l'affichage d'un petit texte à l'écran selon que le volume soit
//inférieur ou supérieur à une certaine valeur.
 
Ulong hauteur(102), largeur(125), profondeur, volume, valeur(2000000);
bool estPlusGrand;
 
cout << "Entrez la profondeur du parallelepipede ";
cin >> profondeur;
cout << endl;
 
volume = hauteur * largeur * profondeur;
 
cout << "Le volume recherche est " << volume << endl;
 
if (volume > 2000000)
estPlusGrand = true;
else
estPlusGrand = false;
 
if (!estPlusGrand)
cout << "Le volume est < 2000000" << endl;
else
cout << "Le volume est > 2000000" << endl;
 
 
_getch();
 
return 0;
}

 

   Le programme demande quelques explications supplémentaires. Nous créons premièrement un alias Ulong comme définition du type unsigned long int.

   Dans le main(), nous déclarons nos variables de calcul sur une même ligne sauf la profondeur qui sera demandée à l'utilisateur et le volume qui bien sûr, n'est pas encore connu. laugh

   A la ligne suivante, le booléen n'est pas initialisé mais il le sera un peu plus loin suite au calcul du volume.

   J'aurais pu écrire les résultats dans une seule condition (if...else) mais j'ai préféré le faire en deux étapes pour vous montrer la forme prise par le booléen estPlusGrand.
   Remarquez la forme !estPlusGrand, qui est identique mais est à préférer à estPlusGrand == false, qui affiche que le volume est inférieur à 2 000 000 et supérieur dans le cas contraire.

   Voici le résultat de ce programme dans la console, dans le cas où l'utilisateur aurait par exemple entré 229 comme profondeur du parallélépipède rectangle :

 

 

   Conversion forcée de type (cast)

   Nous savons que le résultat de la division d'un entier par un entier produira toujours un résultat entier. Une division exacte ne cause pas de problème mais considérons le cas de 10/3. Le résultat est 3 car 3 est le plus grand nombre entier de fois compris dans 10. Le reste de la division de 10/3 est 1 et s'écrit 10%3 (10 mod 3 ou 10 modulo 3).

   Afin de forcer la sortie à un résultat non tronqué, on «force» («cast») la variable entière à prendre le type float ou double.

   Un autre cas est celui où nous désirerions que le résultat d'une division de 2 entiers soit une valeur flottante. Nous pouvons forcer le résultat de l'opération en utilisant un ''static_cast'' sur l'une des deux opérandes. Nous voyons ceci dans le programme suivant (projet 010 - Cast) avec les commentaires dans le code : 

 

#include <iostream>
 
using namespace std;
 
int main()
{
int a(10);
int b(3);
 
//division de deux entiers
cout << "10/3 = " << 10 / 3 << endl;
 
//Et le reste de la division de 10 par 3
cout << "10 % 3 = " << 10 % 3 << endl;
 
//On fait un cast sur le résultat (en simple précision)
cout << "10/3 = " << (float)10 / 3 << endl;
 
//Si le résultat doit être d'un type plus grand que les valeurs à traiter,
//on utilise un static_cast sur l'une d'entre-elles
double resultat2 = static_cast<double>(a) / b;
cout << "Resultat2 = " << resultat2 << endl;
 
//Egalement valable mais beaucoup moins parlant...
double resultat3 = a / static_cast<double>(b);
cout << "Resultat3 = " << resultat3 << endl;
 
cin.get();
}

 

   Les types ''auto'' et ''decltype'' de la nouvelle norme C++ 11.

   Le C++ 11 a introduit ces deux spécificateurs de type dans sa nouvelle norme. Ceux-ci sont principalement utilisés dans certaines fonctions ou dans des expressions complexes comme nous le verrons plus tard, mais nous pouvons déjà en expliquer le principe.

   ''auto'' peut- être utilisé pour déduire le type d'une variable ou de son résultat. Soit l'exemple suivant (Projet 011 - auto) :

 
#include<iostream>
 
using namespace std;
 
int main()
{
//Exemple 1 : le compilateur déduit le type 'int' de b
int a = 10;
auto b = 3;
//On affiche la somme (a + b)
cout << "La somme de " << a << " + " << b << " = " << a + b << endl;
 
 
//Exemple 2 : le compilateur déduit le type 'double' du quotient
auto num = 11;
double denom = 4;
//On affiche le quotient (num / denom)
cout << "Le quotient de " << num << " / " << denom << " = " << num / denom << endl;
 
 
cin.get();
return 0;
}

 

 

   Si le spécificateur de type auto déclare une variable avec un type particulier, decltype, quant à lui, permet d'extraire le type d'une variable ou d'une expression.

   Considérons le code suivant (projet 012 - decltype) :
 

#include<iostream>
using namespace std;
 
int main()
{
int a = 10;
int somme(0);
decltype (a) b = a; //decltype déduit le type de b comme étant le type de a
 
b = 3;
somme = a + b;
 
cout << "La somme de " << a << " + " << b << " = " << somme << endl;
 
cin.get();
return 0;
}

 

   Le type de la variable ''b'' n'a pas besoin d'être déclaré, le spécificateur de type ''decltype'' s'en est chargé dans la ligne précédente. wink


   Dans cet exemple simple, ''decltype'' agit un peu à la manière de ''auto''. Il est cependant beaucoup plus utilisé dans certaines fonctions ou expressions complexes que nous verrons plus tard. 
   A ce niveau du tutoriel, il me semblait néanmoins utile d'en faire mention maintenant... cool

 

   5 – Constantes


   Une constante, au même titre qu'une variable, sert à stocker de l'information. A la différence d'une variable, une constante NE PEUT PLUS être modifiée après son initialisation. Le C++ distingue deux types de constantes, les constantes littérales et les constantes symboliques.

 

   Exemples de constantes littérales :

   const int maxLife = 100 ; // Le nombre maximum de points de vie est 100.
   const string northEnemy = ''Gobelin'' ; //L'ennemi du nord ne peut-être qu'un Gobelin.

 

   Exemple de constante symbolique :

   Supposons qu'un carquois contienne 100 flèches au maximum et que nous nous saisissions de deux carquois complets. Soit :

   int nb_de_fleches(0);
   int nb_carquois(2);
   const int fleches_par_carquois = 100;

   Nous aurons donc acquis :

   nb_de_fleches = nb_carquois * fleches_par_carquois ;


Définition de constantes

   Les constantes devront toujours être déclarées en début de programme ou dans un fichier ''include'' reprenant toutes les constantes d'un programme. wink

   Les constantes peuvent être définies de deux manières, soit en utilisant l'ancienne directive du pré-processeur #define (cependant déclarée obsolète par la nouvelle norme C++ 11).

# define HAUT 0 ;
# define Bas 1 ;

   soit en utilisant le mot-clé const (vivement conseillé wink) comme nous l'avons fait précédemment dans notre exemple sur les flèches.

   Notez également que C++ 11 a introduit la nuance relative d'expression constante. Celle-ci utilise le mot-clé '''constexpr'' que nous développerons dans l'étude des pointeurs.

 

Constantes énumérées (enum)

   Ce sont des constantes qui permettent la création de nouveaux types. On écrit le mot-clé enum, suivi du type que l'on veut donner à l'énumération, d'une série de valeurs comprises entre une accolade ouvrante et une accolade fermante. Un point-virgule doit suivre l'accolade fermante.

Exemple :

enum Enemy { Gobelin, Orc, Troll, Squelette, Liche };

   Dans cette instruction, Enemy est le type de l'énumération. Gobelin, Orc, Troll... sont des constantes symboliques initialisées d'office avec les valeurs 0, 1, 2 ...

   Considérons le petit programme suivant (projet 013 - enum) :

 
#include<iostream>
 
using namespace std;
 
int main()
{
//Déclaration d'un type énuméré Enemy
enum Enemy { Gobelin, Orc, Troll, Squelette, Liche };
 
//Déclaration d'une variable RACE de type Enemy
Enemy RACE;
 
//On met un Troll dans la variable RACE
RACE = Troll;
 
switch (RACE)
{
case Gobelin :
cout << "Mefiez-vous d'eux, ce sont des voleurs !" << endl;
break;
 
case Orc :
cout << "Vous devriez etre bien prepare avant de les affronter !" << endl;
break;
 
case Troll :
cout << "Si vous en rencontrez un, courez..." << endl;
break;
}
cin.get();
return 0;
} 

 

   Et comme vous pouvez le voir, écrire une instruction switch de cette manière est bien plus parlant qu'écrire case 0, case 1, case 2 ...etc.

   Si vous avez mis un Troll dans la variable RACE, on vous demandera de changer de trottoir... Heu je veux dire de prendre vos jambes à votre cou !

   Et voilà, ce chapitre 2 est maintenant terminé. Dans le prochain chapitre nous parlerons notamment des tableaux, des chaînes de caractères et des fonctions de la classe ''string'' ! angel

 

@ bientôt, smiley

Gondulzak.
  

 

 

 

Connexion

CoalaWeb Traffic

Today144
Yesterday282
This week938
This month3231
Total1742438

19/04/24