Question

J'ai besoin d'emballer quatre octets signés en type intégral 32 bits. ce que je suis venu à:

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

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

est-ce une bonne solution? Est-ce portable (pas dans le sens de la communication)? est-il une solution prête à l'emploi, peut-être augmenter?

question que je suis préoccupé par la plupart du temps est l'ordre binaire lors de la conversion de bits négatifs de char à int. Je ne sais pas ce que devrait être le bon comportement.

Merci

Était-ce utile?

La solution

J'ai aimé la réponse de Joey Adam à l'exception du fait qu'il est écrit avec des macros (qui provoquent une vraie douleur dans de nombreuses situations) et le compilateur ne vous donne un avertissement si « char » est pas 1 octet large. Ceci est ma solution (basée hors 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);
}

J'ai omis casting c0-> c3 à un uint32_t que le compilateur doit gérer pour vous lors du passage et je c style jette comme ils vont travailler pour c ou c ++ (OP marqué à la fois).

Autres conseils

char n'est pas garanti d'être signé ou non signé (sur PowerPC Linux, par défaut char à non signé ). Passez le mot!

Qu'est-ce que vous voulez est quelque chose comme cette 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)))

Il est laid, principalement parce qu'il ne joue pas bien avec l'ordre de C des opérations. En outre, les backslash-retours sont là pour cette macro ne doit pas être une grande ligne longue.

En outre, la raison pour laquelle nous avons jeté à uint8_t avant la coulée à uint32_t est d'empêcher l'extension de signe indésirable.

Vous pouvez éviter les conversions implicites avec des moulages:

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'idée est que vous voir « convertir tous les paramètres correctement. Maj et les combiner », plutôt que « pour chaque paramètre, convertir correctement, changement et combiner ». Pas grand-chose en elle, cependant.

Alors:

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

Sortie:

4
5
249
-7

Ceci est aussi portable que les types uint32_t, uint8_t et int8_t. Aucun d'entre eux est nécessaire C99, et le stdint.h d'en-tête n'est pas défini en C ++ ou C89. Si les types existent et répondent aux exigences C99, cependant, le code fonctionnera. Bien sûr, dans les fonctions C Déballez auraient besoin d'un paramètre de fonction au lieu d'un paramètre de modèle. Vous préférez peut-être que dans C ++ trop si vous voulez écrire des boucles courtes pour le déballage.

Pour tenir compte du fait que les types sont facultatifs, vous pouvez utiliser uint_least32_t, qui est nécessaire dans C99. De même uint_least8_t et int_least8_t. Vous devrez changer le code de pack_helper et 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)));
}

Pour être honnête, il est peu probable d'être la peine - les chances sont le reste de votre application est écrite sur l'hypothèse que int8_t etc existent. Il est une application rare qui ne dispose pas d'un 8 bits et un type de complément à 32 bits 2.

"Bonté" À mon humble avis, c'est la meilleure solution que vous allez obtenir pour cela. EDIT: si j'utiliser static_cast<unsigned int> au lieu de la distribution de style C, et je serais probablement pas utiliser une méthode distincte pour cacher la distribution ....

Portabilité: Il va y avoir aucun moyen portable pour le faire parce que rien ne dit char doit être huit bits, et rien ne dit unsigned int doit être 4 octets de large.

De plus, vous comptez sur boutisme et donc pack'd de données sur une seule architecture ne sera pas utilisable sur un avec le boutisme opposé.

est-il une solution prête à l'emploi, stimuler peut-être? Pas dont je suis conscient.

Ceci est basé sur les réponses Grant Peters et Joey Adams, étendu pour montrer comment déballer les valeurs signées (les fonctions Déballez reposent sur les règles modulo des valeurs non signées en C):

(Comme Steve Jessop a noté dans les commentaires, il n'y a pas besoin de fonctions de pack_s et pack_u séparés).

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

(Ces derniers sont nécessaires plutôt que de simplement jeter retour à int8_t, car celui-ci peut provoquer un signal défini par l'implémentation à soulever si la valeur est supérieure à 127, il est donc pas strictement portable).

Vous pouvez également laisser le compilateur faire le travail pour vous.

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

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

Etc.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top