Domanda

Ho bisogno di mettere in valigia quattro byte firmato in a 32-bit di tipo integrale. questo è ciò che mi è venuta a:

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

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

È questa una buona soluzione? E 'portatile (non in senso comunicazione)? c'è una soluzione pronta all'uso, forse aumentare?

questione Sono per lo più preoccupa è ordine di bit per la conversione di bit negativi da char a int. Io non so quale sia il comportamento corretto dovrebbe essere.

Grazie

È stato utile?

Soluzione

Mi è piaciuta la risposta di Joey Adam tranne per il fatto che è scritto con le macro (che causano un vero e proprio dolore in molte situazioni) e il compilatore non vi darà un avviso se 'char' non è 1 byte di larghezza. Questa è la mia soluzione (in base al largo di 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);
}

ho omesso di colata C0> c3 ad un uint32_t come il compilatore dovrebbe gestire questo per voi quando lo spostamento e ho usato in stile C getta in quanto saranno lavorare sia per C o C ++ (PO contrassegnati come entrambi).

Altri suggerimenti

char non è garantito per essere con o senza segno (su PowerPC Linux, di default char a unsigned ). Spargi la voce!

Quello che vuoi è qualcosa di simile a questa 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)))

E 'brutto soprattutto perché non gioca bene con l'ordine di C delle operazioni. Inoltre, il backslash-rendimenti sono lì così questa macro non deve essere una grande linea lunga.

Inoltre, il motivo per cui abbiamo gettato al uint8_t prima del getto di uint32_t è quello di prevenire l'estensione segno indesiderati.

È possibile evitare calchi con le conversioni implicite:

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);
}

L'idea è che si vede "convertire correttamente tutti i parametri. Maiusc e combinarle", piuttosto che "per ogni parametro, convertirlo in modo corretto, lo spostamento e combinarlo". Non molto in esso, però.

Quindi:

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";
}

Output:

4
5
249
-7

Questo è più portabile come i tipi uint32_t, uint8_t e int8_t. Nessuno di loro è richiesto in C99, e lo stdint.h intestazione non è definita in C ++ o C89. Se esistono i tipi e soddisfano i requisiti C99, però, il codice funzionerà. Naturalmente, in C le funzioni decomprimere avrebbero bisogno di un parametro di funzione invece di un parametro di modello. Si potrebbe preferire che in C ++ anche se si desidera scrivere i cicli brevi per la decompressione.

Per affrontare il fatto che i tipi sono opzionali, è possibile utilizzare uint_least32_t, che è richiesto in C99. Analogamente uint_least8_t e int_least8_t. Si dovrà modificare il codice di pack_helper e 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)));
}

Per essere onesti questo è improbabile che sia valsa la pena - è probabile che il resto della vostra applicazione è scritta sul presupposto che int8_t ecc esistono. Si tratta di una realizzazione rara che non ha un 8 bit e una a 32 bit di tipo complemento a 2.

"Bontà"
IMHO, questa è la soluzione migliore che si vuole ottenere per questo. EDIT: se io userei static_cast<unsigned int> al posto del cast C-style, e io non avrei probabilmente usare un metodo separato per nascondere il cast ....

Portabilità:
Ci sta per essere alcun modo comune di fare questo, perché nulla dice char deve essere otto bit, e nulla dice unsigned int deve avere una larghezza di 4 byte.

Inoltre, vi affidate ai endianness e quindi pack'd i dati su un'architettura non sarà utilizzabile su uno con l'endianness opposto.

c'è una soluzione pronta all'uso, forse aumentare?
Non di cui sono a conoscenza.

Questo è basato su Grant Peters e risposte Joey Adams', estesa a mostrare come disimballare i valori con segno (le funzioni decomprimere si basano sulle regole Modulo di valori senza segno in C):

(Come Steve Jessop ha notato nei commenti, non v'è alcuna necessità di funzioni pack_s e pack_u separati).

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;
}

(Queste sono necessarie piuttosto che semplicemente colata ritorna int8_t, perché quest'ultimo può causare un segnale di attuazione definito da sollevare se il valore è superiore a 127, quindi non è strettamente portatile).

Si potrebbe anche lasciare che il compilatore fare il lavoro per voi.

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

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

Etc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top