tag c string character-encoding char signed

tag - ¿Qué significa para un char ser firmado?



title tag html (9)

"¿Qué significa que un carbonero sea firmado?"

Tradicionalmente, el juego de caracteres ASCII consiste en codificaciones de caracteres de 7 bits. (A diferencia del EBCIDIC de 8 bits).

Cuando se diseñó e implementó el lenguaje C, este fue un problema importante. (Por varias razones, como la transmisión de datos a través de dispositivos de módem en serie). El bit adicional tiene usos como la paridad.

Un "personaje firmado" resulta perfecto para esta representación.

Los datos binarios, OTOH, simplemente toman el valor de cada "fragmento" de datos de 8 bits, por lo tanto, no se necesita señal.

Dado que las entradas firmadas y sin firmar utilizan los mismos registros, etc., e interpretan los patrones de bits de manera diferente, y los caracteres C son básicamente solo 8 bits, ¿cuál es la diferencia entre los caracteres firmados y sin signo en C? Entiendo que la sigla de char está definida por la implementación, y simplemente no puedo entender cómo podría marcar una diferencia, al menos cuando se usa char para mantener cadenas en lugar de hacer operaciones matemáticas.


Hay un par de diferencias. Lo que es más importante, si desborda el rango válido de un char al asignarle un entero demasiado grande o pequeño, y se firma char, el valor resultante se define en la implementación o incluso se puede aumentar alguna señal (en C), como para todos los tipos firmados . Contraste eso con el caso cuando asigna algo demasiado grande o pequeño a un carácter sin signo: el valor se ajusta, obtendrá una semántica definida con precisión. Por ejemplo, asignando un -1 a un char sin signo, obtendrás un UCHAR_MAX. Entonces, cada vez que tenga un byte como en un número de 0 a 2 ^ CHAR_BIT, realmente debería usar char sin signo para almacenarlo.

El signo también hace una diferencia al pasar a funciones vararg:

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

Suponga que el valor asignado a c sería demasiado grande para que lo represente un char, y la máquina usa el complemento de dos. Muchas implementaciones se comportan para el caso en el que se le asigna un valor demasiado grande a la char, ya que el patrón de bits no cambiará. Si un int podrá representar todos los valores de char (que es para la mayoría de las implementaciones), entonces el carácter char será promovido a int antes de pasar a printf. Entonces, el valor de lo que se pasa sería negativo. La promoción a int retendría ese signo. Entonces obtendrás un resultado negativo. Sin embargo, si char no está firmado, entonces el valor no está firmado, y promocionar a un int arrojará un int positivo. Puedes usar char sin signo, obtendrás un comportamiento definido con precisión tanto para la asignación a la variable como para pasar a printf, que luego imprimirá algo positivo.

Tenga en cuenta que un char, unsigned y signed char all tienen al menos 8 bits de ancho. No es necesario que Char tenga exactamente 8 bits de ancho. Sin embargo, para la mayoría de los sistemas eso es cierto, pero para algunos, encontrarás que usan caracteres de 32 bits. Un byte en C y C ++ se define para tener el tamaño de char, por lo que un byte en C tampoco siempre es exactamente 8 bits.

Otra diferencia es que en C, un char sin signo no debe tener bits de relleno. Es decir, si encuentra que CHAR_BIT es 8, entonces los valores de un carácter sin signo deben oscilar entre 0 .. 2 ^ CHAR_BIT-1. Lo mismo es cierto para char si no está firmado. Para char firmado, no puede asumir nada sobre el rango de valores, incluso si sabe cómo su compilador implementa el material de firma (complemento de dos o las otras opciones), puede haber bits de relleno no utilizados en él. En C ++, no hay bits de relleno para los tres tipos de caracteres.


La única situación en la que puedo imaginar que esto sea un problema es si eliges hacer matemáticas con los caracteres. Es perfectamente legal escribir el siguiente código.

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

