web-dev-qa-db-fra.com

uint8_t vs caractère non signé

Quel est l'avantage d'utiliser uint8_t par rapport à unsigned char en C?

Je sais que sur presque tous les systèmes, uint8_t n'est qu'un type pour unsigned char, alors pourquoi l'utiliser?

211
Lyndon White

Il documente votre intention - vous allez stocker de petits nombres, plutôt qu'un personnage.

En outre, cela semble plus agréable si vous utilisez d'autres types de caractères tels que uint16_t ou int32_t.

207
Mark Ransom

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

Une implémentation est nécessaire pour définir des types entiers de largeur exacte pour N = 8, 16, 32 ou 64 si et seulement si elle possède un type qui répond 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, uint8_t n'est pas garanti, bien que ce soit le cas pour toutes les plateformes où 8 bits = 1 octet. Certaines plates-formes intégrées peuvent être différentes, mais cela devient très rare. Certains systèmes peuvent définir les types char sur 16 bits, auquel cas il ne sera probablement pas de type 8 bits.

Autre que ce problème (mineur), réponse de @ Mark Ransom est le meilleur à mon avis. Utilisez celui qui montre le plus clairement à quoi vous utilisez les données.

De plus, je suppose que vous vouliez parler de uint8_t (la typedef standard de C99 fournie dans l'en-tête stdint.h) plutôt que de uint_8 (qui ne fait partie d'aucune norme).

63
Chris Lutz

Le but est d'écrire du code indépendant de l'implémentation. unsigned char n'est pas garanti d'être de type 8 bits. uint8_t est (si disponible).

40
AnT

D'après mon expérience, il y a deux endroits où nous voulons utiliser uint8_t pour signifier 8 bits (et uint16_t, etc.) et où nous pouvons avoir des champs de moins de 8 bits. L'espace est primordial aux deux endroits et nous devons souvent examiner les données brutes lors du débogage et déterminer rapidement ce qu'elles représentent.

Le premier est dans les protocoles RF, en particulier dans les systèmes à bande étroite. Dans cet environnement, il peut être nécessaire de regrouper autant d'informations que possible dans un seul message. La seconde est dans le stockage flash où l’espace peut être très limité (comme dans les systèmes embarqués). Dans les deux cas, nous pouvons utiliser une structure de données compacte dans laquelle le compilateur se chargera de l’emballage et du décompactage:

#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 __attribute__((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 intégré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 principal 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 s'inquièteront également des performances et soutiendront que l'utilisation de ces structures compactes ralentira votre système. Il est vrai que, en coulisse, le compilateur ajoute du code pour accéder aux membres de données non alignés. Vous pouvez le voir en regardant le code d'assemblage dans votre IDE.

Mais étant donné que les structures compactes 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 les utilisez en mémoire. Normalement, nous n’avons de toute façon pas besoin de travailler avec l’ensemble du paquet de données en mémoire.

Voici quelques discussions pertinentes:

pragma pack (1) ni __attribute__ ((aligné (1))) fonctionne

Est-ce que __attribute __ ((emballé))/#pragma pack de gcc est dangereux?

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

7
Tereus Scott

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 amis, utilisez uint8_t mieux les mélanges, et peut même faire partie d'une norme de codage.

7
Justin Love

Il y a peu. Du point de vue de la portabilité, char ne peut pas être inférieur à 8 bits et rien ne peut être inférieur à char. Ainsi, si une implémentation C donnée a un type entier non signé de 8 bits, elle sera char. Sinon, il se peut qu’il n’en ait pas du tout, auquel cas toutes les astuces typedef sont discutables.

Cela pourrait être utilisé pour mieux documenter votre code en ce sens qu'il est clair que vous avez besoin d'octets 8 bits et rien d'autre. Mais dans la pratique, il existe déjà des attentes raisonnables pratiquement partout (il existe des plates-formes DSP sur lesquelles ce n’est pas vrai, mais les chances que votre code y soit exécuté soient minces, et vous pourriez tout aussi bien vous tromper en utilisant une assertion statique en haut de votre programme. une telle plate-forme).

6
Pavel Minaev

C'est très important par exemple lorsque vous écrivez un analyseur de réseau. Les en-têtes de paquet sont définis par la spécification du protocole et non par la façon dont fonctionne le compilateur C d'une plate-forme particulière.

3
VP.

Sur presque tous les systèmes, j’ai rencontré uint8_t == unsigned char, mais cela n’est pas garanti par la norme C. Si vous essayez d'écrire du code portable et que la taille de la mémoire importe exactement, utilisez uint8_t. Sinon, utilisez un caractère non signé.

2
atlpeg