Quelle est la différence entre “int” et “int_fast16_t”?

Si je comprends bien, la spécification C indique 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 l’ont-ils mis là juste pour la cohérence, puisque les autres int_fastXX_t sont nécessaires?

Mettre à jour

Pour résumer la discussion ci-dessous:

  • Ma question était fausse à bien des égards. La norme C ne spécifie pas le 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 n’a pas besoin de stocker des entiers avec un complément à deux (ni même binary). S’ils avaient dit «16 bits», il se peut que certaines plates-formes ayant une parité à 1 bit, un signe à 1 bit et une magnitude de 14 bits soient toujours «conformes à la norme», mais ne répondent pas à cette plage.
  • La norme ne dit rien sur le fait que int soit le type le plus efficace. Outre les exigences de taille ci-dessus, int peut être choisi par le développeur du compilateur en fonction des critères qu’il juge les plus importants. (vitesse, taille, compatibilité ascendante, etc.)
  • En revanche, int_fast16_t revient à indiquer au compilateur qu’il doit utiliser un type optimal en termes de performances, éventuellement au désortingment 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 de l’espace dans les grands tableaux et d’autres choses.

Exemple: MSVC sur x86-64 a un int 32 bits, même sur des systèmes 64 bits. MS a choisi de faire cela parce que trop de gens pensaient que int serait toujours exactement 32 bits, ce qui ferait craquer de nombreuses ABI. Cependant, il est possible que int_fast32_t soit un nombre 64 bits si les valeurs 64 bits étaient plus rapides sous x86-64. (Ce qui, à mon avis, n’est pas le cas, mais cela montre bien le problème)

int_fast16_t est le plus rapide des int avec une taille d’au moins 16 bits. int n’a aucune garantie de sa taille si ce n’est que:

  sizeof(char) = 1 and sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long). 

Et qu'il peut contenir la plage -32767 à +32767.

(7.20.1.3p2) "Le nom de typedef int_fastN_t désigne le type d'entier le plus rapide signé 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. "

int est un “type le plus efficace” en vitesse / taille – mais cela n’est pas spécifié par la spécification C. Ce doit être 16 bits ou plus.

int_fast16_t est le type le plus efficace en vitesse avec au moins la plage d’un int de 16 bits.

Exemple: une plate-forme donnée peut avoir décidé 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 à ce que int 64 bits, un compilateur peut utiliser un mode avec une compilation 32 bits int pour des raisons de compatibilité. Dans ce mode, int_fast16_t pourrait être en 64 bits, car il s’agit en fait de la largeur la plus rapide car il évite les problèmes d’alignement, etc.

Si je comprends bien, la spécification C indique 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 en réalité à propos de int : ( N1570 draft , section 6.2.5, paragraphe 5):

Un object int “plain” a la taille naturelle suggérée par l’architecture de l’environnement d’exécution (assez grand pour contenir une valeur comprise dans la plage INT_MIN à INT_MAX telle que définie dans l’en-tête ).

La référence à INT_MIN et INT_MAX est peut-être légèrement trompeuse; 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 exister une seule taille “naturelle” pour un type entier.

Ailleurs, la norme indique INT_MIN doit être au plus de -32767 et INT_MAX doit être au moins de +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 ayant au moins la largeur spécifiée.

avec une note de bas de page:

Il n’est pas garanti que le type désigné soit le plus rapide à toutes fins utiles; si l’implémentation n’a pas de raison claire de choisir un type plutôt qu’un autre, il choisira simplement un type entier satisfaisant aux exigences de signature et de largeur.

Les exigences pour int et int_fast16_t sont similaires mais non identiques – et elles sont également vagues.

En 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 simplement. 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 portage du code. Et il existe une motivation assez forte pour que int ne soit pas plus large que 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 les x86-64, la taille “naturelle” est probablement de 64 bits, mais la plupart des compilateurs C utilisent int 32 bits au lieu de 64 (et certains compilateurs ne font même que 32 bits de long ).

Je suppose que le choix du type sous-jacent pour int_fast16_t dépend moins de ces considérations, car tout code qui l’utilise demande explicitement un type entier rapide signé de 16 bits. Un grand nombre de codes existants font des hypothèses sur les caractéristiques d’ int qui vont au-delà de ce que la norme garantit, et les développeurs de compilateurs doivent prendre en charge ce code s’ils veulent que leurs compilateurs soient utilisés.

La différence est que les types rapides sont autorisés à être plus larges que leurs homologues (sans rapide ) à des fins d’efficacité / d’optimisation. Mais la norme 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) à utiliser avec tous les types entiers ayant au moins la largeur spécifiée.

