Question

Étant donné que les ints signés et non signés utilisent les mêmes registres, etc., et n'interprètent que les motifs de bits différemment, et que les caractères C ne sont fondamentalement que de 8 bits, quelle est la différence entre les caractères signés et non signés en C? Je comprends que la signature de char est définie par la mise en œuvre, et je ne comprends tout simplement pas en quoi cela pourrait faire une différence, du moins lorsque char est utilisé pour contenir des chaînes au lieu de faire des calculs.

Était-ce utile?

La solution

Cela ne fera aucune différence pour les chaînes. Mais en C, vous pouvez utiliser un personnage pour faire des mathématiques, quand cela fera une différence.

En fait, lorsque vous travaillez dans des environnements à mémoire contrainte, comme les applications 8 bits intégrées, un caractère est souvent utilisé pour les calculs, ce qui fait toute la différence. En effet, il n'y a pas de byte type par défaut en C.

Autres conseils

En termes de valeurs qu'ils représentent:

caractère non signé:

  • s'étend sur la plage de valeurs 0..255 (00000000..11111111)
  • les valeurs débordent autour du bord inférieur en tant que:

    0 - 1 = 255 (00000000 - 00000001 = 11111111)

  • les valeurs débordent autour du bord supérieur en tant que:

    255 + 1 = 0 (11111111 + 00000001 = 00000000)

  • l'opérateur de décalage droit au niveau du bit (>>) effectue un décalage logique:

    10000000 >> 1 = 01000000 (128 / 2 = 64)

caractère signé:

  • s'étend sur la plage de valeurs -128..127 (10000000..01111111)
  • les valeurs débordent autour du bord inférieur en tant que:

    -128 - 1 = 127 (10000000 - 00000001 = 01111111)

  • les valeurs débordent autour du bord supérieur en tant que:

    127 + 1 = -128 (01111111 + 00000001 = 10000000)

  • l'opérateur de décalage droit au niveau du bit (10000000 >> 1 = 11000000 (-128 / 2 = -64)) effectue un décalage arithmétique:

    <=>

J'ai inclus les représentations binaires pour montrer que le comportement de retour à la valeur est pur, arithmétique binaire cohérente et n'a rien à voir avec un caractère signé / non signé (attendez-vous à des décalages à droite).

Mettre à jour

Certains comportements spécifiques à l'implémentation mentionnés dans les commentaires:

#include <stdio.h>

int main(int argc, char** argv)
{
    char a = 'A';
    char b = 0xFF;
    signed char sa = 'A';
    signed char sb = 0xFF;
    unsigned char ua = 'A';
    unsigned char ub = 0xFF;
    printf("a > b: %s\n", a > b ? "true" : "false");
    printf("sa > sb: %s\n", sa > sb ? "true" : "false");
    printf("ua > ub: %s\n", ua > ub ? "true" : "false");
    return 0;
}


[root]# ./a.out
a > b: true
sa > sb: true
ua > ub: false

Il est important de trier les chaînes.

Il y a une ou deux différences. Plus important encore, si vous dépassez la plage valide d'un caractère en lui attribuant un entier trop grand ou trop petit et que caractère est signé, la valeur résultante est définie par l'implémentation ou même un signal (en C) peut être généré, comme pour tous les types signés. . Comparez cela au cas où vous affectez quelque chose de trop grand ou de petit à un caractère non signé: la valeur est renvoyée à la ligne, vous obtiendrez une sémantique définie avec précision. Par exemple, en affectant -1 à un caractère non signé, vous obtiendrez un UCHAR_MAX. Donc, chaque fois que vous avez un octet comme dans un nombre compris entre 0 et 2 ^ CHAR_BIT, vous devriez vraiment utiliser un caractère non signé pour le stocker.

Le signe fait également la différence lors du passage aux fonctions vararg:

char c = getSomeCharacter(); // returns 0..255
printf("%d\n", c);

Supposons que la valeur affectée à c soit trop grande pour que char soit représentée, et que la machine utilise un complément à deux. De nombreuses implémentations se comportent dans le cas où vous attribuez une valeur trop grande au caractère, car le motif binaire ne changera pas. Si un int est capable de représenter toutes les valeurs de char (ce qui est le cas pour la plupart des implémentations), alors le caractère est promu en int avant de passer à printf. Ainsi, la valeur de ce qui est passé serait négative. Promouvoir l'int serait conserver ce signe. Donc, vous obtiendrez un résultat négatif. Cependant, si char est non signé, alors la valeur est non signée, et passer à un int donnera un int positif. Vous pouvez utiliser un caractère non signé, puis vous obtiendrez un comportement défini avec précision à la fois pour l'affectation à la variable et pour le passage à printf qui imprimera ensuite un résultat positif.

