2009-11-12 24 views
178

¿Cuál es la ventaja de usar uint8_t sobre unsigned char en C?uint8_t vs char sin signo

Sé que en casi todos los sistemas uint8_t es sólo un typedef para unsigned char, ¿por qué se utiliza?

Respuesta

176

Documenta su intención: almacenará números pequeños, en lugar de un carácter.

También se ve mejor si está usando otros typedefs como uint16_t o int32_t.

+1

No estaba claro en la pregunta original si estábamos hablando de un tipo estándar o no. Estoy seguro de que ha habido muchas variaciones de esta convención de nombres a lo largo de los años. –

+0

He solucionado mi respuesta, gracias. –

+5

Explícitamente el uso de documentos 'unsigned char' o' signed char' también es intencional, ya que "char" sin adornos es lo que muestra que estás trabajando con los caracteres. – caf

6

Como dijiste, "casi cada sistema".

char es probablemente uno de los menos propensos a cambiar, pero una vez de empezar a usar uint16_t y amigos, usando uint8_t mezcla mejor, e incluso puede ser parte de un estándar de codificación.

2

En casi todos los sistemas he encontrado uint8_t == char sin signo, pero esto no está garantizado por el estándar C. Si está tratando de escribir código portátil y es exactamente el tamaño de la memoria, use uint8_t. De lo contrario, use char sin signo.

+2

'uint8_t' _always_ coincide con el rango y tamaño de' unsigned char' y relleno (ninguno) cuando 'unsigned char' es de 8 bits. Cuando 'unsigned char' no es de 8 bits,' uint8_t' no existe. – chux

+0

@chux, ¿Tiene alguna referencia al lugar exacto en el estándar donde dice eso? Si 'unsigned char' es de 8 bits, ¿se garantiza' uint8_t' como 'typedef' y no como' typedef' de un _extended entero sin signo type_? – hsivonen

+0

@hsivonen "lugar exacto en el estándar donde dice eso?" -> No - aún mira a 7.20.1.1. Se deduce fácilmente ya que 'unsigned char/signed char/char' es el tipo más pequeño, no más pequeño que 8 bits. 'unsigned char' no tiene relleno. Para 'uint8_t' ser, debe ser de 8 bits, sin relleno, existe debido a una implementación proporcionada tipo entero: que coincida con los requisitos mínimos de' unsigned char'. En cuanto a "... garantizado para ser un typedef ..." parece una buena pregunta para publicar. – chux

52

Para ser pedante, algunos sistemas pueden no tener un tipo de 8 bits. Según Wikipedia: se requiere

Una implementación para definir tipos de enteros exacta de ancho para N = 8, 16, 32, o 64 si, y sólo si tiene cualquier tipo que cumpla los requisitos. No es necesario definirlos para ninguna otra N, incluso si admite los tipos apropiados.

Así que uint8_t no se garantiza que exista, aunque lo hará para todas las plataformas donde 8 bits = 1 byte. Algunas plataformas integradas pueden ser diferentes, pero eso se está volviendo muy raro. Algunos sistemas pueden definir que los tipos char sean 16 bits, en cuyo caso probablemente no habrá un tipo de 8 bits de ningún tipo.

Aparte de la cuestión (menor), @Mark Ransom's answer es la mejor en mi opinión. Usa el que muestra más claramente para qué estás usando los datos.

Además, estoy asumiendo que quería decir uint8_t (typedef la norma de C99 proporcionada en la cabecera stdint.h) en lugar de uint_8 (no parte de ningún estándar).

+1

Los DSP con 'CHAR_BIT> 8' se están volviendo * menos * raros, no más. – caf

+3

@caf, por pura curiosidad, ¿puede vincular a la descripción de algunos? Sé que existen porque alguien mencionó uno (y lo vinculó a documentos de desarrollador) en una discusión moderada de comp.lang.C++ sobre si las garantías de tipo C/C++ son demasiado débiles, pero no puedo encontrar ese hilo nunca más, y siempre es útil para hacer referencia a eso en cualquier discusión similar :) –

+3

"Algunos sistemas pueden definir tipos de caracteres para ser de 16 bits, en cuyo caso probablemente no habrá un tipo de 8 bits de ningún tipo". - y a pesar de algunas objeciones incorrectas de mí, Pavel ha demostrado en su respuesta que si char es de 16 bits, incluso si el compilador proporciona un tipo de 8 bits, * no debe * llamarlo 'uint8_t' (o digitarlo a ese) Esto se debe a que el tipo de 8 bits tendría bits no utilizados en la representación de almacenamiento, que 'uint8_t' no debe tener. –

5

