Los peores efectos secundarios de la firma de Chars. (Explicación de los efectos de firmeza en caracteres y moldes)

StackOverflow https://stackoverflow.com/questions/2192880

Pregunta

Con frecuencia trabajo con bibliotecas que usan Char cuando trabajo con bytes en C ++. La alternativa es definir un "byte" como char sin firmar, pero que no es el estándar que decidieron usar. Con frecuencia paso bytes de C# a las DLL C ++ y los lijo a Char para trabajar con la biblioteca.

Al lanzar ints a Chars o Chars a otros tipos simples, ¿cuáles son algunos de los efectos secundarios que pueden ocurrir? Específicamente, ¿cuándo tiene este código roto en el que ha trabajado y cómo descubrió que fue debido a la firma de Char?

Por suerte, no me he encontrado con esto en mi código, he usado un truco de casting firmado en una clase de sistemas integrados en la escuela. Estoy buscando comprender mejor el problema, ya que siento que es relevante para el trabajo que estoy haciendo.

¿Fue útil?

Solución

Un riesgo importante es si necesita cambiar los bytes. Un carbón firmado mantiene el letrero cuando se desplaza a la derecha, mientras que un carbón sin firmar no lo hace. Aquí hay un pequeño programa de prueba:

#include <stdio.h>

int main (void)
{
    signed char a = -1;
    unsigned char b = 255;

    printf("%d\n%d\n", a >> 1, b >> 1);

    return 0;
}

Debe imprimir -1 y 127, a pesar de que A y B comienzan con el mismo patrón de bits (dados caracteres de 8 bits, dos planos de complemento y firmados utilizando el desplazamiento aritmético).

En resumen, no puede confiar en que el turno funcione de manera idéntica para caracteres firmados y sin firmar, por lo que si necesita portabilidad, use unsigned char más bien que char o signed char.

Otros consejos

Los gotchas más obvios vienen cuando necesitas comparar el valor numérico de un char con una constante hexadecimal al implementar protocolos o esquemas de codificación.

Por ejemplo, al implementar Telnet, es posible que desee hacer esto.

// Check for IAC (hex FF) byte
if (ch == 0xFF)
{
    // ...

O cuando se prueban secuencias de múltiples bytes UTF-8.

if (ch >= 0x80)
{
    // ...

Afortunadamente, estos errores generalmente no sobreviven mucho tiempo como las pruebas más superficiales en una plataforma con una firmada char debería revelarlos. Se pueden solucionar usando una constante de carácter, convirtiendo la constante numérica en un char o convertir el personaje a un unsigned char antes de que el operador de comparación promueva ambos a un int. Convirtiendo el char directamente a un unsigned Sin embargo, no funcionará.

if (ch == '\xff')               // OK

if ((unsigned char)ch == 0xff)  // OK, so long as char has 8-bits

if (ch == (char)0xff)           // Usually OK, relies on implementation defined behaviour

if ((unsigned)ch == 0xff)       // still wrong

He sido mordido por Char Firmedness en la escritura de algoritmos de búsqueda que usaban caracteres del texto como índices en árboles de estado. También he tenido problemas al expandir los caracteres en tipos más grandes, y el bit de signo se propaga causando problemas en otros lugares.

Descubrí cuando comencé a obtener resultados extraños, y los segfaultes derivados de la búsqueda de textos que no sean los que he usado durante el desarrollo inicial (obviamente los caracteres con valores> 127 o <0 van a causar esto, y no necesariamente lo serán Presente en sus archivos de texto típicos.

Siempre verifique la firma de una variable cuando trabaje con ella. En general, ahora hago firmados tipos a menos que tenga una buena razón de otra manera, lanzar cuando sea necesario. Esto encaja muy bien con el uso ubicuo de char en bibliotecas para simplemente representar un byte. Tenga en cuenta que la firma de char no está definido (a diferencia de otros tipos), debe darle un tratamiento especial y tener en cuenta.

El que más me molesta:

typedef char byte;

byte b = 12;

cout << b << endl;

Claro que es cosmético, pero arrr ...

Al lanzar ints a Chars o Chars a otros tipos simples

El punto crítico es que lanzar un valor firmado de un tipo primitivo a otro tipo (más grande) no retiene el patrón de bits (suponiendo el complemento de dos). Un char con patrón de bits firmado 0xff es -1, mientras que un corto firmado con el valor decimal -1 es 0xffff. Lanzar un carbón sin firmar con valor 0xff a un corto sin firmar, sin embargo, produce 0x00ff. Por lo tanto, piense siempre en la firma adecuada antes de que se encienda a un tipo de datos más grande o más pequeño. Nunca lleves datos sin firmar en los tipos de datos firmados si no necesita - Si una biblioteca externa lo obliga a hacerlo, haga la conversión lo más tarde posible (o lo antes posible si el código externo actúa como fuente de datos).

Las especificaciones del lenguaje C y C ++ definen 3 tipos de datos para mantener caracteres: char, signed char y unsigned char. Los últimos 2 han sido discutidos en otras respuestas. Veamos el char escribe.

Los estándares dicen que el char tipo de datos mayo estar firmado o sin firmar y es una decisión de implementación. Esto significa que algunos compiladores o versiones de compiladores pueden implementar char diferentemente. La implicación es que el char El tipo de datos no es propicio para las operaciones aritméticas o booleanas. Para operaciones aritméticas y booleanas, signed y unsigned versiones de char funcionará bien.

En resumen, hay 3 versiones de char tipo de datos. los char El tipo de datos funciona bien para tener caracteres, pero no es adecuado para la aritmética en todas las plataformas y traductores, ya que es firme ¿Está definida la implementación?

Fallará miserablemente al compilar múltiples plataformas porque el estándar C ++ no define char ser de cierta "firma".

Por lo tanto, el GCC presenta -fsigned-char y -funsigned-char opciones para forzar cierto comportamiento. Se puede encontrar más sobre ese tema aquí, por ejemplo.

EDITAR:

Como solicitó ejemplos de código roto, hay muchas posibilidades para romper el código que procesa los datos binarios. Por ejemplo, la imagen procese muestras de audio de 8 bits (rango -128 a 127) y desea halavar el volumen. Ahora imagine este escenario (en el que asume el programador ingenuo char == signed char):

char sampleIn;

// If the sample is -1 (= almost silent), and the compiler treats char as unsigned,
// then the value of 'sampleIn' will be 255
read_one_byte_sample(&sampleIn);

// Ok, halven the volume. The value will be 127!
char sampleOut = sampleOut / 2;

// And write the processed sample to the output file, for example.
// (unsigned char)127 has the exact same bit pattern as (signed char)127,
// so this will write a sample with the loudest volume!!
write_one_byte_sample_to_output_file(&sampleOut);

Espero que les guste ese ejemplo ;-) Pero para ser honesto, nunca he encontrado tales problemas, ni siquiera como principiante hasta donde puedo recordar ...

Espero que esta respuesta sea suficiente para usted. ¿Qué pasa con un comentario breve?

Extensión de signo. La primera versión de mi función de codificación de URL produjo cadenas como "%ffffffa3".

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top