real - tipos de variables en c++ y sus rangos
¿Debería un buffer de bytes estar firmado o sin signo? (14)
¿Debería un búfer de bytes estar firmado o sin firmar o simplemente un búfer de carácter? ¿Alguna diferencia entre C y C ++?
Gracias.
¿Debería un búfer de bytes estar firmado o sin firmar o simplemente un búfer de carácter? ¿Alguna diferencia entre C y C ++?
Una pequeña diferencia en cómo la lengua lo trata. Una gran diferencia en cómo la convención lo trata.
-
char
= ASCII (o UTF-8, pero la firmeza se interpone en el camino) datos textuales -
unsigned char
= byte -
signed char
= usado raramente
Y hay un código que se basa en tal distinción. Hace solo una o dos semanas encontré un error por el cual los datos JPEG se estaban corrompiendo porque se pasaban a la versión char*
de nuestra función de codificación Base64, que "útilmente" reemplazó a todos los UTF-8 no válidos en la "cadena". El cambio a BYTE
aka unsigned char
fue todo lo que se necesitó para solucionarlo.
¿En serio te importa? Si no lo hace, solo use el valor predeterminado (char) y no desordene su código con asuntos sin importancia. De lo contrario, los futuros mantenedores se preguntarán por qué usó firmado (o sin firmar). Haz su vida más sencilla.
Debería y debería ... Tiendo a preferir que no esté firmado, ya que se siente más "en bruto", menos invitador a decir "hey, eso es solo un montón de pequeñas ints
", si quiero enfatizar el carácter binario de los datos.
No creo que haya usado un carácter explícito signed char
para representar un búfer de bytes.
Por supuesto, una tercera opción es representar el búfer como void *
tanto como sea posible. Muchas funciones comunes de E / S funcionan con void *
, por lo que a veces la decisión de qué tipo de entero usar debe ser completamente encapsulada, lo que es bueno.
Debes usar char o unsigned char pero nunca firmó char . El estándar tiene lo siguiente en 3.9 / 2
Para cualquier objeto (que no sea un subobjeto de clase base) de POD tipo T, ya sea que el objeto tenga un valor válido de tipo T, los bytes subyacentes (1.7) que forman el objeto se pueden copiar en una matriz de caracteres o sin signo char. Si el contenido de la matriz de char o unsigned char se copia nuevamente en el objeto, el objeto mantendrá su valor original.
Depende.
Si se pretende que el búfer contenga texto, probablemente tenga sentido declararlo como una matriz de caracteres y dejar que la plataforma decida por usted si eso está firmado o sin firmar de forma predeterminada. Eso le dará la menor dificultad para pasar los datos dentro y fuera de la biblioteca de tiempo de ejecución de la implementación, por ejemplo.
Si el búfer está destinado a contener datos binarios, entonces depende de cómo intente usarlo. Por ejemplo, si los datos binarios son en realidad una matriz de muestras de datos que están firmadas con medidas de ADC de punto fijo de 8 bits, entonces lo mejor sería un signed char
.
En la mayoría de los casos del mundo real, el búfer es solo eso, un búfer, y realmente no le importan los tipos de bytes individuales porque llenó el búfer en una operación masiva, y está a punto de pasarlo a un analizador para interpretar la compleja estructura de datos y hacer algo útil. En ese caso, declararlo de la forma más sencilla.
Es mejor definirlo como sin signo. Infact Win32 tipo BYTE se define como sin signo. No hay diferencia entre C y C ++ entre esto.
Hace varios años tuve un problema con una aplicación de consola C ++ que imprimía caracteres de color para valores ASCII por encima de 128 y esto se resolvió cambiando de char a uns sin signo, pero creo que también se pudo solventar manteniendo el tipo de char.
Por ahora, la mayoría de las funciones de C / C ++ usan char y entiendo ambos idiomas mucho mejor ahora, así que uso char en la mayoría de los casos.
La elección de int8_t vs uint8_t es similar a cuando se compara una ptr para ser NULL.
Desde el punto de vista de la funcionalidad, comparar con NULL es lo mismo que comparar con 0 porque NULL es un #define para 0.
Pero personalmente, desde el punto de vista del estilo de codificación, elijo comparar mis punteros a NULL porque el NULL #define se refiere a la persona que mantiene el código en el que está comprobando un puntero defectuoso ...
VS
cuando alguien ve una comparación a 0, esto implica que está comprobando un valor específico.
Por la razón anterior, usaría uint8_t.
Para una máxima portabilidad, utilice siempre caracteres sin signo. Hay un par de casos en que esto podría entrar en juego. Los datos serializados compartidos entre sistemas con diferentes tipos de endian vienen inmediatamente a la mente. Cuando se realiza el desplazamiento o el enmascaramiento de bits, los valores son otra.
Si le mientes al compilador, te castigará.
Si el búfer contiene datos que están pasando, y no los manipulará de ninguna manera, no importa.
Sin embargo, si tiene que operar en el contenido del búfer, la declaración de tipo correcta hará que su código sea más simple. No "int val = buf [i] & 0xff;" disparates.
Entonces, piense en lo que realmente son los datos y cómo necesita usarlos.
Si obtiene un elemento en una variable más amplia, por supuesto se extenderá por signos o no.
Si realmente es un búfer de bytes de 8 bits, en lugar de una cadena en la configuración regional predeterminada de la máquina, entonces usaría uint8_t
. No es que haya muchas máquinas en torno a las cuales un char no sea un byte (o un byte un octeto), pero hacer la declaración "esto es un búfer de octetos" en lugar de "esto es una cadena" es a menudo una documentación útil.
Si tiene la intención de almacenar datos binarios arbitrarios, debe utilizar caracteres unsigned char
. Es el único tipo de datos que se garantiza que no tiene bits de relleno por el C Standard. Cada otro tipo de datos puede contener bits de relleno en su representación de objeto (es decir, el que contiene todos los bits de un objeto, en lugar de solo aquellos que determinan un valor). El estado de los bits de relleno no está especificado y no se utiliza para almacenar valores. Por lo tanto, si lee el uso de algunos datos binarios con char
, las cosas se reducirán al rango de valores de un char (interpretando solo los bits de valor), pero es posible que haya bits que simplemente se ignoren, pero que todavía estén allí y sean leídos por memcpy
. Al igual que el relleno de bits en objetos de estructura real. El tipo unsigned char
está garantizado para no contenerlos. Eso sigue de 5.2.4.2.1/2
(C99 TC2, n1124 aquí):
Si el valor de un objeto de tipo char se trata como un entero con signo cuando se usa en una expresión, el valor de
CHAR_MIN
será el mismo que el deSCHAR_MIN
y el valor deCHAR_MAX
será el mismo que el deSCHAR_MAX
. De lo contrario, el valor deCHAR_MIN
será 0 y el valor deCHAR_MAX
será el mismo que el deUCHAR_MAX
. El valorUCHAR_MAX
será igual a2^CHAR_BIT − 1
De la última oración se deduce que no queda espacio para los bits de relleno. Si usa char
como el tipo de su búfer, también tiene el problema de los desbordamientos: Asignar cualquier valor explícitamente a uno de esos elementos que está en el rango de 8
bits, por lo que puede esperar que dicha asignación sea correcta, pero no dentro del rango de 8
bits. El rango de un char
, que es CHAR_MIN
.. CHAR_MAX
, tal conversión desborda y causa resultados definidos de implementación, incluyendo el aumento de señales.
Incluso si algún problema relacionado con lo anterior probablemente no se muestre en implementaciones reales (sería una calidad de implementación muy baja), es mejor usar el tipo correcto desde el principio en adelante, que es el unsigned char
.
Sin embargo, para las cadenas, el tipo de datos de elección es char
, que se entenderá mediante las funciones de cadena e impresión. Usar un signed char
para estos propósitos me parece una decisión equivocada.
Para obtener más información, lea this proposal
que contiene una solución para una próxima versión del Estándar C que, eventualmente, requerirá que los caracteres signed char
no tengan ningún bit de relleno. Ya está incorporado en el documento de trabajo .
typedef char byte;
Ahora puedes hacer que tu array sea de byte
s. Es obvio para todos lo que querías decir, y no pierdes ninguna funcionalidad.
Sé que es un poco tonto, pero hace que tu código se lea al 100% como pretendías.