Ceci est une ancienne révision du document !
Typage fort et faible ? de même type, notion de typage fort (on ne mélange pas les types). Mais conversion possible
limite des nombres réels
Dans le chapitre Logique binaire et calcul booléen, vous avez vu que lorsque vous afficher l'inverse d'un nombre booléen, la valeur est affichée correspond à un nombre codé sur 32 bits (pour rappel, 32 bits = 4 octets = 8 chiffres hexadécimaux).
#include <iostream> int main() { std::cout << std::hex << std::showbase; std::cout << ~0b1 << std::endl; std::cout << ~0b001 << std::endl; std::cout << ~0b00001 << std::endl; }
affiche :
0xfffffffe 0xfffffffe 0xfffffffe
Quelque soit le nombre de chiffres hexadécimaux que l'on utilise pour écrire la littérale booléenne, la valeur inverse est toujours affichée sur 32 bits. La raison est que dans tous les cas, le compilateur créé une variable temporaire de 32 bits puis calcul l'inverse dessus. Le résultat est donc toujours sur 32 bits.
Plus généralement, un processeur est optimisé pour travailler avec certaines tailles de variable, en général 8, 16, 32 ou 64 bits. Le langage C++ permet d'utiliser des variables de différentes tailles, que vous pourrez utiliser selon vos besoins.
Voyons pour commencer comment connaître la taille en mémoire d'une variable ou d'un type. Dans les deux cas, vous devez utiliser la fonction sizeof
et lui passer un paramètre la variable ou le type :
#include <iostream> #include <string> int main() { std::cout << "sizeof(int) = " << sizeof(int) << std::endl; std::cout << "sizeof(double) = " << sizeof(double) << std::endl; std::cout << "sizeof(bool) = " << sizeof(bool) << std::endl; std::cout << "sizeof(std::string) = " << sizeof(std::string) << std::endl; std::cout << "sizeof(char) = " << sizeof(char) << std::endl; }
Le code précédent va par exemple afficher les tailles suivantes, en octets. La taille peut dépendre du système d'exploitation et du processeur.
sizeof(int) = 4 sizeof(double) = 8 sizeof(bool) = 1 sizeof(std::string) = 8 sizeof(char) = 1
De la même façon, pour connaître la taille en mémoire d'une variable, vous pouvez écrire :
#include <iostream> #include <string> int main() { int x { 123 }; double d { 12.34 }; bool b { true }; std::string s { "hello, world!" }; char c { 'a' }; std::cout << "sizeof(int) = " << sizeof(x) << std::endl; std::cout << "sizeof(double) = " << sizeof(d) << std::endl; std::cout << "sizeof(bool) = " << sizeof(b) << std::endl; std::cout << "sizeof(std::string) = " << sizeof(s) << std::endl; std::cout << "sizeof(char) = " << sizeof(c) << std::endl; }
Ce qui affichera (selon le contexte d'exécution) :
sizeof(int) = 4 sizeof(double) = 8 sizeof(bool) = 1 sizeof(std::string) = 8 sizeof(char) = 1
Il faut bien faire attention à la taille en mémoire et la taille du contenu d'une variable. Si on prend par exemple une chaîne de caractères de type string
, on peut remarquer que la taille retournée par sizeof
sera toujours la même, quelque soit le nombre de caractères.
#include <iostream> #include <string> int main() { std::string s1 { "hello, world!" }; std::string s2 { "Bonjour tout le monde !" }; std::cout << "sizeof(s1) = " << sizeof(s1) << std::endl; std::cout << "sizeof(s2) = " << sizeof(s2) << std::endl; }
affiche :
sizeof(s1) = 8 sizeof(s2) = 8
Pour bien comprendre pourquoi sizeof
donne ce résultat, il faut voir le fonctionnement interne de la classe string
. Vous verrez cela dans les chapitres sur la création de nouvelle classe. Pour connaître la taille de la chaîne de caractères (c'est-à-dire le nombre de caractères, il faut utiliser la fonction membre size
;
#include <iostream> #include <string> int main() { std::string s1 { "hello, world!" }; std::string s2 { "Bonjour tout le monde !" }; std::cout << "s1.size() = " << s1.size() << std::endl; std::cout << "s2.size() = " << s2.size() << std::endl; }
affiche :
s1.size() = 13 s2.size() = 23
Dans de nombreux cas, une application simple sur un ordinateur ne pose pas de problème
Problème : occupation mémoire parfois inutile. Par exemple, bool qui pourrait être représenté par 1 bit est représenté par 1 octet (8 bits). Si on a besoin de compter de 0 à 3, int sur 4 octets est trop gros (il faut que 2 bits pour cela : 0b00, 0b01, 0b10 et 0b11).
Possible de modifier les types de base en ajouter des modificateurs, de façon à réduire leurs taille en mémoire ou leur plage de valeur. Par exemple, pour modifier la taille : short, long, long long.
#include <iostream> int main() { std::cout << "sizeof(char) = " << sizeof(char) << std::endl; std::cout << "sizeof(short int) = " << sizeof(short int) << std::endl; std::cout << "sizeof(int) = " << sizeof(int) << std::endl; std::cout << "sizeof(long int) = " << sizeof(long int) << std::endl; std::cout << "sizeof(long long int) = " << sizeof(long long int) << std::endl; }
affiche :
sizeof(char) = 2 sizeof(short int) = 2 sizeof(int) = 4 sizeof(long int) = 8 sizeof(long long int) = 8
Remarque : char est en fait un entier sur 1 octet, affiché par cout comme un caractère, selon le code ASCII. Mais peut être manipulé comme un entier
Pour modifier le signe : signed (par défaut, pas nécessaire de la mettre) et unsigned (que des positifs)
#include <iostream> int main() { std::cout << "sizeof(char) = " << sizeof(char) << std::endl; std::cout << "sizeof(unsigned char) = " << sizeof(unsigned char) << std::endl; std::cout << "sizeof(short int) = " << sizeof(short int) << std::endl; std::cout << "sizeof(unsigned short int) = " << sizeof(unsigned short int) << std::endl; std::cout << "sizeof(int) = " << sizeof(int) << std::endl; std::cout << "sizeof(unsigned int) = " << sizeof(unsigned int) << std::endl; std::cout << "sizeof(long int) = " << sizeof(long int) << std::endl; std::cout << "sizeof(unsigned long int) = " << sizeof(unsigned long int) << std::endl; std::cout << "sizeof(long long int) = " << sizeof(long long int) << std::endl; std::cout << "sizeof(unsigned long long int) = " << sizeof(unsigned long long int) << std::endl; }
affiche
sizeof(char) = 1 sizeof(unsigned char) = 1 sizeof(short int) = 2 sizeof(unsigned short int) = 2 sizeof(int) = 4 sizeof(unsigned int) = 4 sizeof(long int) = 8 sizeof(unsigned long int) = 8 sizeof(long long int) = 8 sizeof(unsigned long long int) = 8
La différence est que le signed ira de -Max à +Max et que unsigned ira de 0 à 2*Max
En fait, dans la norme, pas de garantie sur la taille, juste des garantie sur l'ordre char < short < int < long < long long. ( Fixed width integer types ? )
Pour les nombres réels, un peu différent :
#include <iostream> int main() { std::cout << "sizeof(float) = " << sizeof(float) << std::endl; std::cout << "sizeof(double) = " << sizeof(double) << std::endl; std::cout << "sizeof(long double) = " << sizeof(long double) << std::endl; }
affiche :
sizeof(float) = 4 sizeof(double) = 8 sizeof(long double) = 16
Ici, les noms changent, pas simplement des modificateurs de type (pour des raisons historiques : au début, que les float - qui veut dire floattant, pour nombre à virgule flottante - puis ensuite les réels 2 fois plus gros que les floats = appelé double. Ensuite taille au dessus, choisit long comme pour les entier).
Par de modificateurs pour les autres types
digits ? digits10 ? max_digits10 ? radix ?
utiliation de u, ul, ull pour modifier une littérale entier et f et l pour une littérale réelle.
max, min, epsilon, etc
Partir d'un code simple :
int x {}; int y {}; int z {}; int a {}; int b {}; int c {};
On décide de changer d'avis et d'utiliser des réels. Nécessite de tout remplacer
double x {}; double y {}; double z {}; double a {}; double b {}; double c {};
Même problème que pour les variables : nécessite modification de plusieurs lignes, code non facilement maintenable, non respect qualité logiciel.
Pour éviter cela, définir un type, le nommer et l'utiliser.
using local_type = int; local_type x {}; local_type y {}; local_type z {}; local_type a {}; local_type b {}; local_type c {};
Pour changer de type, besoin que de changer une ligne. Code plus facilement maintenable
Nom des types = même règle que pour les noms des variables. Souvent, pour montrer que c'est un type, on ajoute _t
a la fin (C++, STL, boost) ou on commence par une majuscule (Qt)
sémantique simple : donner un sens aux types.
ex:
int temp1 { 12 }; // heure int temp2 { 40 }; // minutes temps1 + temps2; // n'a pas de sens
Nommer les types :
using heure = int; using minute = int; heure temp1 { 12 }; minute temp2 { 40 };
Le type exprimer à quoi il correspond, c'est plus précis que “int”. Mais pas suffisant, on peut toujours écrire :
temps1 + temps2; // n'a pas de sens
idéallement, faudrait que compilateur préviennent. Pourquoi fait-il pas ? Parce que “heure” et “minute” ont un sens pour lecteur. Mais pour compilateur, ce sont des int, donc pas d'interdiction d'addition. Sémantique incomplète
Dans la suite, apprendre à créer ses types et créer une sémantique (des “règles” d'utilisation).
représentation des nombres entier négatif
limitation de représentation d'un nombre “réel” par une représentation limité en mémoire