web-dev-qa-db-fra.com

Un tampon d'octets doit-il être un tampon de caractères signé ou non signé?

Un tampon d'octets doit-il être un caractère signé ou un caractère non signé ou simplement un tampon de caractères? Des différences entre C et C++?

Merci.

54
jackhab

Un tampon d'octets doit-il être un caractère signé ou un caractère non signé ou simplement un tampon de caractères? Des différences entre C et C++?

Une différence mineure dans la façon dont la langue la traite. A énorme différence dans la façon dont la convention le traite.

  • char = ASCII (ou UTF-8, mais la signature y fait obstacle) textuel data
  • unsigned char = octet
  • signed char = rarement utilisé

Et il y a du code qui s'appuie sur une telle distinction. Il y a une semaine ou deux, j'ai rencontré un bogue où les données JPEG étaient corrompues parce qu'elles étaient transmises au char* version de notre fonction d'encodage Base64 - qui a "utilement" remplacé tous les UTF-8 invalides dans la "chaîne". Passer à BYTE aka unsigned char était tout ce qu'il fallait pour y remédier.

33
dan04

Si vous avez l'intention de stocker des données binaires arbitraires, vous devez utiliser unsigned char. Il s'agit du seul type de données garanti sans bit de remplissage par la norme C. Chaque autre type de données peut contenir des bits de remplissage dans sa représentation d'objet (c'est-à-dire celui qui contient tous les bits d'un objet, au lieu de seulement ceux qui déterminent une valeur). L'état des bits de remplissage n'est pas spécifié et n'est pas utilisé pour stocker des valeurs. Donc, si vous lisez en utilisant char des données binaires, les choses seraient réduites à la plage de valeurs d'un caractère (en n'interprétant que les bits de valeur), mais il peut toujours y avoir des bits qui sont simplement ignorés mais qui sont toujours là et lu par memcpy. Tout comme le remplissage des bits dans les objets structurels réels. Tapez unsigned char est garanti de ne pas les contenir. Cela découle de 5.2.4.2.1/2 (C99 TC2, n1124 ici):

Si la valeur d'un objet de type char est traitée comme un entier signé lorsqu'il est utilisé dans une expression, la valeur de CHAR_MIN doit être le même que celui de SCHAR_MIN et la valeur de CHAR_MAX doit être le même que celui de SCHAR_MAX. Sinon, la valeur de CHAR_MIN doit être 0 et la valeur de CHAR_MAX doit être le même que celui de UCHAR_MAX. La valeur UCHAR_MAX doit être égal à 2^CHAR_BIT − 1

De la dernière phrase, il s'ensuit qu'il n'y a plus d'espace pour les bits de remplissage. Si vous utilisez char comme type de votre tampon, vous avez également le problème des débordements: affectation explicite de n'importe quelle valeur à un tel élément qui est dans la plage de 8 bits - vous pouvez donc vous attendre à ce qu'une telle affectation soit correcte - mais pas dans la plage d'un char, qui est CHAR_MIN..CHAR_MAX, une telle conversion déborde et provoque des résultats définis par l'implémentation, y compris l'augmentation des signaux.

Même si des problèmes concernant ce qui précède n'apparaîtraient probablement pas dans les implémentations réelles (ce serait une très mauvaise qualité d'implémentation), il est préférable d'utiliser le bon tapez depuis le début, ce qui est unsigned char.

Pour les chaînes, cependant, le type de données choisi est char, ce qui sera compris par les fonctions de chaîne et d'impression. En utilisant signed char à ces fins me semble une mauvaise décision.

Pour plus d'informations, lisez this proposal qui contient un correctif pour une prochaine version de la norme C qui nécessitera éventuellement signed char pas de bits de remplissage non plus. Il est déjà incorporé dans le document de travail .

47

Ça dépend.

Si le tampon est destiné à contenir du texte, il est probablement judicieux de le déclarer comme un tableau de char et de laisser la plate-forme décider pour vous s'il est signé ou non par défaut. Cela vous donnera le moins de mal à transmettre les données dans et hors de la bibliothèque d'exécution de l'implémentation, par exemple.

