web-dev-qa-db-fra.com

Pourquoi préféreriez-vous uint32_t plutôt que uint_fast32_t?

Il paraît que uint32_t est beaucoup plus répandu que uint_fast32_t _ (Je réalise que ceci est une preuve anecdotique). Cela semble contre-intuitif pour moi, cependant.

Presque toujours, quand je vois une implémentation, utiliser uint32_t, tout ce qu’il veut vraiment, c’est un entier pouvant contenir des valeurs allant jusqu’à 4 294 967 295 (généralement une limite beaucoup plus basse située entre 65 535 et 4 294 967 295).

Il semble bizarre d'utiliser ensuite uint32_t, comme la garantie 'exactement 32 bits' n'est pas nécessaire, et la garantie 'le plus rapide disponible> = 32 bits' de uint_fast32_t semble être exactement la bonne idée. De plus, bien que généralement implémenté, uint32_t _ n'est pas réellement garanti.

Pourquoi, alors, uint32_t être préféré? Est-ce simplement mieux connu ou y a-t-il des avantages techniques par rapport aux autres?

79
Joost

uint32_t est garanti avoir presque les mêmes propriétés sur toutes les plates-formes qui le prennent en charge.1

uint_fast32_t n'a que très peu de garanties sur la façon dont il se comporte sur différents systèmes en comparaison.

Si vous passez sur une plate-forme où uint_fast32_t a une taille différente, tout le code utilisant uint_fast32_t doit être retesté et validé. Toutes les hypothèses de stabilité vont sortir par la fenêtre. L'ensemble du système va fonctionner différemment.

Lorsque vous écrivez votre code, vous n’avez peut-être même pas accès à accès à un uint_fast32_t système dont la taille n’est pas de 32 bits.

uint32_t ne fonctionnera pas différemment (voir note de bas de page).

La correction est plus importante que la vitesse. L’exactitude prématurée est donc un meilleur plan que l’optimisation prématurée.

Si j’écrivais du code pour des systèmes où uint_fast32_t avait 64 bits ou plus, je pourrais tester mon code dans les deux cas et l’utiliser. Sauf si besoin et opportunité, c'est un mauvais plan.

Finalement, uint_fast32_t lorsque vous le stockez pour une durée indéterminée ou qu'un nombre d'instances peut être plus lent que uint32 simplement en raison de problèmes de taille de cache et de bande passante mémoire. De nos jours, les ordinateurs sont beaucoup plus souvent liés à la mémoire qu’au processeur, et uint_fast32_t pourrait être plus rapide isolément, mais pas après la prise en compte de la surcharge de mémoire.


1 Comme @chux l’a noté dans un commentaire, si unsigned est supérieur à uint32_t, arithmétique sur uint32_t passe par les promotions entières habituelles, et sinon, il reste comme uint32_t. Cela peut causer des bugs. Rien n'est jamais parfait.

75

Pourquoi beaucoup de gens utilisent-ils uint32_t plutôt que uint32_fast_t?

Remarque: mal nommé uint32_fast_t devrait être uint_fast32_t.

uint32_t a une spécification plus stricte que uint_fast32_t et ainsi permet une fonctionnalité plus cohérente.


uint32_t avantages:

  • Différents algorithmes spécifient ce type. OMI - meilleure raison d'utiliser.
  • La largeur exacte et la portée sont connues.
  • Les tableaux de ce type ne génèrent aucun déchet.
  • unsigned Le calcul mathématique entier avec son débordement est plus prévisible.
  • Correspondance plus étroite dans la plage et les mathématiques des types 32 bits d'autres langues.
  • Jamais rembourré.

