¿Qué es un personaje sin firmar?

479

En C / C ++, ¿para qué unsigned charse utiliza? ¿Cómo es diferente de un regular char?

Landon Kuhn
fuente

Respuestas:

548

En C ++, hay tres tipos de caracteres distintos :

  • char
  • signed char
  • unsigned char

Si está utilizando tipos de caracteres para el texto , use el no calificado char:

  • es el tipo de literales de caracteres como 'a'o '0'.
  • es el tipo que compone las cadenas C como "abcde"

También funciona como un valor numérico, pero no se especifica si ese valor se trata como con signo o sin signo. Tenga cuidado con las comparaciones de caracteres a través de las desigualdades, aunque si se limita a ASCII (0-127) está a salvo.

Si está utilizando tipos de caracteres como números , use:

  • signed char, que le da al menos el rango de -127 a 127. (-128 a 127 es común)
  • unsigned char, que le da al menos el rango de 0 a 255.

"Al menos", porque el estándar C ++ solo proporciona el rango mínimo de valores que cada tipo numérico debe cubrir. sizeof (char)se requiere que sea 1 (es decir, un byte), pero un byte podría ser, en teoría, por ejemplo, 32 bits. sizeofseguiría informando su tamaño como1 , lo que significa que podría tener sizeof (char) == sizeof (long) == 1.

Fruny
fuente
44
Para ser claros, ¿podría tener caracteres de 32 bits y enteros de 32 bits y tener sizeof (int)! = Sizeof (char)? Sé que el estándar dice sizeof (char) == 1, pero ¿el tamaño relativo de (int) se basa en la diferencia real de tamaño o la diferencia de rango?
Joseph Garvin el
14
+1. Pero hay cuatro tipos de caracteres distintos en C ++, wchar_t es uno de ellos.
Eric Z
11
desde c ++ 11 tiene 6 tipos distintos: char, char firmado, unsigned char, wchar_t, char16_t, char32_t.
marcinj
12
@unheilig Es común colocar un espacio después sizeofporque no es una función sino un operador. Es incluso mejor estilo omitir el paréntesis cuando se toma el tamaño de una variable. sizeof *po sizeof (int). Esto deja en claro rápidamente si se aplica a un tipo o variable. Del mismo modo, también es redundante poner paréntesis después return. No es una función.
Patrick Schlüter
3
" char: es el tipo de literales de caracteres como 'a'o '0'". es cierto en C ++ pero no en C. En C, 'a'es un int.
chux - Restablece a Monica
92

Esto depende de la implementación, ya que el estándar C NO define la firma de char. Dependiendo de la plataforma, char puede ser signedo unsigned, por lo que debe solicitar explícitamente signed charo unsigned charsi su implementación depende de ello. Solo úselo charsi tiene la intención de representar caracteres de cadenas, ya que esto coincidirá con lo que su plataforma pone en la cadena.

La diferencia entre signed chary unsigned chares como era de esperar. En la mayoría de las plataformas, signed charhabrá un número de complemento de dos de 8 bits que va de -128a 127, y unsigned charserá un entero sin signo de 8 bits ( 0a 255). Tenga en cuenta que el estándar NO requiere que los chartipos tengan 8 bits, solo ese sizeof(char)retorno 1. Puede obtener el número de bits en un char con CHAR_BITin limits.h. Sin 8embargo, hay pocas plataformas, si es que hay alguna, en las que esto sea algo diferente .

Hay un buen resumen de este problema aquí .

Como otros han mencionado desde que publiqué esto, es mejor que uses int8_ty uint8_tsi realmente quieres representar números enteros pequeños.

Todd Gamblin
fuente
2
los caracteres firmados solo tienen un rango mínimo de -127 a 127, no de -128 a 127
12431234123412341234123
3
@ 12431234123412341234123: Técnicamente cierto, en que el estándar C define -127 a 127 como el rango mínimo. Sin embargo, te desafío a que encuentres una plataforma que no use la aritmética del complemento a dos. En casi todas las plataformas modernas, el rango real de caracteres firmados será -128 a 127.
Todd Gamblin
CHAR_BITse requiere que tenga al menos 8 bits según el estándar.
martinkunev
39

