O que é um caractere não assinado?

479

Em C / C ++, para que unsigned charé usado? Como é diferente de um regular char?

Landon Kuhn
fonte

Respostas:

548

No C ++, existem três tipos de caracteres distintos :

  • char
  • signed char
  • unsigned char

Se você estiver usando tipos de caracteres para texto , use o não qualificado char:

  • é o tipo de literal de caractere como 'a'ou '0'.
  • é o tipo que compõe cadeias C como "abcde"

Também funciona como um valor numérico, mas não é especificado se esse valor é tratado como assinado ou não. Cuidado com as comparações de caracteres por meio de desigualdades - embora se você se limitar a ASCII (0-127), estará quase seguro.

Se você estiver usando tipos de caracteres como números , use:

  • signed char, que fornece pelo menos o intervalo de -127 a 127. (-128 a 127 é comum)
  • unsigned char, que fornece pelo menos o intervalo de 0 a 255.

"Pelo menos", porque o padrão C ++ fornece apenas o intervalo mínimo de valores que cada tipo numérico é necessário para cobrir. sizeof (char)é necessário que seja 1 (ou seja, um byte), mas em teoria um byte pode ter, por exemplo, 32 bits. sizeofainda seria informar seu tamanho como1 - o que você poderia ter sizeof (char) == sizeof (long) == 1.

Fruny
fonte
4
Para ser claro, você poderia ter caracteres de 32 bits e números inteiros de 32 bits, além de sizeof (int)! = Sizeof (char)? Eu sei que o padrão diz sizeof (char) == 1, mas o sizeof (int) relativo é baseado na diferença real de tamanho ou na diferença de intervalo?
31711 Joseph Garvin
14
+1. Mas existem quatro tipos distintos de caracteres em C ++, wchar_t é um deles.
Eric Z
11
desde o c ++ 11, você tem 6 tipos distintos: char, char assinado, char não assinado, wchar_t, char16_t, char32_t.
21768 Marc Marvin
12
@unheilig É comum colocar um espaço depois, sizeofporque não é uma função, mas um operador. É imho um estilo ainda melhor para omitir os parênteses quando se assume o tamanho de uma variável. sizeof *pou sizeof (int). Isso deixa claro rapidamente se se aplica a um tipo ou variável. Da mesma forma, também é redundante colocar parênteses depois return. Não é uma função.
Patrick Schlüter
3
" char: é o tipo de literal de caractere como 'a'ou '0'." é verdadeiro em C ++, mas não em C. Em C, 'a'é um int.
chux - Restabelece Monica
92

Isso depende da implementação, pois o padrão C NÃO define a assinatura de char. Dependendo da plataforma, char pode ser signedou unsigned, portanto, você precisa solicitar explicitamente signed charou unsigned charse sua implementação depende disso. Basta usar charse você pretende representar caracteres de cadeias, pois isso corresponderá ao que sua plataforma coloca na cadeia.

A diferença entre signed chare unsigned charé como você esperaria. Na maioria das plataformas, signed charhaverá um número de complemento de dois bits de 8 bits que varia de -128a 127e unsigned charserá um número inteiro não assinado de 8 bits ( 0para 255). Observe que o padrão NÃO exige que os chartipos tenham 8 bits, apenas esse sizeof(char)retorno 1. Você pode obter o número de bits em um caractere com CHAR_BITin limits.h. Hoje existem poucas plataformas, se houver alguma, em que isso será algo diferente 8.

Há um bom resumo dessa questão aqui .

Como outros já mencionaram desde que eu postei isso, é melhor você usar int8_te uint8_tse realmente deseja representar números inteiros pequenos.

Todd Gamblin
fonte
2
char assinado tem apenas um intervalo mínimo de -127 a 127, e não de -128 a 127
12431234123412341234123
3
@ 12431234123412341234123: Tecnicamente verdade, pois o padrão C define -127 a 127 como o intervalo mínimo. Eu desafio você a encontrar uma plataforma que não use a aritmética de complemento de dois. Em quase todos os plataforma moderna, o alcance real de caracteres assinado será -128 a 127.
Todd Gamblin
CHAR_BITé necessário ter pelo menos 8 bits pelo padrão.
22819 martinkunev
39

Porque eu sinto que é realmente necessário, eu só quero declarar algumas regras de C e C ++ (elas são as mesmas a esse respeito). Primeiro, todos os bits de unsigned charparticipar na determinação do valor se qualquer objeto sem assinatura char. Segundo, unsigned charé explicitamente declarado sem sinal.

Agora, tive uma discussão com alguém sobre o que acontece quando você converte o valor -1do tipo int unsigned char. Ele recusou a ideia de que o resultado unsigned chartenha todos os bits definidos como 1, porque estava preocupado com a representação de sinais. Mas ele não precisa. Imediatamente após esta regra, a conversão faz o que se destina:

Se o novo tipo não estiver assinado, o valor será convertido adicionando ou subtraindo repetidamente um mais que o valor máximo que pode ser representado no novo tipo até que o valor esteja no intervalo do novo tipo. ( 6.3.1.3p2em um rascunho de C99)

Essa é uma descrição matemática. O C ++ o descreve em termos de módulo de cálculo, que gera a mesma regra. De qualquer forma, o que não é garantido é que todos os bits no número inteiro -1são um antes da conversão. Então, o que temos para afirmar que o resultado unsigned chartem todos os seus CHAR_BITbits voltados para 1?

  1. Todos os bits participam na determinação de seu valor - ou seja, nenhum bit de preenchimento ocorre no objeto.
  2. Adicionar apenas uma vez UCHAR_MAX+1para -1produzirá um valor no intervalo, ou seja,UCHAR_MAX

Já chega, na verdade! Então, sempre que você quiser unsigned charter todos os seus bits um, faça

unsigned char c = (unsigned char)-1;

Segue-se também que uma conversão não está apenas truncando bits de ordem superior. O evento feliz para o complemento de dois é que é apenas um truncamento, mas o mesmo não é necessariamente verdadeiro para outras representações de signos.

Johannes Schaub - litb
fonte
2
Por que não usar apenas UCHAR_MAX?
Nicolás
1
Porque (unsigned type)-1é algum tipo de linguagem. ~0não é.
Patrick Schlüter
1
se eu tiver algo parecido com isto int x = 1234e char *y = &x. Representação binária de 1234 é 00000000 00000000 00000100 11010010. Minha máquina é pouco endian e, portanto, a reverte e armazena na memória que o 11010010 00000100 00000000 00000000LSB vem em primeiro lugar. Agora parte principal. se eu usar printf("%d" , *p). printflerá primeiro byte 11010010apenas a saída é, -46mas 11010010é 210por isso que ela é impressa -46. Estou realmente confuso, eu acho que algum char para promoção inteira está fazendo algo, mas eu não sei.
precisa saber é o seguinte
27

Como, por exemplo, usos de caracteres não assinados :

unsigned charé frequentemente usado em computação gráfica, que muitas vezes (embora nem sempre) atribui um único byte a cada componente de cor. É comum ver uma cor RGB (ou RGBA) representada como 24 (ou 32) bits, cada um unsigned char. Como os unsigned charvalores caem no intervalo [0,255], os valores são geralmente interpretados como:

  • 0 significa uma falta total de um determinado componente de cor.
  • 255, o que significa 100% de um determinado pigmento colorido.

Assim, você terminaria com o vermelho RGB como (255,0,0) -> (100% vermelho, 0% verde, 0% azul).

Por que não usar um signed char? A aritmética e a troca de bits se tornam problemáticas. Como já explicado, signed charo intervalo de a é essencialmente deslocado por -128. Um método muito simples e ingênuo (principalmente não utilizado) para converter RGB em escala de cinza é calcular a média dos três componentes de cores, mas isso ocorre quando os valores dos componentes de cores são negativos. A média de vermelho (255, 0, 0) é de (85, 85, 85) ao usar unsigned chararitmética. No entanto, se os valores fossem signed chars (127, -128, -128), terminaríamos com (-99, -99, -99), que seria (29, 29, 29) em nosso unsigned charespaço, o que está incorreto .

Zachary Garrett
fonte
13

Se você deseja usar um caractere como um número inteiro pequeno, a maneira mais segura de fazer isso é com os tipos int8_teuint8_t

