Qu'est-ce qu'un caractère non signé?

479

En C / C ++, à quoi unsigned charsert un? En quoi est-ce différent d'un habitué char?

Landon Kuhn
la source

Réponses:

548

En C ++, il existe trois types de caractères distincts :

  • char
  • signed char
  • unsigned char

Si vous utilisez des types de caractères pour le texte , utilisez le non qualifié char:

  • c'est le type de littéraux de caractères comme 'a'ou '0'.
  • c'est le type qui compose les chaînes C comme "abcde"

Il fonctionne également comme une valeur numérique, mais il n'est pas spécifié si cette valeur est traitée comme signée ou non signée. Méfiez-vous des comparaisons de caractères à travers les inégalités - bien que si vous vous limitez à ASCII (0-127), vous êtes à peu près en sécurité.

Si vous utilisez des types de caractères sous forme de nombres , utilisez:

  • signed char, ce qui vous donne au moins la gamme -127 à 127. (-128 à 127 est courant)
  • unsigned char, ce qui vous donne au moins la plage de 0 à 255.

"Au moins", car la norme C ++ ne donne que la plage minimale de valeurs que chaque type numérique doit couvrir. sizeof (char)doit être 1 (c'est-à-dire un octet), mais un octet pourrait en théorie être par exemple 32 bits. sizeofserait toujours signaler sa taille comme1 - ce qui signifie que vous pourriez avoir sizeof (char) == sizeof (long) == 1.

Fruny
la source
4
Pour être clair, pourriez-vous avoir des caractères 32 bits et des entiers 32 bits et avoir sizeof (int)! = Sizeof (char)? Je sais que la norme dit sizeof (char) == 1, mais la taille relative (int) est-elle basée sur la différence réelle de taille ou la différence de plage?
Joseph Garvin
14
+1. Mais il existe quatre types de caractères distincts en C ++, wchar_t en fait partie.
Eric Z
11
depuis c ++ 11, vous avez 6 types distincts: char, char signé, char non signé, wchar_t, char16_t, char32_t.
marcinj
12
@unheilig Il est courant de placer un espace après sizeofcar ce n'est pas une fonction mais un opérateur. Il est préférable de supprimer la parenthèse lors de la prise de la taille d'une variable. sizeof *pou sizeof (int). Cela indique rapidement si elle s'applique à un type ou à une variable. De même, il est également redondant de mettre des parenthèses après return. Ce n'est pas une fonction.
Patrick Schlüter,
3
" char: c'est le type de littéraux de caractères comme 'a'ou '0'." est vrai en C ++ mais pas en C. En C, 'a'est un int.
chux
92

Cela dépend de l'implémentation, car la norme C ne définit PAS la signature de char. Selon la plate-forme, char peut être signedor unsigned, vous devez donc demander explicitement signed charou unsigned charsi votre implémentation en dépend. Utilisez simplement charsi vous avez l'intention de représenter des caractères à partir de chaînes, car cela correspondra à ce que votre plate-forme met dans la chaîne.

La différence entre signed charet unsigned charest comme vous vous en doutez. Sur la plupart des plates-formes, signed charsera un nombre complémentaire de deux bits de 8 bits allant de -128à 127, et unsigned charsera un entier non signé de 8 bits ( 0à 255). Notez que la norme n'exige PAS que les chartypes aient 8 bits, seulement ce sizeof(char)retour 1. Vous pouvez obtenir le nombre de bits dans un caractère avec CHAR_BITin limits.h. Il y a peu ou pas de plateformes aujourd'hui où ce sera autre chose que 8, cependant.

Il y a un bon résumé de ce problème ici .

Comme d'autres l'ont mentionné depuis que j'ai posté cela, il vaut mieux utiliser int8_tet uint8_tsi vous voulez vraiment représenter de petits entiers.

Todd Gamblin
la source
2
signed char ont seulement une portée minimale de entre -127 et 127, et non -128 à 127
12431234123412341234123
3
@ 12431234123412341234123: Techniquement vrai, en ce que la norme C définit -127 à 127 comme la plage minimale. Je vous mets au défi de trouver une plate-forme qui n'utilise pas l'arithmétique du complément à deux, cependant. Sur presque toutes les plates-formes modernes, la gamme réelle des caractères signés sera de -128 à 127.
Todd Gamblin
CHAR_BITdoit être d'au moins 8 bits selon la norme.
martinkunev
39

Parce que je pense que c'est vraiment nécessaire, je veux juste énoncer quelques règles de C et C ++ (elles sont les mêmes à cet égard). Tout d'abord, tous les bits de unsigned charparticipent à la détermination de la valeur d'un objet char non signé. Deuxièmement, unsigned charest explicitement déclaré non signé.

Maintenant, j'ai eu une discussion avec quelqu'un sur ce qui se passe lorsque vous convertissez la valeur -1de type int en unsigned char. Il a refusé l'idée que le résultat unsigned charait tous ses bits mis à 1, car il était préoccupé par la représentation des signes. Mais il n'est pas obligé. C'est immédiatement après cette règle que la conversion fait ce qui est prévu:

Si le nouveau type n'est pas signé, la valeur est convertie en ajoutant ou en soustrayant à plusieurs reprises une valeur de plus que la valeur maximale pouvant être représentée dans le nouveau type jusqu'à ce que la valeur soit dans la plage du nouveau type. ( 6.3.1.3p2dans un projet C99)

Voilà une description mathématique. C ++ le décrit en termes de calcul modulo, qui cède à la même règle. Quoi qu'il en soit, ce qui n'est pas garanti, c'est que tous les bits de l'entier -1sont un avant la conversion. Alors, qu'est-ce que nous avons pour que nous puissions prétendre que le résultat unsigned chara tous ses CHAR_BITbits mis à 1?

  1. Tous les bits participent à la détermination de sa valeur, c'est-à-dire qu'aucun bit de remplissage ne se produit dans l'objet.
  2. L'ajout d'une seule fois UCHAR_MAX+1à -1donnera une valeur dans la plage, à savoirUCHAR_MAX

Ça suffit, en fait! Donc, chaque fois que vous voulez en avoir un unsigned char, vous le faites

unsigned char c = (unsigned char)-1;

Il s'ensuit également qu'une conversion ne consiste pas seulement à tronquer des bits d'ordre supérieur. L'événement heureux pour le complément à deux est qu'il ne s'agit que d'une troncature, mais ce n'est pas nécessairement le cas pour les autres représentations de signes.

Johannes Schaub - litb
la source
2
Pourquoi ne pas simplement utiliser UCHAR_MAX?
Nicolás
1
Parce que (unsigned type)-1c'est une sorte d'idiome. ~0n'est pas.
Patrick Schlüter
1
si j'ai quelque chose comme ça int x = 1234et char *y = &x. Représentation binaire de 1234 is 00000000 00000000 00000100 11010010. Ma machine est peu endienne donc elle l'inverse et stocker en mémoire 11010010 00000100 00000000 00000000LSB vient en premier. Maintenant partie principale. si j'utilise printf("%d" , *p). printflira premier octet 11010010que la sortie est , -46mais 11010010est 210alors pourquoi faut - il imprimer -46. Je suis vraiment confus, je suppose que certains chars en promotion entière font quelque chose mais je ne sais pas.
Suraj Jain
27

Comme par exemple les utilisations du caractère non signé :

unsigned charest souvent utilisé en infographie, qui attribue très souvent (mais pas toujours) un seul octet à chaque composant de couleur. Il est courant de voir une couleur RGB (ou RGBA) représentée par 24 (ou 32) bits, chacun un unsigned char. Étant donné que les unsigned charvaleurs se situent dans la plage [0,255], les valeurs sont généralement interprétées comme:

  • 0 signifiant une absence totale d'une composante de couleur donnée.
  • 255 signifie 100% d'un pigment de couleur donné.

Vous vous retrouveriez donc avec du rouge RVB comme (255,0,0) -> (100% rouge, 0% vert, 0% bleu).

Pourquoi ne pas utiliser un signed char? Le décalage arithmétique et binaire devient problématique. Comme expliqué précédemment, signed charla plage de a est essentiellement décalée de -128. Une méthode très simple et naïve (pour la plupart inutilisée) pour convertir le RVB en niveaux de gris consiste à faire la moyenne des trois composantes de couleur, mais cela pose des problèmes lorsque les valeurs des composantes de couleur sont négatives. Le rouge (255, 0, 0) fait la moyenne de (85, 85, 85) lors de l'utilisation de l' unsigned chararithmétique. Cependant, si les valeurs étaient signed chars (127, -128, -128), nous nous retrouverions avec (-99, -99, -99), ce qui serait (29, 29, 29) dans notre unsigned charespace, ce qui est incorrect .

Zachary Garrett
la source
13

Si vous souhaitez utiliser un caractère comme un petit entier, la façon la plus sûre de le faire est d'utiliser les types int8_tet uint8_t.

jbleners
la source
2
Pas une bonne idée: int8_tet uint8_tsont facultatives et ne sont pas définis sur des architectures où la taille des octets ne sont pas exactement 8 bits. Inversement, signed charet unsigned charsont toujours disponibles et garantis pour contenir au moins 8 bits. C'est peut-être un moyen courant mais pas le plus sûr .
chqrlie
2
Ceci est un commentaire, il ne répond pas à la question.
Lundin
@chqrlie Donc, vous voulez dire que la vraie façon la plus sûre de représenter un petit entier, si vous voulez économiser de la mémoire, est de garder avec signed charet unsigned char? Ou recommanderiez-vous une meilleure alternative "plus sûre" dans ce cas particulier? Par exemple, pour s'en tenir aux types entiers "réels" signed intet à la unsigned intplace pour une raison quelconque?
RobertS soutient Monica Cellio
@ RobertS-ReinstateMonica: Utiliser signed charet unsigned charest portable pour toutes les implémentations conformes et économisera de l'espace de stockage mais peut entraîner une augmentation de la taille du code. Dans certains cas, on économiserait plus d'espace de stockage en stockant de petites valeurs dans des champs binaires ou des bits simples de types entiers réguliers. Il n'y a pas de réponse absolue à cette question, la pertinence de cette approche dépend du cas d'espèce. Et cette réponse ne répond pas de toute façon à la question.
chqrlie
10

unsigned charprend uniquement des valeurs positives ... comme 0 à 255

tandis que

signed charprend des valeurs positives et négatives ... comme -128 à +127

munna
la source
9

charet unsigned charne sont pas garantis comme étant de type 8 bits sur toutes les plates-formes - ils sont garantis comme étant de 8 bits ou plus. Certaines plates-formes ont des octets 9 bits, 32 bits ou 64 bits . Cependant, les plates-formes les plus courantes aujourd'hui (Windows, Mac, Linux x86, etc.) ont des octets 8 bits.

bk1e
la source
8

signed chara une plage de -128 à 127; unsigned chara une plage de 0 à 255.

char sera équivalent à char signé ou non signé, selon le compilateur, mais est un type distinct.

Si vous utilisez des chaînes de style C, utilisez simplement char. Si vous devez utiliser des caractères pour l'arithmétique (assez rare), spécifiez explicitement signé ou non pour la portabilité.

James Hopkin
la source
8

An unsigned charest une valeur d'octet non signée (0 à 255). Vous pensez peut-être charen termes d'être un "personnage" mais c'est vraiment une valeur numérique. Le régulier charest signé, vous avez donc 128 valeurs, et ces valeurs sont mappées à des caractères à l'aide du codage ASCII. Mais dans les deux cas, ce que vous stockez en mémoire est une valeur d'octet.

Zac Gochenour
la source
7

En termes de valeurs directes, un caractère normal est utilisé lorsque les valeurs sont comprises entre CHAR_MINet CHAR_MAXtandis qu'un caractère non signé fournit le double de la plage à l'extrémité positive. Par exemple, si la valeur CHAR_BITest 8, la plage de valeurs régulières charest uniquement garantie d'être [0, 127] (car elle peut être signée ou non) tandis que unsigned charsera [0, 255] et signed charsera [-127, 127].

En termes d'utilisation, les normes permettent aux objets de POD (données anciennes simples) d'être directement convertis en un tableau de caractères non signés. Cela vous permet d'examiner la représentation et les motifs binaires de l'objet. La même garantie de punition de type sûre n'existe pas pour le caractère ou le caractère signé.

Julienne Walker
la source
En fait, ce sera le plus souvent [-128, 128].
RastaJedi
Les normes ne définissent formellement la représentation de l'objet que comme une séquence de unsigned char, pas un tableau en particulier, et toute "conversion" n'est définie formellement qu'en copiant de l'objet vers un véritable tableau déclaré de unsigned char& inspectant ensuite ce dernier. Il n'est pas clair si le OU peut être directement réinterprété comme un tel tableau, avec les tolérances pour l'arithmétique du pointeur qu'il impliquerait, c'est-à-dire si "séquence" =="tableau" dans cette utilisation. Il y a un problème principal n ° 1701 dans l'espoir de clarifier cela. Heureusement, car cette ambiguïté me dérange vraiment récemment.
underscore_d
1
@RastaJedi Non, ce ne sera pas le cas. Ça ne peut pas. Une plage de -128 ... + 128 est physiquement impossible à représenter avec 8 bits. Cette largeur ne prend en charge que 2 ^ 8 == 256 valeurs discrètes, mais -128 ... + 128 = 2 * 128 + 1 pour 0 = 257. La représentation de l'amplitude des signes permet -127 ... + 127 mais en a 2 (bipolaire) des zéros. La représentation du complément à deux maintient un zéro unique mais compose la plage en ayant une valeur de plus du côté négatif; il permet -128 ... + 127. (Et ainsi de suite pour les deux à des largeurs de bits plus grandes.)
underscore_d
Concernant mon deuxième commentaire, il est raisonnable de supposer que nous pouvons prendre un pointeur sur le 1er unsigned chardu bloc ++ptropératoire, puis continuer à utiliser à partir de là pour en lire chaque octet ... mais AFAICT, ce n'est pas spécifiquement défini comme étant autorisé, donc nous sommes laissé à déduire que c'est «probablement OK» de nombreux autres passages (et à bien des égards, la simple existence de memcpy) dans la norme, semblable à un puzzle. Ce qui n'est pas idéal. Eh bien, peut-être que le libellé s'améliorera éventuellement. Voici le problème CWG que j'ai mentionné mais qui manquait d'espace pour créer un
underscore_d
@underscore_d désolé, c'était une faute de frappe. [-128, 127] est ce que je voulais taper: p. Oui, je connais les doubles zéros (zéro «positif» et «négatif») avec signe / amplitude. Je devais être fatigué: p.
RastaJedi
5

unsigned charest le cœur de la supercherie. Dans presque TOUS les compilateurs pour TOUTES les plateformes, un unsigned charest simplement un octet et un entier non signé de (généralement) 8 bits qui peut être traité comme un petit entier ou un paquet de bits.

En dépendance, comme quelqu'un l'a dit, la norme ne définit pas le signe d'un char. de sorte que vous avez 3 différents chartypes: char, signed char, unsigned char.

ugasoft
la source
1
La supercherie de bits, alias le twiddling de bits ou le piratage de bits est en effet connue pour provoquer une dépendance ;-)
chqrlie
3
Ce sont les 0 qui causent des problèmes. Pour éviter la dépendance de twiddling, éloignez-vous des bits noughty.
DragonLord
5

