¿Es mejor usar una matriz de caracteres char o sin firmar para almacenar datos sin procesar?

6 minutos de lectura

avatar de usuario
M310

Cuando necesite almacenar en la memoria algunos datos sin procesar, por ejemplo, de una secuencia, ¿es mejor usar una matriz de char o de unsigned char? yo siempre usaba char pero en el trabajo dicen que es mejor unsigned char y no se porque

  • si es un flujo de cadena, entonces estaría bien usarlo char formación. para otros datos brutos numéricos (por ejemplo, hexadecimales, bits), es mejor usar unsigned variables para que no tenga que lidiar con el bit de signo

    – Iósif Murariu

    12 de junio de 2014 a las 9:43


avatar de usuario
tony delroy

ACTUALIZACIÓN: C++17 introducido std::byteque es más adecuado para los búferes de datos “en bruto” que para usar cualquier tipo de char.

Para versiones anteriores de C++:

  • unsigned char enfatiza que los datos no son “solo” texto

  • si tiene lo que efectivamente son datos de “bytes” de, por ejemplo, un flujo comprimido, un archivo de copia de seguridad de la tabla de la base de datos, una imagen ejecutable, un jpeg … entonces unsigned es apropiado para la connotación de datos binarios mencionada anteriormente

    • unsigned funciona mejor para algunas de las operaciones que podría querer hacer en datos binarios, por ejemplo, hay comportamientos indefinidos y definidos por la implementación para algunas operaciones de bits en tipos firmados, y unsigned los valores se pueden usar directamente como índices en matrices

    • no puedes pasar accidentalmente un unsigned char* a una función esperando char* y operarlo como presunto texto

    • en estas situaciones, por lo general, es más natural pensar que los valores están en el rango de 0 a 255, después de todo, ¿por qué el bit de “signo” debería tener un tipo de significado diferente al de los otros bits en los datos?

  • si está almacenando “datos sin procesar” que, a nivel de lógica/diseño de la aplicación, son datos numéricos de 8 bits, entonces, por supuesto, elija cualquiera unsigned o explícitamente signed char según corresponda a sus necesidades

En lo que respecta a la estructura del búfer, no hay diferencia: en ambos casos se obtiene un tamaño de elemento de un byte, exigido por el estándar.

Quizás la diferencia más importante que obtiene es el comportamiento que ve cuando accede a los elementos individuales del búfer, por ejemplo, para imprimir. Con char obtiene un comportamiento firmado o no firmado definido por la implementación; con unsigned char siempre ves un comportamiento sin firmar. Esto se vuelve importante si desea imprimir los bytes individuales de su búfer de “datos sin procesar”.

Otra buena alternativa para el uso de búferes es el entero de ancho exacto uint8_t. Se garantiza que tiene el mismo ancho que unsigned charsu nombre requiere menos tipeo y le dice al lector que no está diseñado para usar los elementos individuales del búfer como información basada en caracteres.

avatar de usuario
pablo francisco perez hidalgo

Internamente, es exactamente lo mismo: cada elemento es un byte. La diferencia se da cuando se opera con esos valores.

Si su rango de valores es [0,255] Deberías usar unsigned char pero si es [-128,127] entonces deberías usar signed char.

Supongamos que está utilizando el primer rango (signed char), entonces puede realizar la operación 100+100. De lo contrario, esa operación se desbordará y le dará un valor inesperado.

Dependiendo de su compilador o tipo de máquina, char puede estar sin firmar o firmado de forma predeterminada: ¿Char está firmado o sin firmar de forma predeterminada? Así teniendo char los rangos descritos para los casos anteriores.

Si está usando este búfer solo para almacenar datos binarios sin operar con él, no hay diferencia entre usar char o unsigned char.

EDITAR

Tenga en cuenta que incluso puede cambiar el valor predeterminado char para la misma máquina y compilador usando las banderas del compilador:

-funsigned-char Deja que el tipo char no esté firmado, como char sin firmar.

Cada tipo de máquina tiene un valor predeterminado para lo que debe ser char. Es como un carácter sin firmar de forma predeterminada o como un carácter firmado de forma predeterminada. Idealmente, un programa portátil siempre debe usar caracteres firmados o caracteres sin firmar cuando depende de la firma de un objeto. Pero muchos programas han sido escritos para usar caracteres simples y esperan que estén firmados, o esperan que no estén firmados, dependiendo de las máquinas para las que fueron escritos. Esta opción, y su inversa, le permiten hacer que dicho programa funcione con el valor predeterminado opuesto.

El tipo char siempre es un tipo distinto de cada uno de char con signo o sin signo, aunque su comportamiento siempre es como uno de esos dos.

  • Asumes char está firmado. Entonces, las partes de “rango” y “desbordamiento” no son necesariamente ciertas.

    – PP

    12 de junio de 2014 a las 9:51

  • “si esto es [-127,127] usar char.” char también podría no estar firmado, si necesita firmar, use signed char. “… darte un número negativo”. Tal vez, tal vez no, el desbordamiento firmado es UB.

    – Baum mit Augen

    12 de junio de 2014 a las 9:53


  • @BaummitAugen Es cierto, pero en ese caso OP no debería esperar obtener el valor deseado.

    – Pablo Francisco Pérez Hidalgo

    12 de junio de 2014 a las 9:56

Como dijo @Pablo en su respuesta, la razón clave es que si está haciendo aritmética en los bytes, obtendrá las respuestas ‘correctas’ si declara los bytes como unsigned char: quieres (en el ejemplo de Pablo) 100 + 100 para sumar 200; si haces esa suma con signed char (lo que podría hacer por accidente si char en su compilador está firmado) no hay garantía de eso, está buscando problemas.

Otra razón importante es que puede ayudar a documentar su código, si es explícito acerca de qué tipos de datos son qué. Es útil declarar

typedef unsigned char byte

o mejor

#include <stdint.h>
typedef uint8_t byte

Usando byte a partir de entonces hace que quede un poco más claro cuál es la intención de su programa. Dependiendo de cuán paranoico sea su compilador (-Wall es tu amigo), este puede que incluso causar una advertencia de tipo si das un byte* argumento a un char* argumento de la función, lo que le incita a pensar un poco más detenidamente si está haciendo lo correcto.

Un ‘carácter’ es fundamentalmente una cosa bastante diferente de un ‘byte’. C pasa a difuminar la distinción (porque en el nivel de C, en un mundo mayormente ASCII, la distinción no importa en muchos casos). Este desenfoque no siempre es útil, pero al menos es una buena higiene intelectual para mantener la diferencia clara en tu cabeza.

Por lo general, es mejor usar char pero hace tan poca diferencia que no importa. Son datos sin procesar, por lo que simplemente debe pasarlos como tales en lugar de intentar trabajar con ellos a través de char punteros de un tipo u otro. Ya que char es el tipo de datos nativo, tiene más sentido usar esto en lugar de imaginar que está forzando sus datos en un tipo u otro.

avatar de usuario
Comunidad

Si usa caracteres sin firmar, solo tomará caracteres ASCII válidos, ya que su rango será de -127 a +127.

y puede encontrar una diferencia completa entre char y los detalles de char sin firmar en esta pregunta.

diff bet char y char sin firmar

y se puede ver la tabla aquí.

tabla ASCII

tablas completas de caracteres en bruto

avatar de usuario
Antonio

Si puede trabajar con C++17, hay un tipo std::byte que es más apropiado para trabajar con datos sin procesar. Solo tiene definidos operadores lógicos bit a bit.

¿Ha sido útil esta solución?