Debido a que siento que es realmente necesario, solo quiero establecer algunas reglas de C y C ++ (son las mismas en este sentido). Primero, todos los bits de unsigned charparticipan en la determinación del valor de cualquier objeto char sin firmar. En segundo lugar, unsigned charse declara explícitamente sin firmar.

Ahora, tuve una discusión con alguien sobre lo que sucede cuando convierte el valor -1de tipo int a unsigned char. Rechazó la idea de que el resultado unsigned chartiene todos sus bits establecidos en 1, porque estaba preocupado por la representación de signos. Pero no tiene que hacerlo. Inmediatamente después de esta regla, la conversión hace lo que se pretende:

Si el nuevo tipo no tiene signo, el valor se convierte agregando o restando repetidamente uno más que el valor máximo que se puede representar en el nuevo tipo hasta que el valor esté en el rango del nuevo tipo. ( 6.3.1.3p2en un borrador C99)

Esa es una descripción matemática. C ++ lo describe en términos de cálculo de módulo, que cede a la misma regla. De todos modos, lo que no está garantizado es que todos los bits en el entero -1sean uno antes de la conversión. Entonces, ¿qué tenemos para poder afirmar que el resultado unsigned chartiene todos sus CHAR_BITbits convertidos en 1?

  1. Todos los bits participan en la determinación de su valor, es decir, no se producen bits de relleno en el objeto.
  2. La adición de una sola vez UCHAR_MAX+1a -1rendirá un valor en el rango, a saber,UCHAR_MAX

Eso es suficiente, en realidad! Así que cada vez que quieras tener uno unsigned charcon todos sus bits, lo haces

unsigned char c = (unsigned char)-1;

También se deduce que una conversión no es solo truncar bits de orden superior. El evento afortunado para el complemento de dos es que es solo un truncamiento allí, pero lo mismo no es necesariamente cierto para otras representaciones de signos.

Johannes Schaub - litb
fuente
2
¿Por qué no solo usar UCHAR_MAX?
Nicolás
1
Porque (unsigned type)-1es una especie de idioma. ~0no lo es
Patrick Schlüter
1
Si tengo algo como esto int x = 1234y char *y = &x. Representación binaria de 1234 is 00000000 00000000 00000100 11010010. Mi máquina es little endian, por lo que la invierte y la almacena en memoria. 11010010 00000100 00000000 00000000LSB es lo primero. Ahora parte principal. Si uso printf("%d" , *p). printfleerá el primer byte 11010010solo el resultado es -46pero 11010010es 210así, ¿por qué se imprime -46? Estoy realmente confundido, supongo que una promoción de char a entero está haciendo algo, pero no lo sé.
Suraj Jain
27

Como, por ejemplo, usos de caracteres sin firmar :

unsigned charA menudo se usa en gráficos de computadora, que muy a menudo (aunque no siempre) asigna un solo byte a cada componente de color. Es común ver un color RGB (o RGBA) representado como 24 (o 32) bits, cada uno unsigned char. Como los unsigned charvalores caen en el rango [0,255], los valores generalmente se interpretan como:

  • 0 significa una falta total de un componente de color dado.
  • 255 significa el 100% de un pigmento de color dado.

Entonces terminaría con rojo RGB como (255,0,0) -> (100% rojo, 0% verde, 0% azul).

¿Por qué no usar a signed char? El cambio aritmético y de bits se vuelve problemático. Como ya se explicó, signed charel rango de a se desplaza esencialmente por -128. Un método muy simple e ingenuo (en su mayoría no utilizado) para convertir RGB a escala de grises es promediar los tres componentes de color, pero esto tiene problemas cuando los valores de los componentes de color son negativos. El rojo (255, 0, 0) promedia a (85, 85, 85) cuando se usa la unsigned chararitmética. Sin embargo, si los valores fueran signed chars (127, -128, -128), terminaríamos con (-99, -99, -99), que sería (29, 29, 29) en nuestro unsigned charespacio, lo cual es incorrecto .

Zachary Garrett
fuente
13

Si desea utilizar un carácter como un entero pequeño, la forma más segura de hacerlo es con los tipos int8_ty uint8_t.

