Welcome to Bukkit France

Inscrivez-vous maintenant pour profiter d'un accès total à tout le contenu offert par la meilleur communauté Bukkit française ! Une fois inscrit et connecté, vous pourrez contribuez à la communauté en postant vos propres sujets et questions ou en répondant à ceux existants. Vous pourrez aussi customiser votre profil, recevoir des points de réputations, communiquer avec les autres membres via le chat, et plus encore! 

  • Annonces

    • Pskyco

      Bukkit France passe sous Discord !   02/20/16

      Bukkit France est désormais passé sur Discord, au revoir donc notre vieux Teamspeak ! Téléchargez le client et venez nous rejoindre sur notre salon en suivant les instructions suivantes.
      M-à-j du 25/02/2017 : Désormais, seuls les comptes actifs sur le forum se verront donner l'accès au Discord, ce dernier n'est pas une plateforme d'aide de la même manière que le chat.
  • billets
    6
  • commentaires
    20
  • vues
    2240

Chapitre II : Les variables

while(true);

948 vues

 

Une variable permet de stocker en mémoire une valeur d'un typé donné. On peut ensuite s'en servir comme bon nous semble.

Rappels de base:


Un octet est égal à 8 bits (sauf dans de la très vieille informatique ou dans de rares domaines de la communication).
Un bit est un chiffre en binaire. C'est-à-dire soit 0, soit 1.
Pour connaître le nombre de possibilités en fonction du nombre de chiffres avec une base donnée, il suffit de faire : basechiffres.
Si on a 32 bits on a donc 232 possibilités soit 4 294 967 296 - un joli paquet.

 

Types courants:

 

Il existe de nombreux types de variables dits "primitifs" en C++. En voici une liste non-exhaustive :

  • bool : C'est un booléen, qui ne peut contenir que true (1) ou false (0). Il est souvent utilisé dans les conditions (voir le prochain chapitre). Attention, il ne mesure pas qu'un seul bit.
  • int : C'est probablement le plus couramment utilisé. Sa taille dépend du type d'architecture cible : Dans le cas du x86, il fait 4 octets (et en x86-64 8 octets). C'est un nombre entier (qui peut être négatif). Il mesure au moins 16 bits. Exemple : 37
  • char : Il est identique au int mais ne mesure généralement qu'un seul octet - attention, cela n'est pas forcément vrai sur certaines architectures exotiques. Il est très souvent utilisé pour la manipulation de texte puisqu'il peut stocker un caractère ASCII (on peut en fait directement assigner un caractère à un char). Exemple : 'a' ou 78. Faites attention : le caractère " permet de délimiter du texte tandis que ' délimite un seul caractère. Vous ne pouvez pas assigner du texte à un char.
  • short : Il est identique au int mais mesure au moins 16 bits, très souvent moins que int. Exemple : 298
  • long : C'est un int de taille supérieure ou égale au int. Il mesure au moins 32 bits.
  • long long : C'est un int de taille supérieure ou égale au long. Il mesure au moins 64 bits. Note: Celui-ci a été introduit par C++11, veillez à utiliser un compilateur récent et à passer -std=c++11 (ou -std=c++14) au compilateur.
  • float : C'est une valeur "flottante". Il permet de stocker des nombres à virgule. Il mesure 4 octets comme le int. Il est préférable de ne l'utiliser que lorsqu'on en a besoin car sa manipulation est plus lente. Exemple : 37.876
  • double : Il est identique au float mais mesure 8 octets. Exemple : 37.876

 

Utilisation & mots-clés:

 

Toutes les variables de ces types peuvent être affichés grâce à std::cout. Il suffit alors de mettre le nom de la variable à la place du "Hello World" que l'on a vu dans le tutoriel.

On peut préfixer les valeurs intégrales (int, char, short) du mot-clé unsigned ce qui fait que les valeurs négatives ne seront plus acceptées. Du coup, cela nous permet d'économiser un bit si on a pas besoin de valeurs négatives (de plus, le compilateur peut mieux pousser ses optimisations). Si vous essayez d'assigner une valeur négative à votre variable unsigned, le compilateur devrait vous avertir.

Le mot clé const permet de rendre une variable non modifiable, par exemple. Dans le cas des primitifs, sa valeur doit donc être assignée lors de la déclaration. Dans cet exemple simple, on place le mot clé const avant le type (voir Approfondir).

#include <iostream>
int main()
{
     float valeurFlottante = 376.84; // Attention! Le nom de cette variable n'est donné qu'à titre d'exemple. Evitez à tout prix de nommer des variables avec des noms peu explicite dans votre code (comme a, b, etc.)
     unsigned int entierPositif = 73;
     char lettreX = 'X';
     std::cout << "La valeur flottante est : " << valeurFlottante << std::endl << "L'entier positif est : " << entierPositif << std::endl << "La lettre est : " << lettreX << std::endl;
     return 0;
}