uint32_t les inconvénients:

  • Pas toujours disponible (mais c'est rare en 2018).
    Exemple: plates-formes manquant d’entiers de 8/16/32 bits (9/18/ 6 - bits, autres ).
    Exemple: plates-formes utilisant un complément différent de 2. ancien 22

uint_fast32_t avantages:

  • Toujours disponible.
    Ceci toujours autorise toutes les plates-formes, nouvelles et anciennes, à utiliser les types rapide/minimum.
  • "Fastest" type prenant en charge la plage 32 bits.

uint_fast32_t les inconvénients:

  • La portée n'est que peu connue. Exemple, il pourrait s'agir d'un type 64 bits.
  • Les tableaux de ce type peuvent être inutiles en mémoire.
  • Toutes les réponses (les miennes aussi au début), le post et les commentaires utilisaient un mauvais nom uint32_fast_t. On dirait que beaucoup n'ont tout simplement pas besoin et utilisent ce type. Nous n'avons même pas utilisé le bon nom!
  • Rembourrage possible - (rare).
  • Dans certains cas, le type "le plus rapide" peut vraiment être un autre type. Alors uint_fast32_t n'est qu'une approximation du 1er ordre.

En fin de compte, le meilleur dépend de l'objectif de codage. À moins que vous ne codiez pour une très grande portabilité ou une fonction de performance spécifique, utilisez uint32_t.


L'utilisation de ces types pose un autre problème: leur rang par rapport à int/unsigned

Vraisemblablement uint_fastN_t serait au moins le rang de unsigned. Ce n'est pas spécifié, mais une condition certaine et testable.

Ainsi, uintN_t est plus probable que uint_fastN_t être plus étroit le unsigned. Cela signifie que le code qui utilise uintN_t _ maths est plus susceptible de faire l’objet de promotions d’entier que uint_fastN_t concernant la portabilité.

Avec cette préoccupation: avantage de portabilité uint_fastN_t avec certaines opérations mathématiques.


Note latérale sur int32_t plutôt que int_fast32_t: Sur des machines rares, INT_FAST32_MIN peut être -2 147 483 647 et non -2 147 483 648. Le plus gros point: (u)intN_t Les types sont bien spécifiés et mènent au code portable.

31
chux

Pourquoi beaucoup de gens utilisent-ils uint32_t plutôt que uint32_fast_t?

Réponse idiote:

  • Il n'y a pas de type standard uint32_fast_t, l'orthographe correcte est uint_fast32_t.

Réponse pratique:

  • Beaucoup de gens utilisent réellement uint32_t ou int32_t pour leur sémantique précise, exactement 32 bits avec une arithmétique non signée en boucle (uint32_t) ou représentation du complément à 2 (int32_t). Le xxx_fast32_t les types peuvent être plus volumineux et donc inappropriés pour être stockés dans des fichiers binaires, utilisés dans des tableaux et des structures compactés, ou envoyés via un réseau. De plus, ils ne seront peut-être même pas plus rapides.

Réponse pragmatique:

  • Beaucoup de gens ne savent tout simplement pas (ou ne se soucient tout simplement pas) de uint_fast32_t, comme démontré dans les commentaires et les réponses, et supposons probablement que unsigned int pour avoir la même sémantique, bien que de nombreuses architectures actuelles aient encore ints en 16 bits et que quelques rares exemples du Musée aient d’autres tailles étranges inférieures à 32.

Réponse UX:

  • Bien que possiblement plus rapide que uint32_t, uint_fast32_t est plus lent à utiliser: la saisie prend plus de temps, ce qui prend en compte notamment la recherche d'orthographe et de sémantique dans la documentation C ;-)

L'élégance compte (évidemment basée sur l'opinion):

  • uint32_t semble assez mauvais pour que de nombreux programmeurs préfèrent définir leur propre u32 ou uint32 type ... De ce point de vue, uint_fast32_t semble maladroit au-delà de la réparation. Pas de surprise, il est assis sur le banc avec ses amis uint_least32_t et autres choses de ce genre.
25
chqrlie

Une des raisons est que unsigned int est déjà "le plus rapide" sans qu'il soit nécessaire de recourir à des types de caractères spéciaux ou d'inclure quelque chose. Donc, si vous en avez besoin rapidement, utilisez simplement le fondamental int ou unsigned int type.
Bien que la norme ne garantisse pas explicitement qu’elle est la plus rapide, elle indirectement le fait en indiquant "Les entités simples ont la taille naturelle suggérée par l’architecture de l’environnement d’exécution" dans 3.9.1. En d'autres termes, int (ou son équivalent non signé) est ce avec quoi le processeur est le plus à l'aise.