Notez que les caractères non signés et signés ont tous une au moins largeur de 8 bits. Il n'est pas nécessaire que char soit exactement sur 8 bits. Cependant, pour la plupart des systèmes, c'est vrai, mais pour certains, vous constaterez qu'ils utilisent des caractères 32 bits. Un octet en C et C ++ est défini comme ayant la taille d'un caractère. Un octet en C n'a donc pas toujours exactement 8 bits.

Une autre différence est qu’en C, un caractère non signé ne doit avoir aucun bit de remplissage. Autrement dit, si vous trouvez que CHAR_BIT est égal à 8, les valeurs d'un caractère non signé doivent être comprises entre 0 et 2. ^ CHAR_BIT-1. La même chose est vraie pour char s'il n'est pas signé. Pour les caractères signés, vous ne pouvez rien supposer sur la plage de valeurs, même si vous savez comment votre compilateur implémente le texte de signe (complément à deux ou les autres options), il peut contenir des bits de remplissage non utilisés. En C ++, il n'y a pas de bits de remplissage pour les trois types de caractères.

  
    

& "Qu'est-ce que cela signifie pour un caractère d'être signé? &";

  

Traditionnellement, le jeu de caractères ASCII consiste en un codage de caractères à 7 bits. (Par opposition à l’EBCIDIC 8 bits.)

Lorsque le langage C a été conçu et mis en œuvre, il s'agissait d'un problème important. (Pour diverses raisons, telles que la transmission de données sur des périphériques de modem série.) Le bit supplémentaire a des utilisations telles que la parité.

Un "é caractère signé " se trouve être parfait pour cette représentation.

Les données binaires, OTOH, prennent simplement la valeur de chaque "ient de 8 bits & "; des données, aucun signe n'est donc nécessaire.

L'arithmétique sur les octets est importante pour les graphiques informatiques (les valeurs 8 bits étant souvent utilisées pour stocker les couleurs). En dehors de cela, je peux penser à deux cas principaux dans lesquels le signe de caractère est important:

  • conversion en un plus grand int
  • fonctions de comparaison

Ce qui est désagréable, c’est qu’ils ne vous piqueront pas si toutes vos données de chaîne sont en 7 bits. Cependant, cela promet d’être une source inépuisable de bugs obscurs si vous essayez de nettoyer votre programme C / C ++ en 8 bits.

La signature fonctionne à peu près de la même manière dans char que comme dans les autres types d'intégrale. Comme vous l'avez noté, les caractères ne sont en réalité que des entiers à un octet. ( Pas nécessairement 8 bits , cependant! Il existe une différence: un octet peut être supérieur à 8 bits sur certaines plates-formes et les sizeof(char) s sont plutôt liés aux octets en raison des définitions de CHAR_BIT et <limits.h>. La macro <climits>, définie dans byte ou dans le (u?)int_least8_t C ++, vous indiquera le nombre de bits dans un <stdint.h>.).

Pour ce qui est de la raison pour laquelle vous voulez un caractère avec un signe: en C et C ++, il n’existe pas de type standard appelé <cstdint>. Pour le compilateur, <=> s sont des octets et inversement, et il ne fait pas de distinction entre eux. Parfois, cependant, vous voulez - parfois, vous voulez que <=> soit un nombre sur un octet, et dans ces cas (en particulier la taille réduite d'une plage d'octets), que le numéro soit signé ou non. J'ai personnellement utilisé la signature (ou unsignedness) pour indiquer qu'un certain <=> est un (numérique) octet & "; plutôt que d'un personnage, et que ça va être utilisé numériquement. Sans signature spécifiée, <=> c'est vraiment un caractère et est destiné à être utilisé comme texte.

Je le faisais plutôt. Maintenant, les versions les plus récentes de C et C ++ ont <=> (actuellement typedef dans <=> ou <=>), qui sont plus explicitement numériques (bien qu'ils ne soient généralement que des typedefs pour les types <=> signés et non signés) ).

La seule situation où je puisse imaginer que cela pose un problème est que vous choisissiez de faire des calculs sur des caractères. Il est parfaitement légal d'écrire le code suivant.

char a = (char)42;
char b = (char)120;
char c = a + b;

En fonction de la signature du caractère, c peut être l'une des deux valeurs. Si les caractères ne sont pas signés, c sera (caractère) 162. Si elles sont signées, cela entraînera un dépassement de capacité car la valeur maximale pour un caractère signé est 128. Je suppose que la plupart des implémentations renverraient simplement (caractère) -32.

Une chose à propos des caractères signés est que vous pouvez tester c > = '' (espace) et vous assurer que c'est un caractère imprimable normal. Bien sûr, ce n'est pas portable, donc pas très utile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top