jbleners
fuente
2
No es una buena idea: int8_ty uint8_tson opcionales y no están definidos en arquitecturas donde el tamaño del byte no es exactamente de 8 bits. Por el contrario, signed chary unsigned charsiempre están disponibles y garantizados para contener al menos 8 bits. Puede ser una forma común pero no la más segura .
chqrlie
2
Este es un comentario, no responde la pregunta.
Lundin
@chqrlie Entonces, ¿quiere decir que la forma más segura de representar un número entero pequeño, si desea ahorrar memoria, es mantenerse con signed chary unsigned char? ¿O recomendaría una mejor alternativa "más segura" en ese caso particular? Por ejemplo, para quedarse con los tipos enteros "reales" signed inty en su unsigned intlugar por alguna razón?
RobertS apoya a Monica Cellio el
@ RobertS-ReinstateMonica: usar signed chary unsigned chares portátil para todas las implementaciones conformes y ahorrará espacio de almacenamiento, pero puede causar un aumento en el tamaño del código. En algunos casos, se ahorraría más espacio de almacenamiento al almacenar valores pequeños en campos de bits o bits individuales de tipos enteros regulares. No hay una respuesta absoluta a esta pregunta, la pertinencia de este enfoque depende del caso específico en cuestión. Y esta respuesta no aborda la pregunta de todos modos.
chqrlie
10

unsigned charsolo toma valores positivos ... como 0 a 255

donde como

signed chartoma valores positivos y negativos ... como -128 a +127

munna
fuente
9

chary unsigned charno se garantiza que sean de 8 bits en todas las plataformas; se garantiza que serán de 8 bits o más. Algunas plataformas tienen bytes de 9 bits, 32 bits o 64 bits . Sin embargo, las plataformas más comunes en la actualidad (Windows, Mac, Linux x86, etc.) tienen bytes de 8 bits.

bk1e
fuente
8

signed chartiene un rango de -128 a 127; unsigned chartiene rango de 0 a 255.

char será equivalente a char firmado o no firmado, dependiendo del compilador, pero es un tipo distinto.

Si está utilizando cadenas de estilo C, solo utilícelas char. Si necesita usar caracteres para aritmética (bastante raro), especifique con o sin signo explícitamente para la portabilidad.

James Hopkin
fuente
8

Un unsigned chares un valor de byte sin signo (0 a 255). Puede estar pensando charen términos de ser un "personaje", pero en realidad es un valor numérico. El regular charestá firmado, por lo que tiene 128 valores, y estos valores se asignan a caracteres mediante codificación ASCII. Pero en cualquier caso, lo que está almacenando en la memoria es un valor de byte.

Zac Gochenour
fuente
7

En términos de valores directos, se utiliza un carácter normal cuando se sabe que los valores están entre CHAR_MINy CHAR_MAXmientras que un carácter sin signo proporciona el doble del rango en el extremo positivo. Por ejemplo, si CHAR_BITes 8, el rango de regular charsolo se garantiza que sea [0, 127] (porque puede estar firmado o no) mientras unsigned charque será [0, 255] y signed charserá [-127, 127].

En términos de para qué se utiliza, los estándares permiten que los objetos de POD (datos antiguos simples) se conviertan directamente en una matriz de caracteres sin signo. Esto le permite examinar la representación y los patrones de bits del objeto. La misma garantía de juego seguro no existe para char o char firmado.

