4

4つの符号付きバイトを32ビットの整数型にパックする必要があります。これは私が思いついたものです:

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

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

これは良い解決策ですか?ポータブルですか(通信の意味ではありません)?既製の解決策はありますか、おそらくブーストですか?

私が最も懸念している問題は、負のビットをcharからintに変換するときのビット順序です。正しい振る舞いがどうあるべきかわかりません。

ありがとう

4

6 に答える 6

8

char符号付きまたは符号なしが保証されているわけではありません(PowerPC Linuxでは、charのデフォルトはunsignedです)。言葉を広める!

必要なのは、次のマクロのようなものです。

#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)))

これは主に、Cの演算の順序とうまく機能しないために醜いです。また、バックスラッシュリターンが存在するため、このマクロは1つの大きな長い行である必要はありません。

また、uint32_tにキャストする前にuint8_tにキャストする理由は、不要な符号拡張を防ぐためです。

于 2010-03-13T04:53:08.373 に答える
7

私はJoeyAdamの答えが好きでしたが、マクロで書かれていて(多くの状況で本当に苦痛を引き起こします)、「char」の幅が1バイトでない場合、コンパイラーは警告を表示しません。これが私の解決策です(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);
}

コンパイラがシフト時にこれを処理する必要があるため、c0-> c3をuint32_tにキャストすることを省略しました。また、cまたはc ++(両方のタグが付けられたOP)で機能するため、cスタイルのキャストを使用しました。

于 2010-03-13T05:12:09.550 に答える
4

暗黙の変換でキャストを回避できます。

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

考え方は、「各パラメーターについて、正しく変換し、シフトして結合する」ではなく、「すべてのパラメーターを正しく変換する。シフトして結合する」ということです。ただし、それほど多くはありません。

それで:

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

出力:

4
5
249
-7

uint32_tこれは、、uint8_tおよびint8_tタイプと同じくらいポータブルです。これらはいずれもC99では必要ありません。また、ヘッダーstdint.hはC++またはC89で定義されていません。ただし、タイプが存在し、C99要件を満たしている場合、コードは機能します。もちろん、Cでは、アンパック関数にはテンプレートパラメーターの代わりに関数パラメーターが必要です。解凍するための短いループを記述したい場合は、C++でもそれを好むかもしれません。

uint_least32_tタイプがオプションであるという事実に対処するために、C99で必須のを使用できます。同様にuint_least8_tそしてint_least8_t。pack_helperと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)));
}

正直なところ、これは価値がない可能性があります。アプリケーションの残りの部分は、int8_tetcが存在することを前提として作成されている可能性があります。これは、8ビットと32ビットの2の補数型を持たないまれな実装です。

于 2010-03-13T13:52:08.470 に答える
1

「良さ」
私見、これはあなたがこれのために得ようとしている最良の解決策です。編集:私static_cast<unsigned int>はCスタイルのキャストの代わりに使用しますが、キャストを非表示にするために別の方法を使用することはおそらくないでしょう...。

移植性:
これを行うための移植可能な方法はありませcharん。8ビットである必要があるとは言われておらず、 unsigned int4バイト幅である必要があるとは何も言われていないからです。

さらに、エンディアンに依存しているため、一方のアーキテクチャにパックされたデータは、反対のエンディアンのアーキテクチャでは使用できません。

既製の解決策はありますか、おそらくブーストですか?
私が知っていることではありません。

于 2010-03-13T04:45:29.390 に答える
1

これは、GrantPetersとJoeyAdamsの回答に基づいており、符号付き値をアンパックする方法を示すために拡張されています(アンパック関数は、Cの符号なし値のモジュロルールに依存しています)。

pack_s(Steve Jessopがコメントで述べたように、個別の関数は必要ありませんpack_u)。

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

(これらは、単ににキャストバックするのではなく必要int8_tです。後者は、値が127を超えると、実装定義のシグナルが発生する可能性があるため、厳密には移植性がありません)。

于 2010-03-13T08:15:54.457 に答える
-2

コンパイラに作業を任せることもできます。

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

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

等。

于 2010-03-13T18:42:23.300 に答える