Frage

Ich brauche haben vier signierten Bytes in packen 32-Bit-Integral-Typ. das ist, was ich kam zu:

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

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

ist dies eine gute Lösung? Ist es tragbar (nicht in Kommunikation Sinne)? gibt es eine fertige Lösung, vielleicht boost?

Frage, die ich meist besorgt bin im Begriff ist, etwas Ordnung, wenn der negativen Bits von char int konvertieren. Ich weiß nicht, was das richtige Verhalten sein sollte.

Danke

War es hilfreich?

Lösung

Ich mochte Joey Adams Antwort, außer der Tatsache, dass es mit Makros geschrieben (die eine echte Schmerzen in vielen Situationen verursachen) und der Compiler wird Ihnen keine Warnung geben, wenn ‚char‘ breit nicht 1 Byte ist. Dies ist meine Lösung (basierend aus Joeys).

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

Ich habe Gießen weggelassen C0-> c3 auf ein uint32_t als der Compiler dies für Sie behandeln sollte beim Schalten und ich c-Casts, wie sie für beide c oder c ++ arbeiten (die OP als beide markiert).

Andere Tipps

char ist nicht garantiert mit oder ohne Vorzeichen werden (für PowerPC-Linux, char standardmäßig auf unsigned ). Verbreiten Sie das Wort!

Was Sie wollen, ist so etwas wie dieses Makro:

#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 ist vor allem hässlich, weil es nicht gut mit C der Reihenfolge der Operationen spielt. Auch ist der Backslash-returns es so dieses Makro nicht eine große lange Linie sein muss.

Auch der Grund, warum wir zu uint8_t werfen, bevor uint32_t Gießen ist es, unerwünschte Zeichen Erweiterung zu verhindern.

Sie können Abgüsse mit impliziten Konvertierungen vermeiden:

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

Die Idee ist, dass Sie sehen, „konvertiert alle Parameter korrekt. Verschieben und kombinieren sie“, sondern als „für jeden Parameter, wandeln es richtig, Schicht- und kombinieren Sie es“. Nicht viel drin, aber.

Dann:

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

Ausgabe:

4
5
249
-7

Dies ist so portabel wie die uint32_t, uint8_t und int8_t Typen. Keiner von ihnen ist in C99 erforderlich, und der Header stdint.h ist in C ++ oder C89 nicht definiert. Wenn die Typen existieren und die C99-Anforderungen gerecht zu werden, obwohl, wird der Code arbeiten. Natürlich in C müssten die auspacken Funktionen einen Funktionsparameter anstelle eines Template-Parameter. Sie könnten, dass in C ++ bevorzugen auch, wenn Sie kurze Schleifen zum Auspacken schreiben wollen.

, die Tatsache zu richten, dass die Typen optional sind, könnten Sie uint_least32_t, die in C99 erforderlich ist. Ähnlich uint_least8_t und int_least8_t. Sie würden den Code von pack_helper und unpack_u ändern:

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

Um ehrlich zu sein dies unwahrscheinlich ist es wert zu sein - die Chancen der Rest der Anwendung sind auf der Annahme geschrieben, dass int8_t usw. existieren. Es ist eine seltene Implementierung, die keine 8-Bit hat und eine 32-Bit 2-Komplement-Typ.

"Güte"
IMHO, ist dies die beste Lösung, die Sie für diese bekommen sind. EDIT: obwohl ich static_cast<unsigned int> anstelle des C-Casts benutzen würde, und ich würde wahrscheinlich kein separates Verfahren verwenden, um die Besetzung zu verstecken ....

Portabilität:
Es gibt keinen tragbaren Weg, dies zu tun, weil nichts sagt char acht Bits sein muss, und nichts sagt unsigned int Bedürfnisse zu 4 Bytes breit.

Darüber hinaus Sie auf endianness und daher Daten sich auf eine Architektur pack'd wird auf einer mit dem anderen endianness nicht verwendet werden.

gibt es eine fertige Lösung, vielleicht steigern?
Nicht von denen ich bin mir dessen bewusst.

Dies basiert auf Grant-Peters und Antworten Joey Adams, erweitert zu zeigen, wie die signierten Werte entpacken (die auspacken Funktionen stützen sich auf den Modulo Regeln des Wertes ohne Vorzeichen in C):

(wie Steve Jessop in den Kommentaren erwähnt, gibt es keine Notwendigkeit für separate pack_s und pack_u Funktionen).

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

(Dies sind eher notwendig als einfach zurück zu int8_t Gießen, weil diese dazu führt, kann eine Implementierung definiert Signal angehoben werden, wenn der Wert über 127 ist, so dass es nicht unbedingt tragbar ist).

Sie können auch die Compiler lassen die Arbeit für Sie tun.

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

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

Etc.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top