92 votes

Différence entre uint8_t, uint_fast8_t et uint_least8_t

La norme C99 introduit les types de données suivants. La documentation peut être trouvée aquí pour la bibliothèque AVR stdint.

  • uint8_t signifie qu'il s'agit d'un type non signé de 8 bits.
  • uint_fast8_t signifie que c'est le plus rapide des int non signés d'au moins 8 bits. bits.
  • uint_least8_t signifie que c'est un int non signé avec au moins 8 bits.

Je comprends uint8_t et ce qui est uint_fast8_t (Je ne sais pas comment c'est implémenté au niveau du registre).

1.Pouvez-vous expliquer la signification de "c'est une unsigned int avec au moins 8 bits" ?

2.Comment uint_fast8_t y uint_least8_t contribuer à accroître l'efficacité/l'espace de code par rapport à la uint8_t ?

0 votes

Pour votre première question, je peux imaginer qu'alors que uint8_t est garanti comme étant de 8 bits, uint_fast8_t est garanti d'être >= 8 bits, tout comme une unsigned char .

1 votes

Une des considérations est que uint8_t n'existe pas sur les systèmes qui n'ont pas un type 8 bits natif. Les deux autres seront là.

11 votes

Vous avez obtenu des réponses faisant référence à des architectures "obscures" et "exotiques". Ces termes sont un peu biaisés. Bien sûr, si votre seule expérience est celle des systèmes de bureau, ces architectures sont en dehors de votre champ d'expérience. Mais "je n'ai jamais vu ça avant" n'est pas la même chose que "c'est obscur ou exotique". Pour les personnes qui travaillent avec des systèmes embarqués ou des DSP, ces choses sont assez courantes.

113voto

rodrigo Points 34500

uint_least8_t est le plus petit type qui possède au moins 8 bits. uint_fast8_t est le type le plus rapide qui possède au moins 8 bits.

Vous pouvez voir les différences en imaginant des architectures exotiques. Imaginez une architecture de 20 bits. Son site unsigned int a 20 bits (un registre), et sa fonction unsigned char a 10 bits. Donc sizeof(int) == 2 mais en utilisant char Les types nécessitent des instructions supplémentaires pour couper les registres en deux. Ensuite :

  • uint8_t : est indéfini (pas de type 8 bits).
  • uint_least8_t : est unsigned char le plus petit type qui a au moins 8 bits.
  • uint_fast8_t : est unsigned int car dans mon architecture imaginaire, une variable de demi-registre est plus lente qu'une variable de registre complet.

14 votes

J'adore le fait qu'il faille imaginer des architectures exotiques pour trouver un cas d'utilisation. Ont-ils trouvé une quelconque utilité dans la pratique ?

21 votes

@Mehrdad Dans ARM par exemple, si votre int_fast8_t est une variable 32 bits, vous n'avez pas besoin de faire l'extension du signe avant les opérations arithmétiques.

2 votes

@Mehrdad MIPS par exemple, il serait très malvenu de faire n'importe quel uintX_fast_t moins de 32 bits. Il n'est même pas nécessaire d'imaginer des architectures pour obtenir uint8_t pour être indéfinie, prenez par exemple l'UNIVAC qui est de 36 bits, je suppose qu'il y a char est de 9 bits.

34voto

Lundin Points 21616

uint8_t signifie : donnez-moi un int non signé d'exactement 8 bits.

uint_least8_t signifie : donnez-moi le plus petit type de unsigned int qui a au moins 8 bits. Optimisez pour la consommation de mémoire.

uint_fast8_t signifie : donnez-moi un int non signé d'au moins 8 bits. Choisissez un type plus grand si cela peut rendre mon programme plus rapide, pour des raisons d'alignement. Optimisez pour la vitesse.

En outre, contrairement à l'ordinaire int la version signée des types stdint.h ci-dessus est garantie au format complément à 2.

1 votes

Merci. Il est bon de savoir que les types signés dans stdint.h sont garantis comme étant des compléments à deux. Je me demande en quoi cela peut être utile pour écrire du code portable.

7 votes

Notez que seules les variantes de largeur exacte doivent utiliser le format de complément à 2. Notez également qu'il n'est pas nécessaire qu'elles existent. Par conséquent, il n'est pas nécessaire qu'une plate-forme prenne en charge le format de complément à 2.

0 votes

@legends2k : Les types de stdint.h sont un peu moins utiles que l'on pourrait le souhaiter si l'on essaie d'écrire du code portable, car bien qu'ils soient tenus d'utiliser un format de stockage à deux compléments, cela n'implique pas qu'ils présenteront un comportement d'enveloppement à deux compléments. Notez également que même sur les plateformes où int est de 32 bits l'écriture d'une valeur à l'aide d'un int32_t* et la lecture à l'aide d'un int* ou vice versa, n'est pas garanti.

28voto

plugwash Points 795

La théorie est la suivante :

uint8_t doit avoir exactement 8 bits, mais il n'est pas obligé d'exister. Vous devez donc l'utiliser lorsque vous comptez sur le comportement arithmétique modulo-256 d'un entier de 8 bits et lorsque vous préférez un échec de compilation à un mauvais comportement sur des architectures obscures.

uint_least8_t doit être le plus petit type d'entier non signé disponible qui peut stocker au moins 8 bits. Vous l'utiliserez lorsque vous voudrez minimiser l'utilisation de la mémoire pour des choses comme les grands tableaux.

uint_fast8_t est censé être le type non signé "le plus rapide" pouvant stocker au moins 8 bits ; toutefois, il n'est pas garanti qu'il soit le plus rapide pour une opération donnée sur un processeur donné. Vous l'utiliserez dans un code de traitement qui effectue de nombreuses opérations sur la valeur.

Dans la pratique, les types "fast" et "least" ne sont pas beaucoup utilisés.

Les types "les moins" ne sont vraiment utiles que si vous vous souciez de la portabilité sur des architectures obscures avec CHAR_BIT != 8, ce qui n'est pas le cas de la plupart des gens.

Le problème avec les types "rapides" est que le terme "le plus rapide" est difficile à cerner. Un type plus petit peut signifier une charge moindre sur le système de mémoire/cache, mais l'utilisation d'un type plus petit que le type natif peut nécessiter des instructions supplémentaires. De plus, la meilleure solution peut changer d'une version d'architecture à l'autre, mais les implémenteurs veulent souvent éviter de casser l'ABI dans de tels cas.

En regardant certaines implémentations populaires, il semble que les définitions de uint_fastn_t sont assez arbitraires. La glibc semble les définir comme ayant au moins la "taille de mot native" du système en question, sans tenir compte du fait que de nombreux processeurs modernes (surtout ceux à 64 bits) ont un support spécifique pour les opérations rapides sur des éléments plus petits que leur taille de mot native. IOS les définit apparemment comme équivalents aux types de taille fixe. Les autres plateformes peuvent varier.

Dans l'ensemble, si la performance d'un code serré avec des entiers minuscules est votre objectif, vous devriez faire du benchmarking. su code sur les plateformes qui vous intéressent avec différents types de taille pour voir ce qui fonctionne le mieux.

2 votes

Les définitions de la glibc ont été choisies à une époque où ces optimisations n'existaient pas, et elles sont maintenant intégrées à l'ABI et ne peuvent pas être modifiées. C'est l'une des nombreuses raisons pour lesquelles les types _least et _fast ne sont pas vraiment utiles en pratique.

5 votes

@zwol : J'aimerais que le langage ajoute des types définis en termes de disposition et d'exigences sémantiques, par exemple : "J'ai besoin de quelque chose dont les bits inférieurs aliaseront d'autres types 16 bits, et qui peut contenir les valeurs 0-65535, mais je n'ai pas besoin qu'il fixe des valeurs plus grandes dans cette plage". L'alias, la disposition, la plage et le comportement hors plage devraient être quatre aspects distincts d'un type, mais le C ne permet que certaines combinaisons qui ne sont pas cohérentes entre les différentes plateformes.

4voto

skyking Points 1919

Pouvez-vous expliquer la signification de "c'est un int non signé avec au moins 8 bits" ?

Cela devrait être évident. Cela signifie que c'est un type d'entier non signé et que sa largeur est d'au moins 8 bits. En fait, cela signifie qu'il peut au moins contenir les nombres de 0 à 255, et qu'il ne peut certainement pas contenir des nombres négatifs, mais qu'il peut être capable de contenir des nombres supérieurs à 255.

Il est évident que vous ne devez utiliser aucun de ces types si vous prévoyez de stocker un nombre en dehors de la plage de 0 à 255 (et si vous voulez qu'il soit portable).

Comment uint_fast8_t et uint_least8_t permettent-ils d'améliorer l'efficacité et l'espace de code par rapport à uint8_t ?

uint_fast8_t doit être plus rapide, vous devez donc l'utiliser si votre exigence est que le code soit rapide. uint_least8_t d'autre part, il faut qu'il n'y ait pas de candidat de taille inférieure - vous l'utiliserez donc si la taille est une préoccupation.


Et bien sûr, vous utilisez seulement uint8_t lorsque vous avez absolument besoin qu'il soit exactement de 8 bits. Utilisation de uint8_t peut rendre le code non-portable comme uint8_t n'a pas besoin d'exister (parce qu'un tel petit type d'entier n'existe pas sur certaines plateformes).

3voto

LPs Points 2090

Les types d'entiers "rapides" sont définis comme étant les entiers les plus rapides disponibles avec au moins le nombre de bits requis (dans votre cas 8).

Une plateforme peut définir uint_fast8_t como uint8_t alors il n'y aura absolument aucune différence de vitesse.

La raison en est que certaines plateformes sont plus lentes lorsqu'elles n'utilisent pas leur longueur de mot native.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X