Si le tampon est destiné à contenir des données binaires, cela dépend de la façon dont vous comptez l'utiliser. Par exemple, si les données binaires sont en réalité un ensemble compact d'échantillons de données qui sont des mesures ADC à point fixe 8 bits signées, alors signed char serait mieux.

Dans la plupart des cas réels, le tampon est juste cela, un tampon, et vous ne vous souciez pas vraiment des types des octets individuels parce que vous avez rempli le tampon dans une opération en bloc, et vous êtes sur le point de le transmettre à un analyseur pour interpréter la structure de données complexe et faire quelque chose d'utile. Dans ce cas, déclarez-le de la manière la plus simple.

12
RBerteig

S'il s'agit en fait d'un tampon de 8 octets, plutôt que d'une chaîne dans les paramètres régionaux par défaut de la machine, j'utiliserais uint8_t. Non pas qu'il existe de nombreuses machines dans lesquelles un caractère n'est pas un octet (ou un octet un octet), mais faire la déclaration `` c'est un tampon d'octets '' plutôt que `` c'est une chaîne '' est souvent une documentation utile.

9
Pete Kirkham

Vous devez utiliser soit char ou char non signé mais jamais char signé. La norme présente les éléments suivants en 3.9/2

Pour tout objet (autre qu'un sous-objet de classe de base) de type POD T, que l'objet contienne ou non une valeur valide de type T, les octets sous-jacents (1.7) constituant l'objet peuvent être copiés dans un tableau de caractères ou non signés. Si le contenu du tableau de caractères ou de caractères non signés est recopié dans l'objet, l'objet conservera par la suite sa valeur d'origine.

5
Richard Corden

Il vaut mieux le définir comme caractère non signé. En fait, le type BYTE Win32 est défini comme un caractère non signé. Il n'y a aucune différence entre C & C++ entre cela.

4
Naveen

Pour une portabilité maximale, utilisez toujours un caractère non signé. Il y a quelques cas où cela pourrait entrer en jeu. Les données sérialisées partagées entre les systèmes avec différents types d'endian viennent immédiatement à l'esprit. Lorsque vous effectuez un masquage de décalage ou de bits, les valeurs en sont une autre.

3
MrEvil

Le choix de int8_t vs uint8_t est similaire à lorsque vous comparez un ptr à NULL.


D'un point de vue fonctionnel, la comparaison avec NULL équivaut à la comparaison avec 0 car NULL est une # définition pour 0.

Mais personnellement, du point de vue du style de codage, j'ai choisi de comparer mes pointeurs à NULL car le NULL #define connote la personne qui gère le code que vous recherchez pour un mauvais pointeur ...

CONTRE

quand quelqu'un voit une comparaison à 0, cela signifie que vous recherchez une valeur spécifique.


Pour la raison ci-dessus, j'utiliserais uint8_t.

2
Trevor Boyd Smith

Si vous récupérez un élément dans une variable plus large, il sera bien sûr étendu au signe ou non.

0
pngaz

Devrais et devrais ... j'ai tendance à préférer non signé, car il semble plus "brut", moins invitant à dire "hé, c'est juste un tas de petits ints", si je veux souligner la binaire des données.

Je ne pense pas avoir déjà utilisé un _ signed char pour représenter un tampon d'octets.

Bien sûr, une troisième option consiste à représenter le tampon comme void * autant que possible. De nombreuses fonctions d'E/S courantes fonctionnent avec void *, donc parfois la décision du type d'entier à utiliser peut être entièrement encapsulée, ce qui est bien.

0
unwind

Il y a plusieurs années, j'ai eu un problème avec une application de console C++ qui imprimait des caractères colorés pour ASCII valeurs supérieures à 128 et cela a été résolu en passant de char à char non signé, mais je pense que cela avait été résoluble tout en garder le type de caractère aussi.

Pour l'instant, la plupart des fonctions C/C++ utilisent char et je comprends mieux les deux langages maintenant, donc j'utilise char dans la plupart des cas.

0
schnaader

Ça te préoccupe vraiment? Si vous ne le faites pas, utilisez simplement la valeur par défaut (char) et n'encombrez pas votre code avec une question sans importance. Sinon, les futurs responsables se demanderont pourquoi vous avez utilisé signé (ou non signé). Rendez leur vie plus simple.

0
Gorpik