jbleners
fonte
2
Não é uma boa ideia: int8_te uint8_tsão opcionais e não definida em arquiteturas onde o tamanho byte não é exatamente 8 bits. Por outro lado, signed chare unsigned charestão sempre disponíveis e garantidos para conter pelo menos 8 bits. Pode ser uma maneira comum , mas não a mais segura .
chqrlie
2
Este é um comentário, não responde à pergunta.
Lundin
@chqrlie Então você quer dizer que a maneira mais segura de representar um número inteiro pequeno, se você quiser economizar memória, é continuar com signed chare unsigned char? Ou você recomendaria uma alternativa melhor "mais segura" nesse caso específico? Por exemplo, ficar com os tipos inteiros "reais" signed inte, unsigned intpor algum motivo?
RobertS suporta Monica Cellio 21/12/19
@ RobertS-ReinstateMonica: Usar signed chare unsigned charé portátil para todas as implementações em conformidade e economizará espaço de armazenamento, mas poderá causar algum aumento no tamanho do código. Em alguns casos, economizaria mais espaço de armazenamento armazenando pequenos valores em campos de bits ou bits únicos de tipos inteiros regulares. Não há resposta absoluta para essa pergunta, a pertinência dessa abordagem depende do caso específico em questão. E esta resposta não aborda a questão de qualquer maneira.
chqrlie
10

unsigned charleva apenas valores positivos .... como 0 a 255

enquanto que

signed charleva valores positivos e negativos .... como -128 a +127

munna
fonte
9

chare unsigned charnão são garantidos tipos de 8 bits em todas as plataformas - eles têm 8 bits ou mais. Algumas plataformas têm bytes de 9, 32 ou 64 bits . No entanto, as plataformas mais comuns hoje em dia (Windows, Mac, Linux x86 etc.) possuem bytes de 8 bits.

bk1e
fonte
8

signed chartem um intervalo de -128 a 127; unsigned chartem intervalo de 0 a 255.

char será equivalente a um caractere assinado ou não assinado, dependendo do compilador, mas é um tipo distinto.

Se você estiver usando seqüências de caracteres em estilo C, use char. Se você precisar usar chars para aritmética (bastante raro), especifique assinado ou não assinado explicitamente para portabilidade.

James Hopkin
fonte
8

An unsigned charé um valor de byte não assinado (0 a 255). Você pode estar pensando charem termos de ser um "personagem", mas é realmente um valor numérico. O regular charé assinado, então você tem 128 valores e esses valores são mapeados para caracteres usando a codificação ASCII. Mas em ambos os casos, o que você está armazenando na memória é um valor de byte.

Zac Gochenour
fonte
7

Em termos de valores diretos, um caractere comum é usado quando se sabe que os valores estão entre CHAR_MINe CHAR_MAXenquanto um caractere não assinado fornece o dobro do intervalo na extremidade positiva. Por exemplo, se CHAR_BITfor 8, o intervalo de regular charé garantido apenas como [0, 127] (porque pode ser assinado ou não assinado) enquanto unsigned charserá [0, 255] e signed charserá [-127, 127].

Em termos de para que é usado, os padrões permitem que objetos de POD (dados antigos simples) sejam diretamente convertidos em uma matriz de caracteres não assinados. Isso permite que você examine a representação e os padrões de bits do objeto. A mesma garantia de punção de tipo seguro não existe para char ou char assinado.