Testez en ligne!
 

Il est possible de déclarer plusieurs variables à la suite, pour alléger le code visuellement :

int a = 0, b = 3, c = 4; // a, b et c sont tous des int, et on leur donne une valeur de départ
int d, e, f = 4; // Attention : cela ne donne pas la valeur 4 pour d, e et f, mais seulement à f.

 

Portée des variables

 

Une variable déclarée dans un scope anonyme (rappel : délimité par des accolades) ne sera plus accessible en dehors de celle-ci. Par exemple :

#include <iostream>
int main()
{
     { // On a tout à fait le droit de faire ça!
          int maValeur = 5;
     }
     std::cout << maValeur << std::endl; // Erreur : maValeur n'existe pas ici
     return 0;
}

clang++ prévient en effet : main.cpp:7:19: error: use of undeclared identifier 'maValeur'
g++ nous alerte également : main.cpp:7:19: error: 'maValeur' was not declared in this scope

 

Si le compilateur est bien assez intelligent pour éliminer les variables inutilisées, cela peut être une bonne pratique d'utiliser des scopes anonymes quand on peut, notamment dans les grosses fonctions (il faut tout de même mieux diviser les grandes fonctions en plus petites fonctions selon le cas).

 

Valeur des primitifs non initialisés


De plus, une variable que nous définissons mais que nous n'initialisons pas est indéfinie. Cela signifie que sa valeur ne sera pas garantie tant que nous lui donnons pas une valeur.

De ce fait, il ne faut pas lire la valeur de cette variable si l'on ne l'a pas initialisée. Sinon, cela relève du comportement indéfini.

En effet, cette variable aura des chances d'hériter de bouts de mémoire d'un ancien programme ou de bouts de mémoire que le programme a libéré.

#include <iostream>
int main()
{
     int a;
     // La valeur de a ne sera pas forcément zéro!
     a = 3;
     // Maintenant on peut utiliser a.
     return 0;
}

 

Allocation


Quand nous définissons une variable ainsi, cela s'appelle de l'allocation automatique. Dans l'exemple précédent, est un local à la fonction main.
Nous verrons plus tard l'allocation dynamique moderne avec le C++11, grâce à des pointeurs intelligents qui permettent de s'abstraire de la gestion manuelle de la mémoire.
Java utilise presque exclusivement l'allocation dynamique et abstrait la gestion manuelle de la mémoire avec un ramasse-miettes (GC). Si cela rend Java plus performant lors de l'allocation dynamique que le C++, l'allocation automatique en reste plus performante et cela permet d'éviter les coûts inévitables d'un GC.

Nous verrons d'ici peu comment manipuler ces variables.

 

Approfondir (avec des concepts plus avancés) :

 

Taille et capacité d'un type:

La fonction sizeof permet d'obtenir le nombre d'octets qu'utilise un type. Par exemple sizeof(int) retournera la taille du type int. Attention : Pour des pointeurs, par exemple sizeof(float*) mesurera la taille du pointeur plutôt que de la cible.
Vous pouvez inclure le fichier "limits" pour pouvoir connaître de nombreuses informations sur un type donné - par exemple pour la taille maximale d'un float : std::numeric_limits<float>::max(). Documentation

Sur coliru:

  • int : -2147483648 to 2147483647
  • unsigned int : 0 to 4294967295
  • long : -9223372036854775808 to 9223372036854775807
  • unsigned long : 0 to 18446744073709551615
  • long long : -9223372036854775808 to 9223372036854775807
  • unsigned long long : 0 to 18446744073709551615
  • char : -128 to 127
  • unsigned char : 0 to 255
  • short : -32768 to 32767
  • unsigned short : 0 to 65535
  • float : 1.17549e-38 to 3.40282e+38
  • double : 2.22507e-308 to 1.79769e+308

 

Notez qu'il est presque toujours préféré d'utiliser un type plus petit quand on a pas besoin de plus de capacité. Cela aura pour but de réduire l'empreinte mémoire et de potentiellement légèrement accélérer le programme, mais trop se préoccuper de cela est inutile (souvenez vous, l'optimisation prématurée est la cause de tous les maux).

Si il est nécessaire d'avoir des variables de taille fixée (les types normaux ci-dessus ont une taille définie par l'implémentation, variant ainsi entre les machines voire les compilateurs), il est mieux d'utiliser les types du header <cstdint>.

Le type size_t est souvent utilisé pour désigner des tailles (ex. vector::size()). C'est un type non signé de taille définie par l'implémentation.

 

Const, const-correctness et const_cast en C++:

Les const sont plus puissants qu'ils en ont l'air. En effet, si vous construisez un objet ou définissez une référence const, vous ne pourrez utiliser que ses méthodes définies comme const, garantissant ainsi que vous ne modifierez pas l'objet, contrairement aux final de Java. De plus, il est également possible d'avoir une version const et une version normale d'une méthode en C++. Comme dit précédemment, le mot-clé const s'applique à l'objet avant lui (ou après lui s'il se trouve au début). Par exemple :

