سؤال

أحتاج إلى حزمة أربعة بايت موقعة في نوع متكامل 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. أنا لا أعرف ما يجب أن يكون السلوك الصحيح.

شكرا

هل كانت مفيدة؟

المحلول

أحببت إجابة جوي آدم باستثناء حقيقة أنه مكتوب مع وحدات الماكرو (التي تسبب ألم حقيقي في العديد من الحالات) ولن يمنحك المحول البرمجي تحذيرا إذا كان 'char' ليس واسعا 1 بايت. هذا حلاي (بناء على جوي).

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 أو C ++ (المرجع الموسوم).

نصائح أخرى

char غير مضمون لتوقيعها أو غير موقعة (على PowerPC Linux، char الإعدادات الافتراضية غير موقعة). انشر الكلمة!

ما تريده هو شيء مثل هذا الماكرو:

#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 للعمليات. أيضا، عودة الخلفية هناك حتى لا يجب أن يكون هذا الماكرو خطا طويلا كبيرا.

أيضا، السبب الذي يلقيه إلى UINT8_T قبل الصب على UINT32_T هو منع امتداد الإشارة غير المرغوب فيه.

يمكنك تجنب السقوط مع التحويلات الضمنية:

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، ستحتاج وظائف Unpack إلى معلمة دالة بدلا من معلمة القالب. قد تفضل ذلك في C ++ أيضا إذا كنت ترغب في كتابة حلقات قصيرة للتطبيق.

لمعالجة حقيقة أن الأنواع اختيارية، يمكنك استخدامها uint_least32_t, ، وهو مطلوب في C99. بصورة مماثلة uint_least8_t و int_least8_t. وبعد يجب عليك تغيير رمز Pack_Helper و Upack_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_t وما إلى ذلك موجودة. إنه تطبيق نادر لا يحتوي على 8 بت و 32 بت مكملا.

"صلاح"
IMHO، هذا هو أفضل حل سوف تحصل عليه لهذا الغرض. تحرير: على الرغم من أنني كنت تستخدم static_cast<unsigned int> بدلا من وضع نمط C، وربما لن أستخدم طريقة منفصلة لإخفاء المصبوب ....

قابلية التنقل:
لن تكون هناك طريقة محمولة للقيام بذلك لأن لا شيء يقول char يجب أن يكون ثمانية بت، ولا شيء يقول unsigned int يحتاج إلى أن يكون 4 بايت واسعة.

علاوة على ذلك، فإنك تعتمد على الإختناع وبالتالي فإن حزمة البيانات الخاصة بنية واحدة لن تكون صالحة للاستعمال على واحدة مع الإخفاء المعاكس.

هل هناك حل جاهز، ربما دفعة؟
ليس مني أنا على علم.

يعتمد هذا على منحة بيترز وإجابات Joey Adams، ممتدة لإظهار كيفية تفريغ القيم الموقعة (تعتمد وظائف فك التعاود على قواعد MODULO للقيم غير الموقعة في ج):

(كما لاحظ ستيف جيسوب في التعليقات، ليست هناك حاجة للفصل pack_s و 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، فستكون محمولة بدقة).

يمكنك أيضا السماح للمترجم بعمل العمل المناسب لك.

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

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

إلخ.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top