Dependiendo de la firma de la char, c podría ser uno de dos valores. Si los caracteres no están firmados, c será (caracteres) 162. Si están firmados, será un caso de desbordamiento, ya que el valor máximo para un char firmado es 128. Supongo que la mayoría de las implementaciones simplemente devolverían (char) -32.


La aritmética en bytes es importante para gráficos por computadora (donde los valores de 8 bits se usan a menudo para almacenar colores). Aparte de eso, puedo pensar en dos casos principales en los que el signo del carbón importa:

  • convirtiendo a una int más grande
  • funciones de comparación

Lo desagradable es que estos no te morderán si todos tus datos de cadena son de 7 bits. Sin embargo, promete ser una fuente interminable de errores desconocidos si intentas limpiar tu programa C / C ++ de 8 bits.


Una cosa sobre los caracteres con signo es que puedes probar c> = '''' (espacio) y asegurarte de que es un carácter ascii imprimible normal. Por supuesto, no es portátil, por lo que no es muy útil.


#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

Es importante al ordenar cadenas.


No hará una diferencia para las cadenas. Pero en C puedes usar un carácter para hacer matemáticas, cuando hará la diferencia.

De hecho, cuando se trabaja en entornos de memoria limitados, como las aplicaciones integradas de 8 bits, a menudo se usa una marca para hacer operaciones matemáticas, y luego hace una gran diferencia. Esto se debe a que no hay ningún tipo de byte por defecto en C.


La firma significa que funciona prácticamente de la misma manera en las caricaturas que en otros tipos integrales. Como ya habrás notado, los caracteres son en realidad enteros de un byte. ( No necesariamente de 8 bits , sin embargo, hay una diferencia, un byte puede ser más grande que 8 bits en algunas plataformas, y los caracteres están más atados a bytes debido a las definiciones de char y sizeof(char) . La macro CHAR_BIT , definida en <limits.h> o <climits> C ++, le dirá cuántos bits hay en un char .).

En cuanto a por qué querrías un personaje con un signo: en C y C ++, no hay un tipo estándar llamado byte . Para el compilador, los caracteres son bytes y viceversa, y no distingue entre ellos. A veces, sin embargo, desea hacerlo: a veces desea que ese char sea ​​de un byte y, en esos casos (en particular, qué tan pequeño puede ser un byte), también le preocupará si el número está firmado o no. Personalmente, he usado la firma (o unsignedness) para decir que cierto char es un "byte" (numérico) en lugar de un carácter, y que se usará numéricamente. Sin un signo firmado específico, ese char es realmente un carácter y está destinado a ser utilizado como texto.

Yo solía hacer eso, más bien. Ahora las versiones más nuevas de C y C ++ tienen (u?)int_least8_t (actualmente typedef''d en <stdint.h> o <cstdint> ), que son más explícitamente numéricas (aunque típicamente serán solo typedefs para char firmado y no firmado tipos de todos modos).


En términos de los valores que representan:

char sin signo:

  • abarca el rango de valores 0..255 (00000000..11111111)
  • los valores se desbordan alrededor del borde bajo como:

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

  • los valores se desbordan alrededor del borde alto como:

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

  • el operador de desplazamiento a la derecha bit a bit ( >> ) realiza un cambio lógico:

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

char firmado:

  • abarca el rango de valores -128..127 (10000000..01111111)
  • los valores se desbordan alrededor del borde bajo como:

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

  • los valores se desbordan alrededor del borde alto como:

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

  • el operador de desplazamiento a la derecha bit a bit ( >> ) realiza un desplazamiento aritmético:

    10000000 >> 1 = 11000000 (-128 / 2 = -64)

Incluí las representaciones binarias para mostrar que el comportamiento de ajuste del valor es una aritmética binaria pura y consistente, y no tiene nada que ver con que un carácter esté firmado / no firmado (se esperan cambios a la derecha).

Actualizar

Algunos comportamientos específicos de implementación mencionados en los comentarios: