O que significa para um caractere para ser assinado?
-
19-08-2019 - |
Pergunta
Dado que assinados e ints usar o mesmo registra, etc., e apenas interpretar padrões de bits de forma diferente, e C caracteres são, basicamente, apenas 8-bit de ints, qual é a diferença entre assinados e caracteres em C?Eu entendo que o sinal de char é a aplicação definida, e eu simplesmente não consigo entender como ele poderia fazer a diferença, pelo menos quando o char é utilizado para armazenar seqüências de caracteres em vez de fazer matemática.
Solução
Não fará diferença para as cordas. Mas em C você pode usar um char para fazer matemática, quando ele fará a diferença.
De fato, ao trabalhar em ambientes de memória restritos, como aplicativos incorporados de 8 bits, um char geralmente será usado para fazer matemática e, em seguida, faz uma grande diferença. Isso é porque não há byte
Digite por padrão em C.
Outras dicas
Em termos dos valores que eles representam:
caracter não identifcado:
- abrange a faixa de valor
0..255 (00000000..11111111)
Os valores transbordam em torno da borda baixa como:
0 - 1 = 255 (00000000 - 00000001 = 11111111)
Os valores transbordam em torno da borda alta como:
255 + 1 = 0 (11111111 + 00000001 = 00000000)
Operador de mudança direita bit -bit -in (
>>
) faz uma mudança lógica:10000000 >> 1 = 01000000 (128 / 2 = 64)
Char assinado:
- abrange a faixa de valor
-128..127 (10000000..01111111)
Os valores transbordam em torno da borda baixa como:
-128 - 1 = 127 (10000000 - 00000001 = 01111111)
Os valores transbordam em torno da borda alta como:
127 + 1 = -128 (01111111 + 00000001 = 10000000)
Operador de mudança direita bit -bit -in (
>>
) faz uma mudança aritmética:10000000 >> 1 = 11000000 (-128 / 2 = -64)
Incluí as representações binárias para mostrar que o comportamento de embalagem de valor é aritmético binário puro e consistente e não tem nada a ver com um char sendo assinado/não assinado (espere para turnos certos).
Atualizar
Algum comportamento específico da implementação mencionado nos comentários:
- char! = char assinado. O tipo "char" sem "assinado" ou "sem riscos" é definido pela implementação, o que significa que ele pode agir como um tipo assinado ou não assinado.
- O transbordamento inteiro assinado leva a um comportamento indefinido, onde um programa pode fazer qualquer coisa, incluindo dumping core ou invadir um buffer.
#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
É importante ao classificar cordas.
Há algumas diferenças. Mais importante, se você transbordar o intervalo válido de um char atribuindo -lhe um número inteiro muito grande ou pequeno, e o char é assinado, o valor resultante é a implementação definida ou mesmo algum sinal (em c) pode ser aumentado, pois para todos os tipos assinados . Contraste isso com o caso em que você atribui algo muito grande ou pequeno a um char não assinado: o valor envolve, você terá uma semântica definida com precisão. Por exemplo, atribuindo um -1 a um char não assinado, você receberá um uchar_max. Portanto, sempre que você tem um byte como em um número de 0 a 2^char_bit, você realmente deve usar char não assinado para armazená -lo.
O sinal também faz a diferença ao passar para as funções vararg:
char c = getSomeCharacter(); // returns 0..255
printf("%d\n", c);
Suponha que o valor atribuído a C seria grande demais para o CHAR representar, e a máquina usa o complemento de dois. Muitas implementação se comportam para o caso de atribuir um valor muito grande ao char, pois o padrão de bits não mudará. Se um INT puder representar todos os valores de char (que é para a maioria das implementações), o char está sendo promovido a INT antes de passar para o PrintF. Portanto, o valor do que é passado seria negativo. Promover para o INT manteria esse sinal. Então você obterá um resultado negativo. No entanto, se o char não for assinado, o valor não será assinado e a promoção de um int produzirá um Int positivo. Você pode usar char não assinado e, em seguida, terá um comportamento definido com precisão para a atribuição para a variável e a passagem para o PrintF, que imprimirá algo positivo.
Observe que um char de char, não assinado e assinado, todos são pelo menos 8 bits de largura. Não há exigência de que o char seja exatamente 8 bits de largura. No entanto, para a maioria dos sistemas, isso é verdade, mas para alguns, você descobrirá que eles usam chars de 32 bits. Um byte em C e C ++ é definido para ter o tamanho de char; portanto, um byte em C também nem sempre é exatamente 8 bits.
Outra diferença é que, em C, um char não assinado não deve ter bits de preenchimento. Ou seja, se você encontrar char_bit é 8, os valores de um char não assinado devem variar de 0 .. 2^char_bit-1. O mesmo vale para o char se não for assinado. Para o char assinado, você não pode assumir nada sobre o intervalo de valores, mesmo que saiba como seu compilador implementa o material do sinal (o complemento de dois ou as outras opções), pode haver bits de preenchimento não utilizado. Em C ++, não há bits de preenchimento para todos os três tipos de caracteres.
"O que isso significa para um char ser assinado?"
Tradicionalmente, o conjunto de caracteres ASCII consiste em codificações de personagens de 7 bits. (Em oposição ao ebcidic de 8 bits.)
Quando o idioma C foi projetado e implementado, esse foi um problema significativo. (Por vários motivos, como a transmissão de dados em relação aos dispositivos de modem serial.) O bit extra tem usos como paridade.
Um "personagem assinado" é perfeito para essa representação.
Os dados binários, OTOH, estão simplesmente assumindo o valor de cada "pedaço" de dados de 8 bits, portanto, nenhum sinal é necessário.
Aritmética em bytes é importante para gráficos de computador (onde 8 bits valores são muitas vezes utilizados para armazenar cores).Além de que, eu posso pensar em dois casos principais, onde char sinal de questões:
- a conversão para uma maior int
- funções de comparação
A coisa desagradável é que estes não mordê-lo se todos os seus dados de seqüência de caracteres de 7 bits.No entanto, ele promete ser uma inesgotável fonte de obscurecer bugs se você está tentando fazer o seu programa C/C++ 8-bits limpo.
A assinatura funciona praticamente da mesma maneira char
s como acontece em outros tipos integrais. Como você observou, os chars são realmente apenas um byte inteiros. (Não necessariamente de 8 bits, no entanto! Há uma diferença; um byte pode ser maior que 8 bits em algumas plataformas e char
s estão bastante ligados a bytes devido às definições de char
e sizeof(char)
. o CHAR_BIT
macro, definido em <limits.h>
ou C ++ 's <climits>
, dirá quantos bits estão em um char
.).
Quanto ao motivo pelo qual você deseja um personagem com um sinal: em C e C ++, não há tipo padrão chamado byte
. Para o compilador, char
S são bytes e vice -versa, e isso não distingue entre eles. Às vezes, porém, você quer - às vezes você querer este char
Para ser um número de um byte e nesses casos (particularmente quão pequeno pode ter um byte), você também normalmente se importa se o número é assinado ou não. Eu pessoalmente usei a assinatura (ou não assinatura) para dizer que um certo char
é um "byte" numérico) e não um personagem, e que será usado numericamente. Sem uma assinatura especificada, que char
Realmente é um personagem e deve ser usado como texto.
Eu costumava fazer isso, sim. Agora as versões mais recentes de C e C ++ têm (u?)int_least8_t
(atualmente digitado em <stdint.h>
ou <cstdint>
), que são mais explicitamente numéricos (embora normalmente sejam apenas digitados para assinados e não assinados char
tipos de qualquer maneira).
A única situação em que posso imaginar que isso é um problema é se você optar por fazer matemática em chars. É perfeitamente legal escrever o seguinte código.
char a = (char)42;
char b = (char)120;
char c = a + b;
Dependendo da assinatura do char, C pode ser um dos dois valores. Se os chars não forem assinados, C será (char) 162. Se eles forem assinados, ele será um caso de transbordamento, pois o valor máximo para um char assinado é 128. Acho que a maioria das implementações retornaria (char) -32.
Uma coisa sobre chars assinados é que você pode testar C> = '' (espaço) e verifique se é um char ASCII imprimível normal. Claro, não é portátil, então não é muito útil.