Maintenant bien sûr, vous ne savez pas quelle taille unsigned int pourrait être. Vous savez seulement que c'est au moins aussi grand que short (et je semble me souvenir que short doit avoir au moins 16 bits, bien que je ne le trouve pas dans la norme maintenant!). Habituellement, il s'agit simplement de 4 octets, mais il peut en théorie être plus grand ou, dans les cas extrêmes, encore plus petit (bien que je n’ai personnellement jamais rencontré d’architecture où c’était le cas, pas même sur des ordinateurs 8 bits dans les années 1980 ... peut-être quelques microcontrôleurs, qui sait s’avère que je souffre de démence, int était très clairement 16 bits à l’époque).

La norme C++ ne se donne pas la peine de spécifier ce que le <cstdint> Les types sont ou ce qu’ils garantissent, il est simplement mentionné "comme en C".

uint32_t, selon le standard C, garantit que vous obtenez exactement 32 bits. Rien de différent, rien de moins et pas de bits de remplissage. Parfois, c’est exactement ce dont vous avez besoin, ce qui est donc très précieux.

uint_least32_t garantit que quelle que soit sa taille, il ne peut pas être inférieur à 32 bits (mais il pourrait très bien être plus volumineux). Parfois, mais beaucoup plus rarement qu'une pensée exacte ou "ne s'en soucie pas", c'est ce que vous voulez.

Enfin, uint_fast32_t est à mon avis un peu superflu, sauf pour des raisons de documentation d’intention. La norme C stipule "désigne un type entier qui est généralement le plus rapide" (notez le mot "habituellement") et mentionne explicitement qu'il n'est pas nécessairement rapide à toutes fins. En d'autres termes, uint_fast32_t est à peu près la même chose que uint_least32_t, qui est généralement le plus rapide aussi, seulement aucune garantie donnée (mais aucune garantie dans les deux cas).

Depuis la plupart du temps, soit vous ne vous souciez pas de la taille exacte, soit vous voulez exactement 32 (ou 64, parfois 16) bits, et depuis le "ne vous souciez pas" unsigned int le type est le plus rapide de toute façon, cela explique pourquoi uint_fast32_t _ n'est pas si fréquemment utilisé.

8
Damon

Je n'ai pas vu de preuves que uint32_t _ être utilisé pour son plage. Au lieu de cela, la plupart du temps j'ai vu uint32_t _ est utilisé, il contient exactement 4 octets de données dans différents algorithmes, avec une sémantique enveloppante et de décalage garantie!

Il y a aussi d'autres raisons d'utiliser uint32_t au lieu de uint_fast32_t: Souvent, cela donne une ABI stable. De plus, l'utilisation de la mémoire peut être connue avec précision. Cela compense beaucoup quel que soit le gain de vitesse de uint_fast32_t, à chaque fois ce type serait distinct de celui de uint32_t.

Pour les valeurs <65536, il existe déjà un type pratique, il s'appelle unsigned int (unsigned short doit avoir au moins cette plage, mais unsigned int est de taille Word native) Pour les valeurs <4294967296, il existe un autre nom appelé unsigned long.


Et enfin, les gens n'utilisent pas uint_fast32_t parce qu’il est fastidieux de taper et facile à taper: D

6
Antti Haapala

Plusieurs raisons.

  1. Beaucoup de gens ne savent pas que les types "rapides" existent.
  2. C'est plus bavard à taper.
  3. Il est plus difficile de raisonner sur le comportement de vos programmes lorsque vous ne connaissez pas la taille réelle du type.
  4. La norme n’indique pas vraiment le plus rapide, et le type le plus rapide ne dépend pas vraiment du contexte.
  5. Je n'ai vu aucune preuve montrant que les développeurs de plates-formes pensaient à la taille de ces types lors de la définition de leurs plates-formes. Par exemple, sur Linux x86-64, les types "rapides" sont tous en 64 bits, même si x86-64 prend en charge le matériel pour des opérations rapides sur des valeurs 32 bits.