Julienne Walker
fonte
Na verdade, na maioria das vezes será [-128, 128].
RastaJedi
As normas definem apenas formalmente a representação objecto como uma sequência de unsigned char, e não uma matriz especificamente, & qualquer "conversão" só é formalmente definido por cópia do objecto para uma verdadeira, declarado gama de unsigned char&, em seguida, inspeccionar o último. Não está claro se o OR pode ser reinterpretado diretamente como uma matriz, com as permissões para a aritmética do ponteiro que isso implicaria, ou seja, se "seqüência" =="matriz" nesse uso. Há um problema central nº 1701 aberto na esperança de esclarecer isso. Felizmente, como essa ambiguidade está realmente me incomodando recentemente.
Underscore_d
1
@RastaJedi Não, não vai. Não pode. Um intervalo de -128 ... + 128 é fisicamente impossível de representar com 8 bits. Essa largura suporta apenas 2 ^ 8 == 256 valores discretos, mas -128 ... + 128 = 2 * 128 + 1 para 0 = 257. A representação de magnitude de sinal permite -127 ... + 127 mas tem 2 (bipolar) zeros. A representação do complemento de dois mantém um único zero, mas compõe o intervalo tendo mais um valor no lado negativo; permite -128 ... + 127. (E assim por diante, tanto para a largura pouco maior.)
underscore_d
No meu segundo comentário, é razoável presumir que podemos pegar um ponteiro para o 1º unsigned charda ++ptrsala de cirurgia e continuar usando a partir daí para ler todos os bytes ... mas AFAICT, não é definido especificamente como permitido, então estamos resta inferir que é 'provavelmente OK' de muitas outras passagens (e de muitas maneiras, a mera existência de memcpy) no Padrão, semelhante a um quebra-cabeça. O que não é o ideal. Bem, talvez a redação melhore eventualmente. Aqui está o problema do CWG que mencionei, mas sem espaço para vincular - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
underscore_d
@underscore_d desculpe, isso foi um erro de digitação. [-128, 127] é o que eu pretendia digitar: p. Sim, eu sei sobre os zeros duplos (zero 'positivo' e 'negativo') com sinal / magnitude. Eu devo estar cansado: p.
precisa saber é o seguinte
5

unsigned charé o coração de todos os truques. Em quase TODO o compilador para TODA a plataforma, um unsigned charé simplesmente um byte e um número inteiro não assinado de (geralmente) 8 bits que podem ser tratados como um número inteiro pequeno ou um pacote de bits.

Além disso, como alguém já disse, o padrão não define o sinal de um caractere. então você tem 3 diferentes chartipos: char, signed char, unsigned char.

ugasoft
fonte
1
Bit malandragem, aka mordeu girando ou pouco pirataria é realmente conhecida por causar dependência ;-)
chqrlie
3
São os 0 que causam problemas. Para evitar o vício de mexer, fique longe dos pedaços ruins.
DragonLord 23/05
5

Se você gosta de usar vários tipos de comprimento e de assinatura específica, você é provavelmente melhor fora com uint8_t, int8_t, uint16_t, etc, simplesmente porque eles fazem exatamente o que eles dizem.

Dark Shikari
fonte
4

Alguns pesquisadores descobriram isso , onde as pessoas discutiram sobre isso.

Um caracter não assinado é basicamente um byte único. Portanto, você usaria isso se precisar de um byte de dados (por exemplo, talvez você queira usá-lo para ativar e desativar sinalizadores para serem passados ​​para uma função, como costuma ser feito na API do Windows).

dbrien
fonte
4

Um caractere não assinado usa o bit reservado para o sinal de um caractere regular como outro número. Isso altera o intervalo para [0 - 255] em oposição a [-128 - 127].

Geralmente, caracteres não assinados são usados ​​quando você não deseja um sinal. Isso fará a diferença ao fazer coisas como bits de deslocamento (shift estende o sinal) e outras coisas ao lidar com um char como um byte, em vez de usá-lo como um número.


fonte
4

unsigned characeita apenas valores positivos: 0 a 255, enquanto signed characeita valores positivos e negativos: -128 a +127.

NL628
fonte
3

citado do livro "the c programming laugage":

O qualificador signedou unsignedpode ser aplicado a char ou a qualquer número inteiro. números não assinados são sempre positivos ou zero e obedecem às leis do módulo aritmético 2 ^ n, em que n é o número de bits no tipo. Assim, por exemplo, se caracteres são 8 bits, variáveis ​​de caracteres não assinadas têm valores entre 0 e 255, enquanto caracteres assinados têm valores entre -128 e 127 (em uma máquina de complemento de dois). Se caracteres simples são assinados ou não é uma máquina independentes, mas os caracteres imprimíveis são sempre positivos.

ZhaoGang
fonte
2

signed chare unsigned charambos representam 1 byte, mas eles têm intervalos diferentes.

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

Em signed charse considerarmos char letter = 'A', 'A' é representar binário de 65 em ASCII/Unicode, Se 65 pode ser armazenado, -65 também podem ser armazenados. Não há valores binários negativos ASCII/Unicode, pois não há necessidade de se preocupar com valores negativos.

Exemplo

#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;
}

Resultado -:

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

Signed char(-128) : -128
Unsigned char(-128) : 128
Kalana
fonte