Article original : About sizes | Belay the C++ (belaycpp.com)
Traductrice : Chloé Lourseyre
Si j’étais venue ce matin avec une interro surprise sur les tailles des types fondamentaux en C++, beaucoup d’entre nous l’aurait sans doute échoué (moi y compris). La raison à cela : les tailles en C++ sont compliquées.
La taille de chaque type fondamental n’est pas fixée, elle dépend toujours de l’architecture (elle est « implementation-defined« ).
Pourtant, le standard impose des contraintes à ces tailles. Ces contraintes peuvent prendre deux formes différentes :
- Une comparaison des
sizeof
des types - Le nombre minimum de bits qui composent un type
Qu’est-ce que sizeof() ?
Une des idées reçues les plus largement propagées (même si inoffensive) est celle selon laquelle un octet est composé de 8 bits.
Même si la plupart du temps c’est vrai en pratique, c’est techniquement faux.
Un byte (anglais de octet1) est en réalité défini par la taille d’un char
. Bien qu’un char
fasse toujours au moins 8 bits, il peut être plus grand. Dans tous les cas, on part de la définition qu’un byte est la taille d’un char
.
En C++, toutes les tailles sont des multiples de la taille d’un char
. Ainsi, la fonction sizeof(N)
renvoie le nombre de bytes qui composent le type N
.
De ce fait, si sizeof(int)
vaut quatre dans une architecture donnée, cela veut dire qu’il vaut la taille de 4 char
, donc au moins 32 bits. Il peut cependant être plus grand. Si un char
mesure 32 bits, alors un int
fera, dans ce contexte, 128 bits.
La véritable taille d’un byte est enregistrée dans la constante CHAR_BIT
.
1. À partir de là et jusqu’à la fin de l’article, j’utiliserai le terme anglais byte à la place de l’équivalent français octet, car ce dernier est, de fait, étymologiquement inexact.
Résumé des tailles en C++
Voici l’intégralité des contraintes de taille sur les types fondamentaux en C++ :
1 ≡ sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long)
1 ≤ sizeof(bool) ≤ sizeof(long)
sizeof(char) ≤ sizeof(wchar_t) ≤ sizeof(long)
sizeof(float) ≤ sizeof(double) ≤ sizeof(long double)
sizeof(N) ≡ sizeof(unsigned N) ≡ sizeof(signed N)
- Un
char
fait au moins 8 bits - Un
short
fait au moins 16 bits - Un
long
fait au moins 32 bits
… et c’est tout.
Fait amusant : selon cette définition, il est techniquement valide d’avoir une architecture où tous les types fondamentaux font 32 bits.
Deux mots de sagesse
Puisque les tailles des types fondamentaux dépendent entièrement de l’architecture, il peut être parfois complexe d’écrire du code fiable.
#include <limits>
L’include limits
de la librairie standard contient les bornes supérieures et inférieures de tous les types fondamentaux. Il vous permet en plus de savoir si un type est signé ou pas.
Exemple :
#include <limits>
#include <iostream>
int main()
{
std::cout << "largest double == " << std::numeric_limits<double>::max() << std::endl;
std::cout << "char is signed == " << std::numeric_limits<char>::is_signed << std::endl;
}
Plus d’informations ici : std::numeric_limits – cppreference.com.
Rappel : l’overflow d’entier signé est un comportement indéfini. Utiliser les limites vous permettra d’éviter cela.
#include <cstdint>
Parfois on veut utiliser directement des types de taille définie. Quand on écrit une classe de sérialisation, quand on travaille sur des systèmes à mémoire très limitée ou quand on veut que le code soit compatible cross-plateforme, on veut pouvoir utiliser des types qui ont une longueur (en terme de bits) prédéfinie.
C’est possible avec la librairie cstdint
qui contient des types de taille fixe.
En voici quelques-uns:
int8_t int16_t int32_t int64_t | Signed integer type with width of exactly 8, 16, 32 and 64 bits respectively with no padding bits and using 2’s complement for negative values (provided only if the implementation directly supports the type) |
int_least8_t int_least16_t int_least32_t int_least64_t | Smallest signed integer type with width of at least 8, 16, 32 and 64 bits respectively |
intmax_t | Maximum-width signed integer type |
uint8_t uint16_t uint32_t uint64_t | Unsigned integer type with width of exactly 8, 16, 32 and 64 bits respectively (provided only if the implementation directly supports the type) |
uint_least8_t uint_least16_t uint_least32_t uint_least64_t | smallest unsigned integer type with width of at least 8, 16, 32 and 64 bits respectively |
uintmax_t | maximum-width unsigned integer type |
Plus d’informations ici : Fixed width integer types (since C++11) – cppreference.com.
En conclusion
Si vous voulez en lire plus à propos des tailles de types, je vous renvoie à la section §6.2.8 de l’ouvrage The C++ Langage (de Bjarne Stroustrup). Plus largement, vous pouvez vous documenter à propos des types et déclaration dans toute la section §6 du livre.
Vous pouvez aussi aller voir Fundamental types – cppreference.com si vous préférez la documentation en ligne.
Merci de votre attention et à la semaine prochaine !
Article original : About sizes | Belay the C++ (belaycpp.com)
Traductrice : Chloé Lourseyre