const int a = 0;
int const b = 0;

Les deux cas précédents sont identiques. Un exemple avec des pointeurs :

int val = 0, val2 = 0;

const int* a = &val; // Pointeur non-const vers un int const
int const *b = &val; // Idem que a
int *const c = &val; // Pointeur const vers un int non-const
int const *const d = &val; // Pointeur const vers un int const
const int *const e = &val; // Idem que d

*a = val2; // Erreur : Assignation de valeur const
a = &val2; // Correct : Assignation d'un pointeur non-const

*c = val2; // Correct : Assignation de valeur non-const
c = &val2; // Erreur : Assignation de pointeur const

*d = val2; // Erreur : Assignation de valeur const
d = &val2; // Erreur : Assignation de pointeur const

On ne peut pas abandonner le const en cours de route de manière implicite:

const int a = 0;
int& b = a; // Erreur: Assigner un 'const int' à une référence de type 'int' élimine le 'const'

Cependant, si on a un objet de départ non-const et une référence vers celui-ci qui est const, il est possible d'utiliser const_cast pour obtenir une référence non-const à partir de cette référence const.

Attention au comportement indéfini, il ne faut surtout pas faire ça si l'objet de départ est const!

int a = 0;
const int& ref = a;
int& b = const_cast<int&>(ref); // Correct
const int a = 0;
const int& ref = a;
int& b = const_cast<int&>(ref); // ATTENTION: Comportement indéfini!

 

Comportements spéciaux:
Quand on dépasse la capacité maximale d'une variable, il se passe ce qu'on appelle un overflow.


Le standard du C++ ne définit pas le comportement d'un overflow sur des types signés (c'est-à-dire qui peuvent avoir une valeur positive ou négative, comme int, char, short).

Il ne faut donc pas baser le fonctionnement de son programme sur de tels overflow, car on ne sait pas ce qui va se passer, et en général les optimiseurs et leurs compilateurs se basent sur le principe que le code de l'utilisateur ne se base pas sur du comportement indéfini. Cela peut paraître agaçant, mais c'est une des puissances (ou faiblesses) du C++ : Cela permet de laisser place à de puissantes optimisations au niveau de compilateur.


Avec les variables unsigned, c'est différent : En cas d'overflow, la variable revient à 0, cette fois-ci le standard le définit bien!

 

Notez qu'il existe des cas comme ceux-ci : Utiliser l'opérateur binaire << sur un entier négatif cause un comportement indéfini, utiliser l'opérateur binaire >> sur un entier négatif cause un comportement défini par l'implémentation.


Pour les types flottants, l'overflow provoque un comportement indéfini surtout car le fonctionnement des float est plus compliqué (et serait inutilement complexe à implémenter de manière standardisée pour diverses raisons).


3 personnes aiment ça


4 Commentaires


Je pense que meme si la valeur maximum et minimum n'est pas fixe , tu devrai quand meme mettre un ordre d'idee de valeurs maximum et minimum pour les variables ... et a la fin tu parle des pointeurs , sur le chapitres des variables , je pense que tu devrai garder sa pour plus tard ;)

Sinon comme d'hab c'est pas mal :D

Partager ce commentaire


Lien vers le commentaire
Partager sur d’autres sites
Il y a 3 heures, DeltaEvo a dit :

Je pense que meme si la valeur maximum et minimum n'est pas fixe , tu devrai quand meme mettre un ordre d'idee de valeurs maximum et minimum pour les variables ... et a la fin tu parle des pointeurs , sur le chapitres des variables , je pense que tu devrai garder sa pour plus tard ;)

Sinon comme d'hab c'est pas mal :D


Je vais rajouter ça :P
Niveau pointeurs je sais bien mais c'est bien spécifié dans la catégorie "approfondir", donc on comprend ou on comprend pas :P

Partager ce commentaire


Lien vers le commentaire
Partager sur d’autres sites

Un peu léger comme article, tu ne parles pas des autres mot clefs utilisable (autre que unsigned), tu ne décris pas les différentes manières d'initialisation et il manque aussi le long et certains type_def très utiles.

Partager ce commentaire


Lien vers le commentaire
Partager sur d’autres sites
il y a 46 minutes, ObimwaKenobi a dit :

Un peu léger comme article, tu ne parles pas des autres mot clefs utilisable (autre que unsigned), tu ne décris pas les différentes manières d'initialisation et il manque aussi le long et certains type_def très utiles.

C'est vrai, faudrait que je complète un peu. Je vais essayer de rajouter ça dès que je continue les articles :P

Partager ce commentaire


Lien vers le commentaire
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !


Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.


Connectez-vous maintenant