web-dev-qa-db-fra.com

Raisons d'utiliser (ou non) stdint

Je sais déjà que stdint est utilisé lorsque vous avez besoin de tailles de variables spécifiques pour la portabilité entre plates-formes. Je n'ai pas vraiment un tel problème pour l'instant, mais quels sont les inconvénients et les avantages de l'utiliser en plus du fait déjà montré ci-dessus?

En cherchant cela sur stackoverflow et d'autres sites, j'ai trouvé 2 liens qui traitent du thème:

Ces deux liens sont particulièrement utiles si l'on cherche à en savoir plus sur la raison principale de la portabilité de cet en-tête - . Mais pour moi, ce que j'aime le plus, c'est que je pense que uint8_t Est plus propre que unsigned char (Pour stocker une valeur de canal RBG par exemple), int32_t Semble plus significatif que simplement int, etc.

Donc, ma question est, quels sont exactement les inconvénients et les avantages de l'utilisation de stdint en plus de la portabilité? Dois-je l'utiliser uniquement dans certaines parties spécifiques de mon code, ou partout? si partout, comment puis-je utiliser des fonctions comme atoi(), strtok(), etc. avec elle?

Merci!

47
Sassa

Avantages

L'utilisation de types bien définis rend le code beaucoup plus facile et plus sûr à porter, car vous n'aurez aucune surprise lorsque, par exemple, une machine interprète int en 16 bits et une autre en 32 bits. Avec stdint.h, ce que vous tapez est ce que vous obtenez.

L'utilisation de int etc rend également difficile la détection des promotions de type dangereux.

Un autre avantage est qu'en utilisant int8_t au lieu de char, vous savez que vous obtenez toujours une variable 8 bits signée. char peut être signé ou non, c'est un comportement défini par l'implémentation et varie selon les compilateurs. Par conséquent, la valeur par défaut char est très dangereuse à utiliser dans du code qui devrait être portable.

Si vous souhaitez indiquer au compilateur qu'une variable doit être optimisée, vous pouvez utiliser le uint_fastx_t qui indique au compilateur d'utiliser le type entier le plus rapide possible, au moins aussi grand que 'x'. La plupart du temps, cela n'a pas d'importance, le compilateur est suffisamment intelligent pour effectuer des optimisations sur les tailles de type, peu importe ce que vous avez tapé. Entre les points de séquence, le compilateur peut implicitement changer le type en un autre que celui spécifié, tant qu'il n'affecte pas le résultat.

Les inconvénients

Aucun.


Référence: MISRA-C: 2004 règle 6.3. " les caractères typographiques qui indiquent la taille et la signature doivent être utilisés à la place des types de base".

EDIT: Suppression d'un exemple incorrect.

69
Lundin

La seule raison d'utiliser uint8_t Plutôt que unsigned char (À part les préférences esthétiques) est si vous voulez documenter que votre programme nécessite que char soit exactement 8 bits. uint8_t Existe si et seulement si CHAR_BIT==8, Selon les exigences de la norme C.

Les autres types intX_t Et uintX_t Sont utiles dans les situations suivantes:

  • lecture/écriture de disque/réseau (mais vous devez également utiliser des fonctions de conversion endian)
  • lorsque vous souhaitez un comportement enveloppant non signé à une limite exacte (mais cela peut être fait de manière plus portable avec l'opérateur &).
  • lorsque vous contrôlez la disposition exacte d'une structure, car vous devez vous assurer qu'il n'y a pas de remplissage (par exemple pour memcmp ou à des fins de hachage).

D'un autre côté, les types uint_least8_t, Etc. sont utiles partout où vous voulez éviter d'utiliser des types inutiles ou lents mais devez vous assurer que vous pouvez stocker des valeurs d'une certaine ampleur. Par exemple, alors que long long Est d'au moins 64 bits, il peut être de 128 bits sur certaines machines, et l'utiliser lorsque ce dont vous avez besoin est juste un type qui peut stocker des nombres 64 bits serait très coûteux sur ces machines . int_least64_t Résout le problème.

J'éviterais d'utiliser les types [u]int_fastX_t Entièrement car ils ont parfois changé sur une machine donnée (cassant l'ABI) et puisque les définitions sont généralement fausses. Par exemple, sur x86_64, le type entier 64 bits est considéré comme le type "rapide" pour les valeurs 16, 32 et 64 bits, mais tandis que l'addition, la soustraction et la multiplication ont exactement la même vitesse que vous utilisiez 32- bits ou 64 bits, la division est presque sûrement plus lente avec des types plus grands que nécessaire, et même s'ils étaient à la même vitesse, vous utilisez deux fois la mémoire sans aucun avantage.

Enfin, notez que les arguments que certaines réponses ont avancés concernant l'inefficacité de l'utilisation de int32_t Pour un compteur quand ce n'est pas la taille entière native sont techniquement généralement corrects, mais il n'est pas pertinent de corriger le code. À moins que vous ne comptiez un petit nombre de choses où le nombre maximal est sous votre contrôle, ou quelque chose externe (pas dans la mémoire de votre programme) où le nombre pourrait être astronomique, le type correct pour un nombre est presque toujours size_t. C'est pourquoi toutes les fonctions C standard utilisent size_t Pour les décomptes. N'envisagez pas d'utiliser autre chose à moins d'avoir une très bonne raison.

16
R..

les inconvénients

La principale raison pour laquelle le langage C ne spécifie pas la taille de int ou long, etc. est pour l'efficacité du calcul. Chaque architecture a une taille naturelle et la plus efficace, et les concepteurs ont spécifiquement habilité et prévu l'implémentateur du compilateur à utiliser les données de taille de données natives naturelles pour la vitesse et l'efficacité de la taille du code.

Au cours des années passées, la communication avec d'autres machines n'était pas une préoccupation principale - la plupart des programmes étaient locaux sur la machine - donc la prévisibilité de la taille de chaque type de données était peu préoccupante.

Insister pour qu'une architecture particulière utilise une taille particulière int pour compter est vraiment mauvaise idée, même si cela semble rendre les choses plus faciles.

D'une certaine manière, grâce à XML et à ses frères, la taille du type de données n'est plus une préoccupation majeure. L'expédition de structures binaires spécifiques à une machine d'une machine à l'autre est encore une fois l'exception plutôt que la règle.

7
wallyk

J'utilise les types stdint pour une seule raison, lorsque les données que je garde en mémoire doivent aller sur disque/réseau/descripteur sous forme binaire. Vous n'avez qu'à lutter contre le problème du petit endian/big-endian mais c'est relativement facile à surmonter .

La raison évidente de ne pas utiliser stdint est que le code est indépendant de la taille, en termes mathématiques tout ce qui fonctionne sur les entiers rationnels. Cela produirait des doublons de code moches si vous fournissiez une version uint*_t De, disons, qsort() pour chaque expansion de *.

J'utilise mes propres types dans ce cas, dérivés de size_t Lorsque je suis paresseux ou du plus grand entier non signé pris en charge sur la plate-forme lorsque je ne le suis pas.

Modifier, car j'ai rencontré ce problème plus tôt:
Je pense qu'il est à noter qu'au moins uint8_t, uint32_t Et uint64_t Sont rompus dans Solaris 2.5.1. Donc, pour une portabilité maximale, je suggère toujours d'éviter stdint.h (Au moins pour les prochaines années).

6
hroptatyr