Outils d'utilisateurs

Outils du Site


types_en_detail

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

Les types en profondeur

Taille des variables

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).

main.cpp
#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 :

main.cpp
#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 :

main.cpp
#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.

main.cpp
#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 ;

main.cpp
#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

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).

Les modificateurs de type

Les nombres entiers

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.

main.cpp
#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)

main.cpp
#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 ? )

Les nombres réels

Pour les nombres réels, un peu différent :

main.cpp
#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

Tester les types

digits ? digits10 ? max_digits10 ? radix ?

  • is_signed
  • is_integer
  • is_exact
  • min
  • max
  • lowest
  • epsilon

Tester les valeurs

  • is_nan
  • is_finite
  • is_infinite

Les littérales modifiées

utiliation de u, ul, ull pour modifier une littérale entier et f et l pour une littérale réelle.

Limites des types numériques

max, min, epsilon, etc

Créer ses types

Définir des types

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)

Type et sémantique

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

représentation des nombres entier négatif

limitation de représentation d'un nombre “réel” par une représentation limité en mémoire

types_en_detail.1401907157.txt.gz · Dernière modification: 2014/06/04 20:39 par gbdivers