Dans le monde multi-plateforme C ++ (ou C) actuel, nous avons :
Data model | short | int | long | long long | pointers/size_t | Sample operating systems
...
LLP64/IL32P64 16 32 32 64 64 Microsoft Windows (x86-64 and IA-64)
LP64/I32LP64 16 32 64 64 64 Most Unix and Unix-like systems, e.g. Solaris, Linux, BSD, and OS X; z/OS
...
Ce que cela signifie aujourd’hui, c’est que, pour tout entier «commun» (signé), int
cela suffira et peut toujours être utilisé comme type d’entier par défaut lors de l’écriture du code d’application C ++. Pour les besoins pratiques actuels, il aura également une taille uniforme sur toutes les plateformes.
Ssi un cas d'utilisation nécessite au moins 64 bits, on peut utiliser aujourd'hui long long
, mais peut - être en utilisant l' un des types de bitness spécifiant ou le __int64
type peut plus logique.
Cela laisse long
au milieu, et nous envisageons d'interdire carrément l'utilisation de long
notre code d'application .
Est-ce que cela aurait du sens ou est-il judicieux d’utiliser long
du code C ++ (ou C) moderne qui doit fonctionner sur plusieurs plates-formes? (la plate-forme étant un ordinateur de bureau, des appareils mobiles, mais pas des choses comme des microcontrôleurs, des DSP, etc.)
Liens de fond éventuellement intéressants:
- En quoi la norme C ++ définit-elle la taille de type int, long?
- Pourquoi l'équipe Win64 a-t-elle choisi le modèle LLP64?
- Modèles de programmation 64 bits: Pourquoi LP64? (un peu âgé)
- Est-il
long
garanti d'avoir au moins 32 bits? (Cela répond à la discussion ci-dessous. Réponse .)
la source
long
est le seul moyen de garantir 32 bits.int
peut être 16 bits, donc pour certaines applications, cela ne suffit pas. Oui,int
c’est parfois 16 bits sur les compilateurs modernes. Oui, les gens écrivent des logiciels sur des microcontrôleurs. Je dirais que plus de gens écrivent des logiciels qui ont plus d'utilisateurs sur des microcontrôleurs que sur des PC avec la montée des appareils iPhone et Android, sans parler de la montée des Arduinos, etc.int
reste encore beaucoup 16 bits. Je déteste le dire, mais si vous écrivez à propos du "monde multiplateforme d'aujourd'hui", vous ne pouvez pas ignorer le sous-continent indien dans son ensemble.Réponses:
La seule raison pour laquelle j'utiliserais
long
aujourd'hui est lorsque vous appelez ou implémentez une interface externe qui l'utilise.Comme vous le dites dans votre message, short et int ont des caractéristiques raisonnablement stables sur toutes les principales plates-formes de bureau / serveur / mobile actuelles et je ne vois aucune raison pour que cela change dans un avenir proche. Je vois donc peu de raisons de les éviter en général.
long
de l'autre côté est un gâchis. Je sais que sur tous les systèmes 32 bits, il présentait les caractéristiques suivantes.De grandes quantités de code ont été écrites en fonction d’une ou de plusieurs de ces caractéristiques. Cependant, avec le passage à la version 64 bits, il n’a pas été possible de toutes les conserver. Les plates-formes de type Unix ont opté pour LP64 qui a conservé les caractéristiques 2 et 3 au détriment de la caractéristique 1. Win64 a opté pour LLP64 qui a conservé la caractéristique 1 au détriment des caractéristiques 2 et 3. Il en résulte que vous ne pouvez plus vous en servir. et que l’OMI laisse peu de raisons d’utiliser
long
.Si vous voulez un type dont la taille est exactement 32 bits, vous devriez utiliser
int32_t
.Si vous voulez un type de la même taille qu'un pointeur, vous devez utiliser
intptr_t
(ou mieuxuintptr_t
).Si vous voulez un type qui soit l'élément le plus volumineux pouvant être traité dans un seul registre / instruction, je ne pense malheureusement pas que la norme en fournisse un.
size_t
devrait être juste sur la plupart des plateformes courantes mais ce ne serait pas sur x32 .PS
Je ne m'embêterais pas avec les types "rapide" ou "le moins". Les types "les moins" ne comptent que si vous vous souciez de la portabilité pour vraiment obscurcir les architectures où
CHAR_BIT != 8
. La taille des types "rapides" dans la pratique semble être assez arbitraire. Linux semble leur donner au moins la même taille que le pointeur, ce qui est ridicule sur les plates-formes 64 bits avec un support 32 bits rapide comme x86-64 et arm64. IIRC iOS les rend aussi petites que possible. Je ne suis pas sûr de ce que font les autres systèmes.PPS
Une des raisons d'utiliser
unsigned long
(mais pas clairementlong
) est parce qu'il est gaurante d'avoir un comportement modulo. Malheureusement, en raison de la confusion des règles de promotion de C, les types non signés plus petits que ceux quiint
n'ont pas de comportement modulo.Aujourd'hui, sur toutes les grandes plates-formes,
uint32_t
la taille est supérieure ou égale à celle de l'int et a donc un comportement modulo. Cependant, il y a eu historiquement et il pourrait théoriquement y avoir dans les futures plates-formes oùint
est 64 bits etuint32_t
n'a donc pas de comportement modulo.Personnellement, je dirais qu'il est préférable de forcer le comportement modulo en utilisant "1u *" ou "0u +" au début de vos équations car cela fonctionnera pour toute taille de type non signé.
la source
Comme vous l'avez mentionné dans votre question, un logiciel moderne consiste essentiellement à interopérer entre plates-formes et systèmes sur Internet. Les normes C et C ++ donnent des plages pour les tailles de type entier, et non des tailles spécifiques (contrairement aux langages tels que Java et C #).
Pour que votre logiciel compilé sur différentes plates-formes fonctionne avec les mêmes données de la même manière et que d'autres logiciels puissent interagir avec votre logiciel avec les mêmes tailles, vous devez utiliser des entiers de taille fixe.
Entrez
<cstdint>
ce qui fournit exactement cela et constitue un en-tête standard que toutes les plateformes de compilateur et de bibliothèque standard doivent fournir. Remarque: cet en-tête n'était requis qu'à partir de C ++ 11, mais de nombreuses implémentations de bibliothèques plus anciennes le fournissaient quand même.Vous voulez un entier non signé 64 bits? Utilisez
uint64_t
. Nombre entier 32 bits signé? Utilisezint32_t
. Bien que les types figurant dans l'en-tête soient facultatifs, les plates-formes modernes devraient prendre en charge tous les types définis dans cet en-tête.Parfois, une largeur de bits spécifique est nécessaire, par exemple, dans une structure de données utilisée pour communiquer avec d'autres systèmes. D'autres fois ce n'est pas. Pour les situations moins strictes,
<cstdint>
fournit des types de largeur minimale.Il y a moins de variantes: il
int_leastXX_t
s'agira d'un type entier d'au moins XX bits. Il utilisera le plus petit type fournissant XX bits, mais il est permis que le type soit plus grand que le nombre de bits spécifié. En pratique, ce sont généralement les mêmes que ceux décrits ci-dessus qui donnent le nombre exact de bits.Il existe également des variantes rapides :
int_fastXX_t
au moins XX bits, mais il convient d'utiliser un type rapide sur une plate-forme donnée. La définition de "rapide" dans ce contexte n'est pas spécifiée. Cependant, dans la pratique, cela signifie généralement qu'un type inférieur à la taille de registre de la CPU peut être associé à un type de la taille de registre de la CPU. Par exemple, l'en-tête de Visual C ++ 2015 spécifie qu'ilint_fast16_t
s'agit d'un entier de 32 bits, car l'arithmétique de 32 bits est globalement plus rapide sur l'arithmétique x86 que sur 16 bits.Tout cela est important car vous devriez pouvoir utiliser des types pouvant contenir les résultats des calculs effectués par votre programme, quelle que soit la plate-forme. Si un programme produit des résultats corrects sur une plate-forme mais des résultats incorrects sur une autre en raison de différences de dépassement d'entier, c'est mauvais. En utilisant les types d'entiers standard, vous garantissez que les résultats sur différentes plates-formes seront identiques en ce qui concerne la taille des entiers utilisés (bien entendu, il peut exister d'autres différences entre les plates-formes en plus de la largeur d'entier).
Donc, oui,
long
devrait être banni du code C ++ moderne. Donc , devraitint
,short
etlong long
.la source
std
espace de noms lorsque vous vous trouvez dans#include
une unité de compilation C ++, mais la documentation que j’ai liée ne le mentionne pas et Visual Studio ne semble pas se soucier de la façon dont j’y accède.int
peut être ... excessif? . (Je considérerais si le code doit être extrêmement portable dans tous les obscurs (et pas si obscure) plates - formes Banning pour « code de l' application » ne peut pas s'asseoir très bien avec nos devs.#include <cstdint>
est requis pour mettre les typesstd::
et (malheureusement) éventuellement éventuellement aussi pour les mettre dans l'espace de noms global.#include <stdint.h>
est exactement l'inverse. La même chose s'applique à toute autre paire d'en-têtes C. Voir: stackoverflow.com/a/13643019/2757035 J'aurais aimé que la norme impose à chacun de n'affecter que son espace de noms requis respectif - plutôt que de se plier en apparence aux mauvaises conventions établies par certaines implémentations - mais bon, nous en sommes à présent.Non, interdire les types d'entiers intégrés serait absurde. Cependant, ils ne doivent pas non plus être maltraités.
Si vous avez besoin d'un entier de exactement N bits de large, utilisez (ou si vous avez besoin d'une version). Considérer comme un entier 32 bits et comme un entier 64 bits est tout simplement faux. C'est peut-être le cas sur vos plates-formes actuelles, mais cela repose sur un comportement défini par l'implémentation.
std::intN_t
std::uintN_t
unsigned
int
long long
L'utilisation de types entiers à largeur fixe est également utile pour interagir avec d'autres technologies. Par exemple, si certaines parties de votre application sont écrites en Java et d'autres en C ++, vous souhaiterez probablement faire correspondre les types d'entiers afin d'obtenir des résultats cohérents. (Gardez toujours à l'esprit que les débordements dans Java ont une sémantique bien définie, tandis que les
signed
dépassements en C ++ représentent un comportement non défini. La cohérence est donc un objectif primordial.) Ils seront également précieux pour l'échange de données entre différents hôtes informatiques.Si vous n'avez pas besoin exactement de N bits, mais simplement d'un type suffisamment large , envisagez d'utiliser (optimisé pour l'espace) ou (optimisé pour la vitesse). Encore une fois, les deux familles ont aussi leurs homologues.
std::int_leastN_t
std::int_fastN_t
unsigned
Alors, quand utiliser les types prédéfinis? Eh bien, étant donné que la norme ne spécifie pas précisément leur largeur, utilisez-les lorsque vous ne vous souciez pas de la largeur réelle des bits, mais d’autres caractéristiques.
A
char
est le plus petit entier adressable par le matériel. Le langage vous oblige en fait à l’utiliser pour aliaser de la mémoire arbitraire. C'est également le seul type viable pour représenter des chaînes de caractères (étroites).An
int
sera généralement le type le plus rapide que la machine puisse gérer. Il sera suffisamment large pour pouvoir être chargé et stocké avec une seule instruction (sans masquer ni décaler les bits) et suffisamment étroit pour pouvoir être utilisé avec les instructions matérielles (les plus efficaces). Par conséquent,int
est un choix parfait pour transmettre des données et faire du calcul lorsque les débordements ne sont pas un sujet de préoccupation. Par exemple, le type d'énumérations sous-jacent par défaut estint
. Ne le changez pas en un entier 32 bits simplement parce que vous le pouvez. De plus, si vous avez une valeur qui ne peut être que –1, 0 et 1, unint
est un choix parfait, sauf si vous allez en stocker d'énormes tableaux, auquel cas vous voudrez peut-être utiliser un type de données plus compact au prix de payer un prix plus élevé pour accéder à des éléments individuels. Une mise en cache plus efficace sera probablement rentable. De nombreuses fonctions du système d'exploitation sont également définies en termes deint
. Il serait stupide de convertir leurs arguments et leurs résultats dans les deux sens. Tout ce que cela pourrait faire serait d’ introduire des erreurs de débordement.long
sera généralement le type le plus large pouvant être traité avec des instructions machine simples. Cela est particulièrementunsigned long
intéressant pour traiter des données brutes et toutes sortes de manipulations de bits. Par exemple, je m'attendrais à voirunsigned long
dans la mise en œuvre d'un vecteur de bits. Si le code est écrit avec soin, la largeur du type importe peu (car le code s'adapte automatiquement). Sur les plateformes où le mot machine natif est 32 bits, le tableau de sauvegarde du vecteur de bits étant un tableau deunsigned
Les entiers 32 bits sont les plus souhaitables car il serait idiot d'utiliser un type 64 bits qui doit être chargé via des instructions coûteuses uniquement pour décaler et masquer les bits inutiles. D'autre part, si la taille de mot native de la plate-forme est de 64 bits, je souhaite un tableau de ce type, car cela signifie que des opérations telles que «trouver le premier ensemble» peuvent être exécutées jusqu'à deux fois plus vite. Ainsi, le «problème» dulong
type de données que vous décrivez, du fait que sa taille varie d'une plate-forme à l'autre, est en fait une fonctionnalité qui peut être utilisée à bon escient. Cela ne devient un problème que si vous considérez les types intégrés comme des types d'une certaine largeur de bit, ce qu'ils ne sont tout simplement pas.char
,int
etlong
sont des types très utiles comme décrit ci-dessus.short
etlong long
ne sont pas aussi utiles parce que leur sémantique est beaucoup moins claire.la source
long
entre Windows et Unix. Je me trompe peut-être, mais votre description de la différence de taille entrelong
une "fonctionnalité" et non un "problème" me semble logique pour comparer des modèles de données 32 bits et 64 bits, mais pas pour cette comparaison particulière. Dans le cas particulier de cette question, s'agit-il vraiment d'une fonctionnalité? Ou est-ce une caractéristique dans d'autres situations (c'est-à-dire en général) et inoffensive dans ce cas?uint32_t
valeurs sera effectuée comme signé , l'int
arithmétique -width sur une plate - forme oùint
est plus large queuint32_t
. (Avec les ABI d'aujourd'hui, ce problème est beaucoup plus susceptible de poser problèmeuint16_t
.)long
sera généralement le type le plus large qui puisse être traité avec des instructions machine simples. ..." - et c'est tout à fait faux . Regardez le modèle de données Windows. IMHO, votre exemple suivant entier tombe en panne, car sur Windows x64 long est toujours 32 bits.Une autre réponse précise déjà les types de cstdint et leurs variantes moins connues.
J'aimerais ajouter à cela:
utiliser des noms de type spécifiques au domaine
Autrement dit, ne déclarez pas vos paramètres et variables comme étant
uint32_t
(certainement paslong
!), Mais des noms tels quechannel_id_type
,room_count_type
etc.à propos des bibliothèques
Les bibliothèques tierces qui utilisent
long
ou non peuvent être ennuyantes, surtout si elles sont utilisées comme références ou pointeurs.La meilleure chose à faire est de faire des wrappers.
Ce que ma stratégie est, en général, est de créer un ensemble de fonctions de type cast qui seront utilisées. Ils sont surchargés pour accepter uniquement les types qui correspondent exactement aux types correspondants, ainsi que les variations de pointeur, etc., dont vous avez besoin. Ils sont définis spécifiques à os / compiler / settings. Cela vous permet de supprimer les avertissements tout en garantissant que seules les "bonnes" conversions sont utilisées.
En particulier, avec différents types de primitifs produisant 32 bits, votre choix de
int32_t
définition peut ne pas correspondre à l'appel de la bibliothèque (par exemple, int vs long sous Windows).La fonction de type transtypage documente le conflit, fournit une vérification au moment de la compilation sur le résultat correspondant au paramètre de la fonction et supprime tout avertissement ou toute erreur si et seulement si le type réel correspond à la taille réelle impliquée. Autrement dit, il est surchargé et défini si je passe (sous Windows) un
int*
ou unlong*
et donne une erreur de compilation, sinon.Ainsi, si la bibliothèque est mise à jour ou si quelqu'un modifie ce qui
channel_id_type
est, cela continue d'être vérifié.la source