Si vous aimez en utilisant différents types de longueur spécifique et signedness, vous êtes probablement mieux avec uint8_t, int8_t, uint16_t, etc simplement parce qu'ils font exactement ce qu'ils disent.

Shikari noir
la source
4

Certains googleurs ont trouvé cela , où les gens ont eu une discussion à ce sujet.

Un caractère non signé est essentiellement un octet unique. Donc, vous l'utiliseriez si vous avez besoin d'un octet de données (par exemple, vous souhaitez peut-être l'utiliser pour activer et désactiver les indicateurs à transmettre à une fonction, comme cela se fait souvent dans l'API Windows).

dbrien
la source
4

Un caractère non signé utilise le bit réservé au signe d'un caractère normal comme un autre nombre. Cela change la plage en [0 - 255] par opposition à [-128 - 127].

Généralement, les caractères non signés sont utilisés lorsque vous ne voulez pas de signe. Cela fera une différence lorsque vous faites des choses comme le décalage des bits (décalage étend le signe) et d'autres choses lorsque vous traitez un caractère comme un octet plutôt que de l'utiliser comme un nombre.


la source
4

unsigned charprend uniquement des valeurs positives: 0 à 255 tandis que signed charprend des valeurs positives et négatives: -128 à +127.

NL628
la source
3

