Quelle est la différence entre "int" et "int_fast16_t"?
Si je comprends bien, la spécification C dit que le type int
est censé être le type le plus efficace sur la plate-forme cible qui contient au moins 16 bits.
N'est-ce pas exactement ce que la définition C99 de int_fast16_t
est aussi?
Peut-être qu'ils l'ont mis là juste pour des raisons de cohérence, puisque les autres int_fastXX_t
sont nécessaires?
Mise à jour
Pour résumer la discussion ci-dessous:
- Ma question était fausse à bien des égards. La norme C ne spécifie pas bitness pour int. Il donne une plage [-32767,32767] qu'il doit contenir.
- Je me rends compte au début que la plupart des gens diraient, "mais cette plage implique au moins 16 bits!" Mais C ne nécessite pas de stockage à deux compléments (ou même binaire) d'entiers. S'ils avaient dit "16 bits", il se peut que certaines plates-formes aient une parité de 1 bit, un signe de 1 bit et une magnitude de 14 bits qui soient toujours "conformes à la norme", mais ne satisfassent pas cette plage.
- La norme ne dit rien sur int étant le type le plus efficace. Outre les exigences de taille ci-dessus, int peut être décidé par le développeur du compilateur en fonction des critères qu'il juge les plus importants. (vitesse, taille, rétrocompatibilité, etc.)
- D'autre part, int_fast16_t revient à fournir au compilateur une indication qu'il devrait utiliser un type optimal pour les performances, peut-être au détriment de tout autre compromis.
- De même, int_least16_t indiquerait au compilateur d'utiliser le plus petit type>> 16 bits, même s'il serait plus lent. Bon pour préserver l'espace dans les grands tableaux et autres.
Exemple: MSVC sur x86-64 a un 32 bits int, même sur les systèmes 64 bits. MS a choisi de le faire parce que trop de gens pensaient que int serait toujours exactement 32 bits, et donc beaucoup d'ABI se briseraient. Cependant, il est possible que int_fast32_t soit un nombre 64 bits si les valeurs 64 bits étaient plus rapides sur x86-64. (Ce qui, je ne pense pas, est en fait le cas, mais cela démontre juste le point)
int_fast16_t
est garanti pour être l'int entier le plus rapide avec une taille d'au moins 16 bits. int
n'a aucune garantie de sa taille sauf que:
sizeof(char) = 1 and sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long).
Et qu'il peut contenir la plage de -32767 à +32767.
(7.20.1.3p2) "Le nom du typedef
int_fastN_t
désigne le type d'entier signé le plus rapide avec une largeur d'au moins N . Le nom du typedefuint_fastN_t
désigne le type d'entier non signé le plus rapide avec une largeur d'au moins N . "
int
est un "type le plus efficace" en vitesse/taille - mais qui n'est pas spécifié par la spécification C. Il doit être de 16 bits ou plus.
int_fast16_t
est le type de vitesse le plus efficace avec au moins la plage d'un entier 16 bits.
Exemple: Une plate-forme donnée peut avoir décidé que int
devrait être 32 bits pour de nombreuses raisons, pas seulement pour la vitesse. Le même système peut trouver qu'un type différent est le plus rapide pour les entiers 16 bits.
Exemple: Dans une machine 64 bits, où l'on s'attendrait à avoir int
en 64 bits, un compilateur peut utiliser un mode avec une compilation 32 bits int
pour la compatibilité. Dans ce mode, int_fast16_t
pourrait être de 64 bits car c'est la largeur la plus rapide car elle évite les problèmes d'alignement, etc.
Si je comprends bien, la spécification C dit que le type
int
est censé être le type le plus efficace sur la plate-forme cible qui contient au moins 16 bits.
Voici ce que la norme dit réellement à propos de int
: ( N1570 draft , section 6.2.5, paragraphe 5):
Un objet "ordinaire"
int
a la taille naturelle suggérée par l'architecture de l'environnement d'exécution (suffisamment grande pour contenir toute valeur dans le plageINT_MIN
àINT_MAX
tel que défini dans l'en-tête<limits.h>
).
La référence à INT_MIN
et INT_MAX
est peut-être un peu trompeur; ces valeurs sont choisies en fonction des caractéristiques de type int
, et non l'inverse.
Et l'expression " la taille naturelle " est également légèrement trompeuse. Selon l'architecture cible, il peut ne pas y avoir qu'une seule taille "naturelle" pour un type entier.
Ailleurs, la norme dit que INT_MIN
doit être au plus -32767
, et INT_MAX
doit être au moins +32767
, ce qui implique que int
est d'au moins 16 bits.
Voici ce que dit la norme à propos de int_fast16_t
(7.20.1.3):
Chacun des types suivants désigne un type entier qui est généralement le plus rapide à utiliser avec tous les types entiers qui ont au moins la largeur spécifiée.
avec une note de bas de page:
Le type désigné n'est pas garanti pour être le plus rapide à toutes fins; si la mise en œuvre n'a pas de raisons claires de choisir un type plutôt qu'un autre, elle choisira simplement un type entier satisfaisant aux exigences de signature et de largeur.
Les exigences pour int
et int_fast16_t
sont similaires mais pas identiques - et ils sont tout aussi vagues.
Dans la pratique, la taille de int
est souvent choisie en fonction de critères autres que "la taille naturelle" - ou cette phrase est interprétée pour plus de commodité. Souvent, la taille de int
pour une nouvelle architecture est choisie pour correspondre à la taille d'une architecture existante, afin de minimiser la difficulté de porter du code. Et il y a une motivation assez forte pour que int
ne dépasse pas 32 bits, de sorte que les types char
, short
et int
puissent couvrir des tailles de 8 , 16 et 32 bits. Sur les systèmes 64 bits, en particulier x86-64, la taille "naturelle" est probablement de 64 bits, mais la plupart des compilateurs C font int
32 bits plutôt que 64 (et certains compilateurs font même long
juste 32 bits).
Le choix du type sous-jacent pour int_fast16_t
est, je le soupçonne, moins dépendant de telles considérations, car tout code qui l'utilise demande explicitement un type entier signé 16 bits rapide. Beaucoup de code existant émet des hypothèses sur les caractéristiques de int
qui vont au-delà de ce que les garanties standard, et les développeurs de compilateurs doivent répondre à ce code s'ils veulent que leurs compilateurs soient utilisés.
De la justification C99 7.8
Conversion de format des types entiers <inttypes.h>
(document qui accompagne le Standard), accentuez le mien:
C89 spécifie que le langage doit prendre en charge quatre types de données entiers signés et non signés,
char
,short
,int
etlong
, mais impose très peu d'exigences à leur taille à part celaint
etshort
doit être d'au moins 16 bits etlong
doit être au moins aussi long queint
et pas inférieur à 32 bits. Pour les systèmes 16 bits, la plupart des implémentations affectent respectivement 8, 16, 16 et 32 bits àchar
,short
,int
etlong
. Pour les systèmes 32 bits, la pratique courante consiste à affecter 8, 16, 32 et 32 bits à ces types. Cette différence de tailleint
peut créer des problèmes pour les utilisateurs qui migrent d'un système à un autre qui attribue différentes tailles aux types d'entiers, car la règle de promotion d'entiers Standard C peut produire des modifications silencieuses de manière inattendue. La nécessité de définir un type entier étendu a augmenté avec l'introduction des systèmes 64 bits.Le but de
<inttypes.h>
est de fournir un ensemble de types entiers dont les définitions sont cohérentes sur toutes les machines et indépendantes des systèmes d'exploitation et autres idiosyncrasies d'implémentation. Il définit, viatypedef
, des types entiers de différentes tailles. Les implémentations sont libres detypedef
en tant que types d'entiers C standard ou extensions qu'ils prennent en charge. Une utilisation cohérente de cet en-tête augmentera considérablement la portabilité du programme d'un utilisateur sur toutes les plateformes.
La principale différence entre int
et int_fast16_t
est que ce dernier est susceptible d'être exempt de ces "idiosyncrasies d'implémentation". Vous pouvez le considérer comme quelque chose comme:
Je ne me soucie pas de la "politique" actuelle de l'OS/implémentation de la taille int
. Donnez-moi simplement le type d'entier signé le plus rapide avec au moins 16 bits.
La différence est que les types rapides peuvent être plus larges que leurs homologues (sans rapide ) à des fins d'efficacité/d'optimisation. Mais le standard C ne garantit en aucun cas qu'ils sont réellement plus rapides.
C11, 7.20.1.3 Types d'entiers de largeur minimale les plus rapides
1 Chacun des types suivants désigne un type entier qui est généralement le plus rapide 262) pour fonctionner avec tous les types entiers qui ont au moins la largeur spécifiée.
2 Le nom de typedef int_fastN_t désigne le type d'entier signé le plus rapide avec une largeur d'au moins N. Le nom de typedef uint_fastN_t désigne le type d'entier non signé le plus rapide avec une largeur d'au moins N.
262) Le type désigné n'est pas garanti d'être le plus rapide à toutes fins; si la mise en œuvre n'a pas de raisons claires de choisir un type plutôt qu'un autre, elle choisira simplement un type entier satisfaisant aux exigences de signature et de largeur.
Une autre différence est que les types rapides et les moins sont des types obligatoires tandis que d'autres exacts les types de largeur sont facultatifs :
3 Les types suivants sont requis: int_fast8_t int_fast16_t int_fast32_t int_fast64_t uint_fast8_t uint_fast16_t uint_fast32_t uint_fast64_t Tous les autres types de ce formulaire sont facultatifs.
Sur certaines plates-formes, l'utilisation de valeurs 16 bits peut être beaucoup plus lente que l'utilisation de valeurs 32 bits [par ex. une mémoire 8 bits ou 16 bits nécessiterait d'effectuer une charge 32 bits, de modifier la valeur chargée et de réécrire le résultat]. Même si l'on pouvait contenir deux fois plus de valeurs 16 bits dans un cache que de valeurs 32 bits (la situation normale où les valeurs 16 bits seraient plus rapides que les valeurs 32 bits sur les systèmes 32 bits), la nécessité d'avoir chaque écriture précédé d'une lecture annulerait tout avantage de vitesse qui pourrait se produire à moins qu'une structure de données ne soit lue beaucoup plus souvent qu'elle ne l'a été. Sur ces plateformes, un type comme int_fast16_t
serait probablement 32 bits.
Cela dit, la norme ne permet malheureusement pas ce qui serait la sémantique la plus utile pour un compilateur, qui serait d'autoriser des variables de type int_fast16_t
dont l'adresse n'est pas considérée comme se comportant arbitrairement comme des types 16 bits ou des types plus grands, selon ce qui convient. Considérez, par exemple, la méthode:
int32_t blah(int32_t x)
{
int_fast16_t y = x;
return y;
}
Sur de nombreuses plates-formes, les entiers 16 bits stockés en mémoire peuvent souvent être manipulés de la même manière que ceux stockés dans les registres, mais il n'y a pas d'instructions pour effectuer des opérations 16 bits sur les registres. Si un int_fast16_t
les variables stockées en mémoire ne peuvent contenir que -32768 à +32767, cette même restriction s'appliquerait à int_fast16_t
variables stockées dans des registres. Étant donné que la contrainte de valeurs surdimensionnées dans des types entiers signés trop petits pour les contenir est un comportement défini par l'implémentation, cela obligerait le code ci-dessus à ajouter des instructions pour signer-étendre les 16 bits inférieurs de x
avant de le renvoyer; si la norme autorisait un tel type, un type flexible "d'au moins 16 bits, mais plus si commode" pourrait éliminer le besoin de telles instructions.
Un exemple de la façon dont les deux types peuvent être différents: supposons qu'il existe une architecture où l'arithmétique 8 bits, 16 bits, 32 bits et 64 bits est tout aussi rapide. (Le i386 se rapproche.) Ensuite, l'implémenteur peut utiliser un modèle LLP64, ou mieux encore permettre au programmeur de choisir entre ILP64, LP64 et LLP64, car il y a beaucoup de code qui suppose que long est exactement 32 bits, et que sizeof(int) <= sizeof(void*) <= sizeof(long)
. Toute implémentation 64 bits doit violer au moins une de ces hypothèses.
Dans ce cas, int
aurait probablement une largeur de 32 bits, car cela détruirait le moins de code des autres systèmes, mais uint_fast16_t
peut encore avoir une largeur de 16 bits, ce qui économise de l'espace.