2 Le nom typedef int_fastN_t désigne le type entier le plus rapide avec une largeur d’au moins N. Le nom typedef uint_fastN_t désigne le type le plus rapide non signé avec une largeur d’au moins N.

262) Il n’est pas garanti que le type désigné soit le plus rapide à toutes fins; si l’implémentation n’a pas de raison claire de choisir un type plutôt qu’un autre, il choisira simplement un type entier satisfaisant aux exigences de signature et de largeur.

Une autre différence est que les types les plus rapides et les moins nombreux sont requirejs, alors que les autres types de largeur exacte sont facultatifs :

3 Les types suivants sont obligatoires: 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.

A partir de la logique C99 7.8 Conversion de format de types entiers (document avec Standard), soulignons les suivantes:

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 et long , mais impose très peu d’exigences à leur taille. int et short sont au moins 16 bits et long au moins aussi longs que int et pas plus petit que 32 bits . Pour les systèmes 16 bits, la plupart des implémentations atsortingbuent respectivement 8, 16, 16 et 32 ​​bits à char , short , int et long . Pour les systèmes 32 bits, la pratique courante consiste à affecter 8, 16, 32 et 32 ​​bits à ces types. Cette différence de taille int peut créer des problèmes pour les utilisateurs qui migrent d’un système à un autre, en affectant des tailles différentes à des types entiers, car la règle de promotion de l’entier Standard C peut produire des modifications silencieuses inattendues. La nécessité de définir un type entier étendu a augmenté avec l’introduction des systèmes 64 bits.

pour objective 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 des autres idiosyncrasies d’implémentation . Il définit, via typedef , des types entiers de différentes tailles. Les implémentations sont libres de les typedef comme des types entiers Standard C ou des extensions qu’ils supportent. L’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 probablement exempt de ces “idiosyncrasies de mise en oeuvre”. Vous pouvez penser à quelque chose comme:

Je me fiche de la “politique” actuelle de l’OS / implémentation de taille int . Donnez-moi simplement le type d’entier le plus rapide avec au moins 16 bits.

Sur certaines plates-formes, l’utilisation de valeurs 16 bits peut être beaucoup plus lente que l’utilisation de valeurs 32 bits (par exemple, une mémoire de stockage 8 bits ou 16 bits nécessiterait de charger une charge 32 bits, de modifier la valeur chargée et d’écrire le résultat] . Même si une mémoire cache peut contenir deux fois plus de valeurs 16 bits que de valeurs 32 bits (la situation normale dans laquelle les valeurs 16 bits seraient plus rapides que les valeurs 32 bits sur les systèmes 32 bits), il est nécessaire de disposer de tous les parameters d’écriture. précédé d’une lecture annulera tout avantage en termes de rapidité pouvant être obtenu à moins que la structure de données ne soit lue beaucoup plus souvent qu’elle n’a été écrite. Sur de telles plates-formes, un type comme int_fast16_t serait probablement de 32 bits.

Cela dit, la norme n’autorise malheureusement pas la sémantique la plus utile pour un compilateur, à savoir autoriser les variables de type int_fast16_t dont l’adresse n’est pas prise se comporter arbitrairement comme des types 16 bits ou des types plus grands, en fonction ce qui est pratique Considérons, 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 des registres, mais il n’y a pas d’instructions pour effectuer des opérations 16 bits sur des registres. Si une variable int_fast16_t stockée en mémoire ne peut contenir que -32768 à +32767, cette même ressortingction s’appliquerait aux variables int_fast16_t stockées dans des registres. Comme le comportement défini par l’implémentation est contraint de contraindre des valeurs surdimensionnées dans des types entiers signés trop petits pour les contenir, cela obligerait le code ci-dessus à append des instructions pour signer-étendre les 16 bits inférieurs de x avant de le renvoyer; si la norme permettait un tel type, un type flexible “au moins 16 bits, mais davantage si cela conviendrait” pourrait éliminer la nécessité de telles instructions.

Voici un exemple de la différence entre les deux types: supposons qu’il existe une architecture dans laquelle l’arithmétique sur 8 bits, 16 bits, 32 bits et 64 bits est tout aussi rapide. (L’i386 se rapproche.) Ensuite, l’implémenteur peut utiliser un modèle LLP64 ou, encore mieux, permettre au programmeur de choisir entre ILP64, LP64 et LLP64, car de nombreux codes supposent que 32 bits est long, 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 dissoudrait le moins de code des autres systèmes, mais uint_fast16_t pourrait tout de même avoir une largeur de 16 bits, économisant ainsi de l'espace.