En résumé, les types "rapides" sont des déchets sans valeur. Si vous avez vraiment besoin de savoir quel type est le plus rapide pour une application donnée, vous devez analyser votre code avec votre compilateur.

5
plugwash

Du point de vue de l'exactitude et de la facilité de codage, uint32_t Présente de nombreux avantages par rapport à uint_fast32_t, Notamment en raison de la taille et de la sémantique arithmétiques définies plus précisément, comme de nombreux utilisateurs l'ont déjà souligné.

Ce qui a peut-être été oublié, c’est que le un avantage supposé de uint_fast32_t - qu’il puisse être plus rapide , mais jamais matérialisé de manière significative. La plupart des processeurs 64 bits qui ont dominé l'ère 64 bits (principalement x86-64 et Aarch64) ont évolué à partir d'architectures 32 bits et ont rapide Opérations natives 32 bits, même en mode 64 bits. Donc, uint_fast32_t Est identique à uint32_t Sur ces plateformes.

Même si certaines des plates-formes "également exécutées" telles que POWER, MIPS64, SPARC n'offrent que des opérations ALU 64 bits, les grande majorité d'intéressantes opérations 32 bits peuvent être bien fait sur les registres 64 bits: le 32 bits inférieur aura les résultats souhaités (et toutes les plates-formes grand public vous permettront au moins de charger/stocker des fichiers 32 bits). Le décalage gauche est le principal problème, mais être optimisé dans de nombreux cas par des optimisations de suivi valeur/plage dans le compilateur.

Je doute que le décalage gauche légèrement plus lent occasionnel ou la multiplication 32x32 -> 64 l'emporte double sur l'utilisation de la mémoire pour de telles valeurs, sauf la plus obscure. applications.

