Pregunta

He que llevar cuatro bytes firmados en tipo integral de 32 bits. esto es lo que me ocurrió a:

int32_t byte(int8_t c) { return (unsigned char)c; }

int pack(char c0, char c1, ...) {
  return byte(c0) | byte(c1) << 8 | ...;
}

Es esta una buena solución? Es portátil (no en el sentido de la comunicación)? ¿hay una solución lista para usar, tal vez impulsar?

problema que más me preocupa es orden de los bits en la conversión de bits negativos de char a int. No sé cuál es el comportamiento correcto debería ser.

Gracias

¿Fue útil?

Solución

Me gustó la respuesta de Joey Adam excepto por el hecho de que está escrito con macros (que causan un dolor real en muchas situaciones) y el compilador no le dará una advertencia si char 'no es de 1 byte de ancho. Esta es mi solución (con sede fuera de Joey).

inline uint32_t PACK(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) {
    return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
}

inline uint32_t PACK(sint8_t c0, sint8_t c1, sint8_t c2, sint8_t c3) {
    return PACK((uint8_t)c0, (uint8_t)c1, (uint8_t)c2, (uint8_t)c3);
}

He omitido la fundición C0-> c3 a un uint32_t como el compilador debe manejar esto para usted cuando se cambia y solía c-estilo pone en la piel que van a trabajar, ya sea para C o C ++ (OP etiquetado como ambos).

Otros consejos

char no está garantizada para ser con o sin signo (en PowerPC Linux, por defecto char a sin signo ). Corre la voz!

Lo que se quiere es algo así como esta macro:

#include <stdint.h> /* Needed for uint32_t and uint8_t */

#define PACK(c0, c1, c2, c3) \
    (((uint32_t)(uint8_t)(c0) << 24) | \
    ((uint32_t)(uint8_t)(c1) << 16) | \
    ((uint32_t)(uint8_t)(c2) << 8) | \
    ((uint32_t)(uint8_t)(c3)))

Es feo principalmente porque no juega bien con el fin de las operaciones de C. Además, la barra invertida de rendimientos están ahí por lo que esta macro no tiene que ser una gran cola larga.

Además, la razón por la que echamos a uint8_t antes de echar a uint32_t es para evitar la extensión señal no deseada.

Puede evitar moldes con las conversiones implícitas:

uint32_t pack_helper(uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) {
    return c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
}

uint32_t pack(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) {
    return pack_helper(c0, c1, c2, c3);
}

La idea es que se vea "convertir todos los parámetros correctamente. Shift y combinarlos", en lugar de "para cada parámetro, convertirlo correctamente, el cambio y combinarlo". No hay mucho en ella, sin embargo.

A continuación:

template <int N>
uint8_t unpack_u(uint32_t packed) {
    // cast to avoid potential warnings for implicit narrowing conversion
    return static_cast<uint8_t>(packed >> (N*8));
}

template <int N>
int8_t unpack_s(uint32_t packed) {
    uint8_t r = unpack_u<N>(packed);
    return (r <= 127 ? r : r - 256); // thanks to caf
}

int main() {
    uint32_t x = pack(4,5,6,-7);
    std::cout << (int)unpack_u<0>(x) << "\n";
    std::cout << (int)unpack_s<1>(x) << "\n";
    std::cout << (int)unpack_u<3>(x) << "\n";
    std::cout << (int)unpack_s<3>(x) << "\n";
}

Salida:

4
5
249
-7

Esto es tan portátil como los tipos uint32_t, uint8_t y int8_t. se requiere ninguno de ellos en C99, y el stdint.h cabecera no está definido en C ++ o C89. Si existen los tipos y cumplen con los requisitos C99, sin embargo, el código funcionará. Por supuesto, en C las funciones de desempaquetado necesitarían un parámetro de función en lugar de un parámetro de plantilla. Si lo prefiere, que en C ++ también si usted quiere escribir bucles cortos para desempaquetar.

Para hacer frente al hecho de que los tipos son opcionales, se puede usar uint_least32_t, que se requiere en C99. Del mismo modo uint_least8_t y int_least8_t. Usted tendría que cambiar el código de pack_helper y unpack_u:

uint_least32_t mask(uint_least32_t x) { return x & 0xFF; }

uint_least32_t pack_helper(uint_least32_t c0, uint_least32_t c1, uint_least32_t c2, uint_least32_t c3) {
    return mask(c0) | (mask(c1) << 8) | (mask(c2) << 16) | (mask(c3) << 24);
}

template <int N>
uint_least8_t unpack_u(uint_least32_t packed) {
    // cast to avoid potential warnings for implicit narrowing conversion
    return static_cast<uint_least8_t>(mask(packed >> (N*8)));
}

Para ser honesto, esto es poco probable que sea vale la pena - es probable que el resto de su aplicación está escrita en el supuesto de que no exista int8_t etc. Es una aplicación rara que no tiene un niño de 8 bits y una de 32 bits de tipo 2 del complemento.

"bondad"
En mi humilde opinión, esta es la mejor solución que se va a conseguir para esto. EDIT: a pesar de que haría uso de static_cast<unsigned int> en lugar de la conversión de estilo C, y probablemente no haría uso de un método separado para ocultar la presión ....

Portabilidad:
No va a haber ninguna manera portátil para hacer esto porque nada dice char tiene que ser ocho bits, y nada dice unsigned int tiene que ser de 4 bytes de ancho.

Además, usted está confiando en orden de bits y por lo tanto pack'd datos en una arquitectura no se podrá utilizar a uno con el orden de bits contrario.

¿hay una solución lista para usar, tal vez impulsar?
No de los que soy consciente.

Esto se basa en subvención Peters y respuestas Joey Adams, extendida a mostrar cómo se puede desempaquetar los valores con signo (las funciones de desempaquetado y se basan en las reglas de módulo de valores sin signo de C):

(como se señaló Steve Jessop en los comentarios, no hay necesidad de funciones pack_s y pack_u separadas).

inline uint32_t pack(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3)
{
    return ((uint32_t)c0 << 24) | ((uint32_t)c1 << 16) |
        ((uint32_t)c2 << 8) | (uint32_t)c3;
}

inline uint8_t unpack_c3_u(uint32_t p)
{
    return p >> 24;
}

inline uint8_t unpack_c2_u(uint32_t p)
{
    return p >> 16;
}

inline uint8_t unpack_c1_u(uint32_t p)
{
    return p >> 8;
}

inline uint8_t unpack_c0_u(uint32_t p)
{
    return p;
}

inline uint8_t unpack_c3_s(uint32_t p)
{
    int t = unpack_c3_u(p);
    return t <= 127 ? t : t - 256;
}

inline uint8_t unpack_c2_s(uint32_t p)
{
    int t = unpack_c2_u(p);
    return t <= 127 ? t : t - 256;
}

inline uint8_t unpack_c1_s(uint32_t p)
{
    int t = unpack_c1_u(p);
    return t <= 127 ? t : t - 256;
}

inline uint8_t unpack_c0_s(uint32_t p)
{
    int t = unpack_c0_u(p);
    return t <= 127 ? t : t - 256;
}

(Estos son necesarios en lugar de simplemente echando de nuevo a int8_t, porque éste puede causar una señal definida por la implementación que se planteó si el valor es más de 127, lo que no es estrictamente portátil).

También puede dejar que el compilador haga el trabajo por usted.

union packedchars {
  struct {
    char v1,v2,v3,v4;
  }
  int data;
};

packedchars value;
value.data = 0;
value.v1 = 'a';
value.v2 = 'b;

etc.

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