Hay poco. Desde el punto de vista de portabilidad, char no puede ser más pequeño que 8 bits, y nada puede ser más pequeño que char, por lo que si una implementación C dada tiene un tipo entero de 8 bits sin signo, será char. Alternativamente, puede que no tenga uno, en cuyo punto cualquier truco typedef es irrelevante.

Se podría utilizar para documentar mejor su código en un sentido que está claro que requiere bytes de 8 bits allí y nada más. Pero en la práctica es una expectativa razonable prácticamente en cualquier parte (hay plataformas DSP en las que no es cierto, pero las posibilidades de que el código se ejecute allí son escasas, y podrías equivocarte usando una afirmación estática en la parte superior de tu programa en tal plataforma).

+0

Para el registro, puede hacer un tipo de 8 bits en cualquier plataforma: 'typedef struct {unsigned i: 8; } uint8_t; 'pero tendrías que usarlo como' uint8_t x; x.i = ... 'así que sería un poco más engorroso. –

+0

Creo que los caracteres pueden ir tan bajo como 4 bits, por debajo de eso y las cosas se desmoronan un poco en el estándar (aunque existe la posibilidad de que esté equivocado). – Skizz

+7

@Skizz - No, el estándar requiere 'unsigned char' para poder mantener los valores entre 0 y 255. Si puede hacerlo en 4 bits, no tengo sombrero. –

30

El objetivo es escribir código independiente de la implementación. unsigned char no garantiza que sea un tipo de 8 bits.uint8_t es.

+3

... si existe en un sistema, pero eso va a ser muy raro. +1 –

+2

bien si realmente tuviste problemas con el código que no se compila en un sistema porque uint8_t no existía, puedes usar find y sed para cambiar automáticamente todas las ocurrencias de uint8_t a char sin signo o algo más útil para ti. – bazz

+1

@bazz, no si está asumiendo que es un tipo de 8 bits que no puede, por ejemplo, para descomprimir datos empaquetados de manera byte por un sistema remoto. La suposición implícita es que la razón por la cual uint8_t no existe está en un procesador donde un char es más de 8 bits. –

2

Eso es realmente importante, por ejemplo, cuando está escribiendo un analizador de red. los encabezados de paquetes están definidos por la especificación del protocolo, no por la forma en que funciona el compilador C de una plataforma en particular.

+0

cuando le pregunté esto, definitivamente era un protocolo simple para communticaion sobre serial. –

6

En mi experiencia, hay dos lugares en los que queremos usar uint8_t para significar 8 bits (y uint16_t, etc.) y donde podemos tener campos de menos de 8 bits. Ambos lugares son donde el espacio es importante y, a menudo, necesitamos ver un volcado sin procesar de los datos cuando se depura y debemos poder determinar rápidamente lo que representa.

El primero es en protocolos de RF, especialmente en sistemas de banda estrecha. En este entorno, podemos necesitar empacar la mayor cantidad de información posible en un solo mensaje. El segundo es en almacenamiento flash donde podemos tener un espacio muy limitado (como en los sistemas integrados). En ambos casos se puede utilizar una estructura de datos envasado en la que el compilador se encargará del embalaje y desembalaje para nosotros:

#pragma pack(1) 
typedef struct { 
    uint8_t flag1:1; 
    uint8_t flag2:1; 
    padding1 reserved:6; /* not necessary but makes this struct more readable */ 
    uint32_t sequence_no; 
    uint8_t data[8]; 
    uint32_t crc32; 
} s_mypacket __attribute__((packed)); 
#pragma pack() 

El método que use depende de su compilador. También es posible que necesite admitir varios compiladores diferentes con los mismos archivos de encabezado. Esto sucede en sistemas integrados donde los dispositivos y servidores pueden ser completamente diferentes, por ejemplo, puede tener un dispositivo ARM que se comunique con un servidor Linux x86.

Hay algunas advertencias con el uso de estructuras empaquetadas. El mayor problema es que debes evitar eliminar la referencia de la dirección de un miembro. En sistemas con palabras alineadas mutibyte, esto puede dar como resultado una excepción desalineada, y un coredump.

Algunas personas también se preocuparán por el rendimiento y argumentarán que el uso de estas estructuras empaquetadas ralentizará su sistema. Es cierto que, detrás de escena, el compilador agrega código para acceder a los miembros de datos no alineados. Puedes verlo mirando el código ensamblador en tu IDE.

Pero como las estructuras empaquetadas son más útiles para la comunicación y el almacenamiento de datos, los datos se pueden extraer en una representación no empaquetada cuando se trabaja con ella en la memoria. Normalmente, no es necesario que trabajemos con todo el paquete de datos en la memoria de todos modos.

Aquí es un poco de discusión relevante:

pragma pack(1) nor __attribute__ ((aligned (1))) works

Is gcc's __attribute__((packed))/#pragma pack unsafe?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html