uint8_t vs unsigned char

Quel est l’avantage d’utiliser uint8_t sur les caractères unsigned char dans C?

Je sais que sur presque tous les systèmes, uint8_t est juste un typedef pour les caractères unsigned char , alors pourquoi l’utiliser?

Il documente votre intention – vous stockerez de petits nombres, plutôt qu’un personnage.

En outre, il est préférable d’utiliser d’autres types tels que uint16_t ou int32_t .

Juste pour être pédant, certains systèmes peuvent ne pas avoir de type 8 bits. Selon Wikipedia :

Une implémentation est requirejse pour définir des types d’entiers de largeur exacte pour N = 8, 16, 32 ou 64 si et seulement si elle possède un type répondant aux exigences. Il n’est pas nécessaire de les définir pour un autre N, même s’il prend en charge les types appropriés.

Donc, l’ uint8_t n’est pas garantie, bien qu’elle le soit pour toutes les plates-formes où 8 bits = 1 octet. Certaines plates-formes embarquées peuvent être différentes, mais cela devient très rare. Certains systèmes peuvent définir des types de caractères de 16 bits, auquel cas il n’y aura probablement aucun type de 8 bits.

À part ce problème (mineur), la réponse de @Mark Ransom est la meilleure à mon avis. Utilisez celui qui montre le mieux ce pour quoi vous utilisez les données.

En outre, je suppose que vous uint8_t dire uint8_t (le typedef standard de C99 fourni dans l’en-tête stdint.h ) plutôt que uint_8 (ne faisant partie d’aucune norme).

Le but est d’écrire du code indépendant de l’implémentation. unsigned char n’est pas garanti pour être un type 8 bits. uint8_t c’est.

Comme vous l’avez dit, ” presque tous les systèmes”.

char est probablement l’un des moins susceptibles de changer, mais une fois que vous commencez à utiliser uint16_t et friends, l’utilisation de uint8_t s’intègre mieux et peut même faire partie d’une norme de codage.

Dans mon expérience, il y a deux endroits où nous voulons utiliser uint8_t pour désigner 8 bits (et uint16_t, etc.) et où nous pouvons avoir des champs inférieurs à 8 bits. Les deux endroits sont où l’espace compte et nous avons souvent besoin de regarder un vidage brut des données lors du débogage et de pouvoir déterminer rapidement ce qu’il représente.

Le premier concerne les protocoles RF, en particulier dans les systèmes à bande étroite. Dans cet environnement, nous pouvons avoir besoin de rassembler autant d’informations que possible dans un seul message. La seconde est dans le stockage flash où nous pouvons avoir un espace très limité (comme dans les systèmes embarqués). Dans les deux cas, nous pouvons utiliser une structure de données empaquetée dans laquelle le compilateur s’occupera de l’emballage et du déballage pour nous:

 #pragma pack(1) typedef struct { uint8_t flag1:1; uint8_t flag2:1; padding1 reserved:6; /* not necessary but makes this struct more readable */ uint32_t sequence_no; uint8_t data[8]; uint32_t crc32; } s_mypacket __atsortingbute__((packed)); #pragma pack() 

La méthode que vous utilisez dépend de votre compilateur. Vous devrez peut-être également prendre en charge plusieurs compilateurs différents avec les mêmes fichiers d’en-tête. Cela se produit dans les systèmes embarqués où les périphériques et les serveurs peuvent être complètement différents – par exemple, vous pouvez avoir un périphérique ARM qui communique avec un serveur Linux x86.

Il y a quelques mises en garde concernant l’utilisation de structures compactes. Le plus gros problème est que vous devez éviter de déréférencer l’adresse d’un membre. Sur les systèmes avec des mots alignés sur plusieurs octets, cela peut entraîner une exception mal alignée – et un coredump.

Certaines personnes vont également s’inquiéter des performances et prétendre que l’utilisation de ces structures compactées ralentira votre système. Il est vrai que, dans les coulisses, le compilateur ajoute du code pour accéder aux données non alignées. Vous pouvez voir cela en regardant le code d’assemblage dans votre IDE.

Mais comme les structures empaquetées sont les plus utiles pour la communication et le stockage de données, les données peuvent être extraites dans une représentation non compactée lorsque vous travaillez en mémoire. Normalement, nous n’avons pas besoin de travailler avec le paquet de données complet en mémoire.

Voici quelques discussions pertinentes:

Pragma Pack (1) ni __atsortingbute__ ((aligné (1))) fonctionne

Le __atsortingbute __ ((emballé)) / #pragma pack de gcc est-il dangereux?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html

Il y a peu Du sharepoint vue de la portabilité, les caractères ne peuvent pas être inférieurs à 8 bits, et rien ne peut être plus petit que char . Par conséquent, si une implémentation C donnée comporte un type entier non signé de 8 bits, elle char . Alternativement, il peut ne pas en avoir du tout, à quel point toutes les astuces typedef sont discutables.

Il pourrait être utilisé pour mieux documenter votre code dans le sens où il est clair que vous avez besoin d’octets de 8 bits et rien d’autre. Mais dans la pratique, il existe une attente raisonnable pratiquement partout (il existe des plates-formes DSP sur lesquelles ce n’est pas vrai, mais les chances que votre code tourne là-bas sont minces, et vous pourriez également commettre une erreur statique en haut de votre programme). une telle plate-forme).

Sur presque tous les systèmes, j’ai rencontré uint8_t == unsigned char, mais cela n’est pas garanti par le standard C. Si vous essayez d’écrire du code portable et quelle que soit la taille de la mémoire, utilisez uint8_t. Sinon, utilisez des caractères non signés.

C’est très important par exemple lorsque vous écrivez un parsingur de réseau. Les en-têtes de paquets sont définis par la spécification du protocole, et non par le fonctionnement du compilateur C d’une plate-forme particulière.