Julienne Walker
fuente
En realidad, con mayor frecuencia será [-128, 128].
RastaJedi
Los estándares solo definen formalmente la representación del objeto como una secuencia de unsigned char, no una matriz específicamente, y cualquier "conversión" solo se define formalmente copiando del objeto a una matriz real declarada de unsigned chary luego inspeccionando el último. No está claro si el OR se puede reinterpretar directamente como una matriz de este tipo, con las tolerancias para la aritmética del puntero que implicaría, es decir, si la "secuencia" =="matriz" en este uso. Hay un problema central # 1701 abierto con la esperanza de aclarar esto. Afortunadamente, ya que esta ambigüedad realmente me está molestando recientemente.
underscore_d
1
@RastaJedi No, no lo hará. No puede Un rango de -128 ... + 128 es físicamente imposible de representar con 8 bits. Ese ancho solo admite 2 ^ 8 == 256 valores discretos, pero -128 ... + 128 = 2 * 128 + 1 para 0 = 257. La representación de magnitud de signo permite -127 ... + 127 pero tiene 2 (bipolar) ceros La representación del complemento a dos mantiene un solo cero, pero constituye el rango al tener un valor más en el lado negativo; permite -128 ... + 127. (Y así sucesivamente para ambos en anchos de bits más grandes.)
underscore_d
En mi segundo comentario, es razonable suponer que podemos tomar un puntero al primero unsigned charde la OR y luego continuar usando ++ptrdesde allí para leer cada byte ... pero AFAICT, no se define específicamente como permitido, por lo que estamos Dejó inferir que está "probablemente bien" en muchos otros pasajes (y en muchos sentidos, la mera existencia de memcpy) en el Estándar, similar a un rompecabezas. Lo cual no es ideal. Bueno, tal vez la redacción mejore eventualmente. Aquí está el problema de CWG que mencioné pero que no tenía espacio para vincular: open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
underscore_d
@underscore_d lo siento, fue un error tipográfico. [-128, 127] es lo que quise escribir: p. Sí, sé sobre los ceros dobles (cero 'positivo' y 'negativo') con signo / magnitud. Debo haber estado cansado: p.
RastaJedi
5

unsigned chares el corazón de todos los trucos. En casi TODO el compilador para TODAS las plataformas, un unsigned chares simplemente un byte y un entero sin signo de (generalmente) 8 bits que pueden tratarse como un entero pequeño o un paquete de bits.

En adicción, como alguien más ha dicho, el estándar no define el signo de un personaje. por lo que tiene 3 distintos chartipos: char, signed char, unsigned char.

ugasoft
fuente
1
Bit engaño, también conocido como mordió haciendo girar o está de hecho se sabe que causa la adicción ;-) poco piratería
chqrlie
3
Son los 0 los que causan problemas. Para evitar la adicción a los giros, manténgase alejado de los pedacitos.
DragonLord
5

Si te gusta usar varios tipos de longitud y de signo específico, usted es probablemente mejor con uint8_t, int8_t, uint16_t, etc, simplemente porque hacen exactamente lo que dicen.

Shikari oscuro
fuente
4

Algunos buscadores en Google encontraron esto , donde la gente tuvo una discusión sobre esto.

Un carácter sin signo es básicamente un solo byte. Por lo tanto, usaría esto si necesita un byte de datos (por ejemplo, tal vez quiera usarlo para activar y desactivar indicadores para que se pasen a una función, como se hace a menudo en la API de Windows).

dbrien
fuente
4

Un personaje sin signo utiliza el bit que está reservado para el signo de un personaje normal como otro número. Esto cambia el rango a [0 - 255] en lugar de [-128 - 127].

Generalmente, los caracteres sin signo se usan cuando no desea una señal. Esto marcará una diferencia al hacer cosas como cambiar bits (shift extiende el signo) y otras cosas cuando se trata de un carácter como un byte en lugar de usarlo como un número.


fuente
4

unsigned charsolo toma valores positivos: 0 a 255, mientras que signed chartoma valores positivos y negativos: -128 a +127.

NL628
fuente
3

citado del libro "the c programa laugage":

El calificador signedo unsignedse puede aplicar a char o cualquier número entero. los números sin signo son siempre positivos o cero, y obedecen las leyes del módulo aritmético 2 ^ n, donde n es el número de bits en el tipo. Entonces, por ejemplo, si los caracteres son de 8 bits, las variables de caracteres sin signo tienen valores entre 0 y 255, mientras que los caracteres con signo tienen valores entre -128 y 127 (en una máquina complementaria de dos). Si los caracteres simples son con signo o sin signo es máquina independientes, pero los caracteres imprimibles son siempre positivos.

ZhaoGang
fuente
2

signed chary unsigned charambos representan 1byte, pero tienen diferentes rangos.

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

En signed charsi tenemos en cuenta char letter = 'A', 'A' es representar binaria de 65 en ASCII/Unicode, 65 Si se puede almacenar, -65 también se pueden almacenar. No hay valores binarios negativos ASCII/Unicodeallí para no tener que preocuparse por los valores negativos.

Ejemplo

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

Salida -:

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

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