2010-03-13 2 views
4

Мне нужно упаковать четыре подписанных байта в 32-битный интегральный тип. это то, что я придумал, чтобы:C/C++ упаковка подписанного символа в int

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

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

это хорошее решение? Является ли он переносным (не в смысле общения)? Есть ли готовое решение, возможно, повысить?

Проблема В основном я беспокоюсь о битовом порядке при преобразовании отрицательных битов из char в int. Я не знаю, какое должно быть правильное поведение.

Благодаря

ответ

7

Мне понравился ответ Джой Адама, за исключением того факта, что он написан с макросами (которые вызывают настоящую боль во многих ситуациях), и компилятор не даст вам предупреждения, если «char» не имеет ширины в 1 байт. Это мое решение (основанное на Joey's).

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 как компилятор должен справиться с этим для вас при переключении передач и я с-стиль отбрасывает, как они будут работать либо с или с ++ (ОП помечена как и то и другое).

+5

В C и C++ символ 'char' _always_ имеет размер одного байта. Байт _may_ не должен быть восьми бит. –

+0

+1: для разбивки на две функции и использование функции вместо макроса. Это идеальный случай наличия встроенной функции. – Arun

+0

Мне любопытно, почему вы включаете одну функцию C++ (встроенные функции), но осуждаете другую (улучшенные операторы трансляции) ... Стили C-Style устарели по какой-то причине. Иначе +1. –

1

«Совершенство»
ИМХО, это лучшее решение, которое вы собираетесь получить за это. EDIT: хотя я бы использовать static_cast<unsigned int> вместо броска C-стиля, и я, вероятно, не использовать отдельный метод, чтобы скрыть оттенок ....

Переносимость:
Там будет не портативный способ сделать это, потому что ничего не говорит char должно быть восемь бит, и ничего не говорит unsigned int должно быть 4 байта в ширину.

Кроме того, вы полагаетесь на конкретизацию, и поэтому пакет данных на одной архитектуре не будет использоваться на одном с противоположной консистенцией.

Есть ли готовое решение, возможно, повысить?
Не о котором я знаю.

+1

В обоих C и С ++ 'char' гарантированно иметь размер одного байта. –

+0

@ Джеймс Макнеллис: Нет, это не так. См. Здесь -> http://stackoverflow.com/questions/881894/is-char-guaranteed-to-be-exactly-8-bit-long-in-c –

+0

Отклонение Endianness, предполагая, что символы имеют 8 бит, а unsigned int is 32 бита, я думаю, что трюк профсоюза хорош, даже если правила строгих правил псевдонимов считаются (поскольку char может псевдонимом что-либо). Возможно, я ошибаюсь. –

7

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, - это предотвращение нежелательного расширения знака.

+0

Почему вы упаковываете c1 3 раза? Вы можете позаботиться о порядке операций, поставив аргументы макроса в скобки .... –

+0

Ahh - Я вижу, вы исправили это. +1 затем. –

+0

К сожалению. Починил это. –

1

Это основано на Grant Peters и ответы Джои Адамс, расширен, чтобы показать, как распаковать подписанные значения (распаковке функции опираются на правила по модулю беззнаковых значений в C):

(Как отметил Стив Джессоп в комментариях нет необходимости в отдельных функциях 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, так что строго не портативный).

+0

Не является ли 'pack_s' избыточным? Если вы вызываете 'pack_u' с выражениями выражения типа' int8_t', они будут преобразованы автоматически. –

+0

И '<=' в распаковках, я думаю. –

+0

Правильно, вы исправлены. – caf

3

Вы можете избежать разрезов с неявными преобразованиями:

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_t и т.д. действительно существуют. Это редкая реализация, которая не имеет 8-битного и 32-битного 2-го уровня.

-1

Вы также можете позволить компилятору выполнить эту работу за вас.

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

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

т.д.

+0

есть комментарий мистера Петерса, почему этот подход не переносится в этой теме. – Anycorn

Смежные вопросы