cité à partir du livre "le c programmation laugage":

Le qualificatif signedou unsignedpeut être appliqué à char ou à n'importe quel entier. les nombres non signés sont toujours positifs ou nuls et obéissent aux lois du module arithmétique 2 ^ n, où n est le nombre de bits du type. Ainsi, par exemple, si les caractères sont de 8 bits, les variables de caractères non signés ont des valeurs comprises entre 0 et 255, tandis que les caractères signés ont des valeurs comprises entre -128 et 127 (dans une machine complémentaire à deux). -dépendant, mais les caractères imprimables sont toujours positifs.

ZhaoGang
la source
2

signed charet les unsigned chardeux représentent 1 octet, mais ils ont des plages différentes.

   Type        |      range
-------------------------------
signed char    |  -128 to +127
unsigned char  |     0 to 255

Dans signed charsi nous considérons char letter = 'A', 'A' est un binaire de 65 pouces ASCII/Unicode, si 65 peut être stocké, -65 peut également être stocké. Il n'y a pas de valeurs binaires négatives ASCII/Unicodelà-dedans sans avoir à se soucier des valeurs négatives.

Exemple

#include <stdio.h>

int main()
{
    signed char char1 = 255;
    signed char char2 = -128;
    unsigned char char3 = 255;
    unsigned char char4 = -128;

    printf("Signed char(255) : %d\n",char1);
    printf("Unsigned char(255) : %d\n",char3);

    printf("\nSigned char(-128) : %d\n",char2);
    printf("Unsigned char(-128) : %d\n",char4);

    return 0;
}

Production -:

Signed char(255) : -1
Unsigned char(255) : 255

Signed char(-128) : -128
Unsigned char(-128) : 128
Kalana
la source