Enfin, je noterai que, même si le compromis a été largement qualifié de "potentiel d’utilisation de la mémoire et de vectorisation" (en faveur de uint32_t) Par rapport au nombre d’instructions/vitesse (en faveur de uint_fast32_t) - même ce n'est pas clair pour moi. Oui, sur certaines plates-formes, vous aurez besoin d'instructions supplémentaires pour certaines opérations 32 bits, mais vous aurez également sauve quelques instructions parce que:

  • L'utilisation d'un type plus petit permet souvent au compilateur de combiner intelligemment des opérations adjacentes en utilisant une opération 64 bits pour réaliser deux opérations 32 bits. Un exemple de ce type de "vectorisation du pauvre" n'est pas rare. Par exemple, créez une constante struct two32{ uint32_t a, b; } Dans rax comme two32{1, 2}peut être optimisée dans un seul mov rax, 0x20001 la version bit a besoin de deux instructions. En principe, cela devrait également être possible pour les opérations arithmétiques adjacentes (même opération, opérande différent), mais je ne l'ai pas vu dans la pratique.
  • Une faible "utilisation de la mémoire" entraîne également souvent moins d'instructions, même si l'encombrement de la mémoire ou du cache n'est pas un problème, car toutes les structures de type ou tableaux de ce type sont copiés, vous obtenez deux fois plus pour votre argent par registre copié.
  • Les types de données plus petits exploitent souvent des conventions d’appel plus modernes telles que l’ABI SysV, qui encapsule efficacement les données de structure de données dans des registres. Par exemple, vous pouvez retourner jusqu'à une structure de 16 octets dans les registres rdx:rax. Pour une fonction retournant une structure avec 4 valeurs uint32_t (Initialisée à partir d'une constante), cela se traduit par

    ret_constant32():
        movabs  rax, 8589934593
        movabs  rdx, 17179869187
        ret
    

    La même structure avec 4 uint_fast32_t 64 bits nécessite un déplacement de registre et quatre magasins en mémoire pour faire la même chose (et l'appelant devra probablement lire les valeurs de la mémoire après le retour):

    ret_constant64():
        mov     rax, rdi
        mov     QWORD PTR [rdi], 1
        mov     QWORD PTR [rdi+8], 2
        mov     QWORD PTR [rdi+16], 3
        mov     QWORD PTR [rdi+24], 4
        ret
    

    De même, lors du passage d'arguments de structure, les valeurs 32 bits sont regroupées deux fois plus étroitement dans les registres disponibles pour les paramètres. Il est donc moins probable que vous manquiez d'arguments de registre et que vous deviez en déborder la pile.1.

  • Même si vous choisissez d'utiliser uint_fast32_t Pour les endroits où "la vitesse compte", vous aurez souvent aussi des endroits où vous aurez besoin d'un type de taille fixe. Par exemple, lorsque vous transmettez des valeurs pour une sortie externe, depuis une entrée externe, en tant que partie de votre ABI, en tant que partie d'une structure nécessitant une mise en page spécifique, ou parce que vous utilisez intelligemment uint32_t Pour de grandes agrégations de valeurs sur lesquelles enregistrer empreinte mémoire. Aux endroits où vos types uint_fast32_t Et `` uint32_t` doivent s'interfacer, vous pouvez trouver (en plus de la complexité du développement), des extensions de signature inutiles ou un autre code lié à une différence de taille. Les compilateurs parviennent à optimiser cette situation dans de nombreux cas, mais il n’est pas rare de voir cela dans une sortie optimisée lors du mélange de types de tailles différentes.

Vous pouvez jouer avec certains des exemples ci-dessus et plus encore sur godbolt .


1 Pour être clair, la convention consistant à bien tasser les structures dans des registres ne permet pas toujours de gagner des valeurs plus petites. Cela signifie que les plus petites valeurs devront peut-être être "extraites" avant de pouvoir être utilisées. Par exemple, une fonction simple qui renvoie la somme des deux membres de la structure nécessite un mov rax, rdi; shr rax, 32; add edi, eax, Tandis que pour la version 64 bits, chaque argument obtient son propre registre et nécessite simplement un seul add ou lea. Néanmoins, si vous acceptez que la conception "compacte les structures en passant" soit logique, les valeurs plus petites tireront davantage parti de cette fonctionnalité.

5
BeeOnRope

D'après ma compréhension, int était initialement supposé être un type entier "natif" avec une garantie supplémentaire qu'il devait avoir une taille d'au moins 16 bits - une taille considérée à l'époque comme "raisonnable".

Lorsque les plates-formes 32 bits sont devenues plus courantes, on peut dire que la taille "raisonnable" a été modifiée en 32 bits:

  • Windows moderne utilise int 32 bits sur toutes les plateformes.
  • POSIX garantit que int est au moins de 32 bits.
  • C #, Java a le type int qui est garanti pour être exactement 32 bits.

Mais lorsque la plate-forme 64 bits est devenue la norme, personne n'a développé int pour devenir un entier 64 bits à cause de:

  • Portabilité: beaucoup de code dépend de la taille de 32 bits de int.
  • Consommation de mémoire: doubler l'utilisation de la mémoire pour chaque int pourrait être déraisonnable dans la plupart des cas, étant donné que, dans la plupart des cas, leur nombre utilisé est nettement inférieur à 2 milliards.

Maintenant, pourquoi préféreriez-vous uint32_t À uint_fast32_t? Pour les mêmes langages de raison, C # et Java utilisent toujours des entiers de taille fixe: le programmeur n'écrit pas de code en pensant aux tailles possibles de différents types, il écrit pour une plate-forme et teste le code sur cette plate-forme. du code dépend implicitement de la taille spécifique des types de données, raison pour laquelle uint32_t est un meilleur choix dans la plupart des cas - elle ne permet aucune ambiguïté quant à son comportement.

De plus, uint_fast32_t Est-il vraiment le type le plus rapide sur une plate-forme de taille égale ou supérieure à 32 bits? Pas vraiment. Considérez ce compilateur de code par GCC pour x86_64 sous Windows:

extern uint64_t get(void);

uint64_t sum(uint64_t value)
{
    return value + get();
}

L'assemblage généré ressemble à ceci:

Push   %rbx
sub    $0x20,%rsp
mov    %rcx,%rbx
callq  d <sum+0xd>
add    %rbx,%rax
add    $0x20,%rsp
pop    %rbx
retq

Maintenant, si vous modifiez la valeur de retour de get() en uint_fast32_t (Ce qui correspond à 4 octets sous Windows x86_64), vous obtenez ceci:

Push   %rbx
sub    $0x20,%rsp
mov    %rcx,%rbx
callq  d <sum+0xd>
mov    %eax,%eax        ; <-- additional instruction
add    %rbx,%rax
add    $0x20,%rsp
pop    %rbx
retq

Notez que le code généré est presque identique, à l'exception de l'instruction supplémentaire mov %eax,%eax Après l'appel de la fonction, destinée à développer la valeur 32 bits en une valeur 64 bits.

Il n'y a pas de problème de ce type si vous utilisez uniquement des valeurs 32 bits, mais vous utiliserez probablement celles avec des variables size_t (Tailles de tableaux probablement?) Et 64 bits sur x86_64. Sur Linux, uint_fast32_t A 8 octets, la situation est donc différente.

Beaucoup de programmeurs utilisent int quand ils ont besoin de retourner une petite valeur (disons dans l'intervalle [-32,32]). Cela fonctionnerait parfaitement si int était une taille d’entier native pour la plate-forme, mais comme elle n’est pas sur les plates-formes 64 bits, un autre type qui correspond au type de plate-forme native constitue un meilleur choix (à moins qu’il soit fréquemment utilisé avec d’autres entiers de taille plus petite).

Fondamentalement, indépendamment de ce que dit la norme, uint_fast32_t Est cassé sur certaines implémentations de toute façon. Si vous vous souciez des instructions supplémentaires générées à certains endroits, vous devez définir votre propre type entier "natif". Ou vous pouvez utiliser size_t À cette fin, car il correspond généralement à native size (je n'inclus pas les anciennes et obscures plates-formes comme 8086, mais uniquement celles qui peuvent fonctionner sous Windows, Linux, etc.).


"Une règle de promotion des nombres entiers" est un autre signe indiquant que int était supposé être un type natif entier. La plupart des processeurs ne peuvent effectuer des opérations que sur des processus natifs. Par conséquent, les processeurs 32 bits ne peuvent généralement effectuer que des additions, des soustractions, etc. 32 bits (les processeurs Intel sont une exception à cet égard). Les types entiers d'autres tailles ne sont pris en charge que par les instructions de chargement et de stockage. Par exemple, la valeur de 8 bits doit être chargée avec l'instruction "charger 8 bits signé" ou "charger 8 bits non signés" appropriée et étendra la valeur à 32 bits après le chargement. Sans règle de promotion entière, les compilateurs C devraient ajouter un peu plus de code pour les expressions utilisant des types plus petits que le type natif. Malheureusement, cela ne tient plus avec les architectures 64 bits, car les compilateurs doivent maintenant émettre des instructions supplémentaires dans certains cas (comme indiqué ci-dessus).

4
StaceyGirl

Pour des raisons pratiques, uint_fast32_t est complètement inutile. Il est défini de manière incorrecte sur la plate-forme la plus répandue (x86_64) et n'offre aucun avantage ailleurs que si vous avez un compilateur de très basse qualité. Sur le plan conceptuel, il n’a jamais de sens d’utiliser les types "rapides" dans les structures/tableaux de données - les économies que vous réalisez grâce à un type plus efficace à exploiter seront réduites au minimum (coût de l’absence de cache, etc.) en augmentant la taille du fichier. votre ensemble de données de travail. Et pour les variables locales individuelles (compteurs de boucle, temps, etc.), un compilateur non-jouet peut simplement travailler avec un type plus grand dans le code généré si cela est plus efficace, et ne tronquer à la taille nominale que si cela est nécessaire pour la correction (et avec types signés, ce n'est jamais nécessaire).

La seule variante théoriquement utile est uint_least32_t, lorsque vous devez pouvoir stocker une valeur 32 bits, mais que vous souhaitez être portable sur des ordinateurs dépourvus du type 32 bits de taille exacte. Dans la pratique, toutefois, ce n’est pas un sujet de préoccupation.

4
R..

Dans de nombreux cas, lorsqu'un algorithme fonctionne sur un tableau de données, le meilleur moyen d'améliorer les performances est de minimiser le nombre d'erreurs dans le cache. Plus chaque élément est petit, plus il peut y en avoir dans le cache. C’est pourquoi beaucoup de code est encore écrit pour utiliser des pointeurs 32 bits sur des ordinateurs 64 bits: ils n’ont besoin de rien de près de 4 GiB de données, mais les pointeurs et les décalages ont besoin de huit octets au lieu de quatre, ce qui est considérable.

Il existe également des ABI et des protocoles spécifiés pour requérir exactement 32 bits, par exemple les adresses IPv4. C'est ce que uint32_t signifie vraiment: utilisez exactement 32 bits, que cela soit efficace ou non sur le processeur. Celles-ci étaient déclarées comme long ou unsigned long, qui a causé beaucoup de problèmes lors de la transition 64 bits. Si vous avez simplement besoin d’un type non signé qui contienne des nombres allant jusqu’à 2³²-1, c’est la définition de unsigned long depuis la publication du premier standard C. En pratique cependant, suffisamment de code ancien supposait qu'un long pouvait contenir n'importe quel pointeur, offset ou horodatage de fichier, et suffisamment de code ancien supposait qu'il avait exactement 32 bits de large, que les compilateurs ne pouvaient pas forcément faire long identique à int_fast32_t sans casser trop de choses.

En théorie, il serait plus sûr pour un programme d’utiliser uint_least32_t, et peut-être même charger uint_least32_t éléments dans un uint_fast32_t variable pour les calculs. Une implémentation qui n'avait pas uint32_t type pourrait même se déclarer conforme formellement à la norme! (Il ne pourrait tout simplement pas compiler de nombreux programmes existants.) En pratique, il n’ya plus d’architecture où int, uint32_t, et uint_least32_t ne sont pas les mêmes et aucun avantage, actuellement, à la performance de uint_fast32_t. Alors pourquoi compliquer les choses?

Pourtant, regardez la raison pour laquelle tous les 32_t Les types _ devaient exister quand nous avions déjà long, et vous verrez que ces hypothèses nous ont déjà explosé. Votre code pourrait bien finir par s'exécuter un jour sur une machine où les calculs 32 bits en largeur exacte sont plus lents que la taille native de Word, et il aurait été préférable d'utiliser uint_least32_t pour le stockage et uint_fast32_t pour le calcul religieux. Ou si vous traversez ce pont quand vous y arrivez et que vous voulez quelque chose de simple, il y a unsigned long.

2
Davislor

Pour donner une réponse directe: je pense que la vraie raison pour laquelle uint32_t est utilisé sur uint_fast32_t ou uint_least32_t est simplement qu’il est plus facile de taper, et, en raison de sa taille plus courte, beaucoup plus agréable à lire: Si vous faites des structs avec certains types, et que certains d’entre eux sont uint_fast32_t ou similaire, alors il est souvent difficile de bien les aligner avec int ou bool ou d’autres types en C, qui sont assez courts (exemple: char contre character). Bien sûr, je ne peux pas confirmer cela avec des données fiables, mais les autres réponses ne peuvent que deviner la raison.

Quant aux raisons techniques de préférer uint32_t, Je ne pense pas qu'il y en ait - quand vous avez absolument besoin un entier 32 bits non signé exact, alors ce type est votre seulement choix standardisé. Dans presque tous les autres cas, les autres variantes sont techniquement préférables - en particulier, uint_fast32_t si vous êtes préoccupé par la vitesse et uint_least32_t si vous êtes préoccupé par l'espace de stockage. En utilisant uint32_t dans tous les cas, il risque de ne pas pouvoir compiler car le type n’est pas obligatoirement existant.

En pratique, le uint32_t et les types associés existent sur toutes les plateformes actuelles, à l'exception de très rares (de nos jours) DSP ou implémentations de blagues, de sorte que l'utilisation du type exact présente peu de risques. De même, même si vous pouvez rencontrer des pénalités de vitesse avec les types à largeur fixe, elles ne sont plus (sur les processeurs modernes) affaiblies.

C'est pourquoi, je pense, le type le plus court l'emporte simplement dans la plupart des cas, en raison de la paresse du programmeur.

1
Marc Lehmann