Мне нужно написать функцию для преобразования прямого порядка байтов в обратный порядок байтов в C. Я не могу использовать какие-либо библиотечные функции.
c
swap
endianness
Алекс Ксандер
источник
источник
Ответы:
Предполагая, что вам нужен простой обмен байтами, попробуйте что-то вроде
16-битное преобразование без знака:
swapped = (num>>8) | (num<<8);
32-битное преобразование без знака:
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0 ((num<<8)&0xff0000) | // move byte 1 to byte 2 ((num>>8)&0xff00) | // move byte 2 to byte 1 ((num<<24)&0xff000000); // byte 0 to byte 3
Это меняет порядок байтов с позиций 1234 на 4321. Если ваш ввод был
0xdeadbeef
, 32-битный порядок байтов мог бы иметь на выходе0xefbeadde
.Приведенный выше код следует очистить макросами или, по крайней мере, константами вместо магических чисел, но, надеюсь, он поможет как есть
РЕДАКТИРОВАТЬ: как указано в другом ответе, существуют конкретные альтернативы для платформы, ОС и набора инструкций, которые могут быть НАМНОГО быстрее, чем указано выше. В ядре Linux есть макросы (например, cpu_to_be32), которые довольно хорошо обрабатывают порядок байтов. Но эти альтернативы специфичны для их среды. На практике с порядком байтов лучше всего справляться с использованием сочетания доступных подходов.
источник
((num & 0xff) >> 8) | (num << 8)
, gcc 4.8.3 генерирует однуrol
инструкцию. И если 32-битное преобразование записано как((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
, тот же компилятор генерирует однуbswap
инструкцию.struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}
где это битовое поле с 8 полями по 1 бит каждое. Но я не уверен, что это так быстро, как другие предложения. Для целых чисел используйтеunion { int i; byte_t[sizeof(int)]; }
для обратного байта байта в целое число.Включая:
#include <byteswap.h>
вы можете получить оптимизированную версию машинно-зависимых функций обмена байтами. Затем вы можете легко использовать следующие функции:
__bswap_32 (uint32_t input)
или
__bswap_16 (uint16_t input)
источник
#include <byteswap.h>
, см. Комментарий в самом файле .h. Этот пост содержит полезную информацию, поэтому я проголосовал за, несмотря на то, что автор игнорировал требование OP не использовать функцию lib.#include <stdint.h> //! Byte swap unsigned short uint16_t swap_uint16( uint16_t val ) { return (val << 8) | (val >> 8 ); } //! Byte swap short int16_t swap_int16( int16_t val ) { return (val << 8) | ((val >> 8) & 0xFF); } //! Byte swap unsigned int uint32_t swap_uint32( uint32_t val ) { val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); return (val << 16) | (val >> 16); } //! Byte swap int int32_t swap_int32( int32_t val ) { val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); return (val << 16) | ((val >> 16) & 0xFFFF); }
Обновление : добавлена замена 64-битных байтов.
int64_t swap_int64( int64_t val ) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); } uint64_t swap_uint64( uint64_t val ) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | (val >> 32); }
источник
int32_t
иint64_t
вариантов, что рассуждения за маскировку... & 0xFFFF
и... & 0xFFFFFFFFULL
? Что-то происходит со знаком-расширением, которого я не вижу? Кроме того, почемуswap_int64
возвращаетсяuint64_t
? Разве этого не должно бытьint64_t
?swap_int64
в своем ответе. +1 за полезный ответ, BTW!LL
не нужны,(u)swap_uint64()
как иL
не нужны(u)swap_uint32()
. ЭтоU
не нужно воuswap_uint64()
многом, какU
не нужно вuswap_uint32()
Вот довольно общая версия; Я не скомпилировал его, так что, вероятно, есть опечатки, но вы должны понять,
void SwapBytes(void *pv, size_t n) { assert(n > 0); char *p = pv; size_t lo, hi; for(lo=0, hi=n-1; hi>lo; lo++, hi--) { char tmp=p[lo]; p[lo] = p[hi]; p[hi] = tmp; } } #define SWAP(x) SwapBytes(&x, sizeof(x));
NB: это не оптимизировано для скорости или места. Он должен быть понятным (легко отлаживать) и переносимым.
Обновление 2018-04-04 Добавлен assert () для перехвата недопустимого случая n == 0, обнаруженного комментатором @chux.
источник
bswap
инструкцию приличным компилятором X86 с включенной оптимизацией. Эта версия с параметром размера не могла этого сделать.Если вам нужны макросы (например, встроенная система):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8)) #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
источник
UINT
в их имени стоит .Редактировать: это библиотечные функции. Следование им - это ручной способ сделать это.
Я совершенно ошеломлен количеством людей, не знающих о __byteswap_ushort, __byteswap_ulong и __byteswap_uint64 . Конечно, они специфичны для Visual C ++, но они компилируются до восхитительного кода на архитектурах x86 / IA-64. :)
Вот явное использование
bswap
инструкции, взятой с этой страницы . Обратите внимание, что внутренняя форма выше всегда будет быстрее, чем эта , я добавил ее только для того, чтобы дать ответ без библиотечной процедуры.uint32 cq_ntohl(uint32 a) { __asm{ mov eax, a; bswap eax; } }
источник
Как шутку:
#include <stdio.h> int main (int argc, char *argv[]) { size_t sizeofInt = sizeof (int); int i; union { int x; char c[sizeof (int)]; } original, swapped; original.x = 0x12345678; for (i = 0; i < sizeofInt; i++) swapped.c[sizeofInt - i - 1] = original.c[i]; fprintf (stderr, "%x\n", swapped.x); return 0; }
источник
int i, size_t sizeofInt
и разные типы.вот способ использования инструкции SSSE3 pshufb с использованием ее встроенной функции Intel, если у вас есть кратное 4
int
с:unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) { int i; __m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); for (i = 0; i < length; i += 4) { _mm_storeu_si128((__m128i *)&destination[i], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask)); } return destination; }
источник
Будет ли это работать / будет быстрее?
uint32_t swapped, result; ((byte*)&swapped)[0] = ((byte*)&result)[3]; ((byte*)&swapped)[1] = ((byte*)&result)[2]; ((byte*)&swapped)[2] = ((byte*)&result)[1]; ((byte*)&swapped)[3] = ((byte*)&result)[0];
источник
char
, что нетbyte
.Вот функция, которую я использовал - протестировал и работает с любым базовым типом данных:
// SwapBytes.h // // Function to perform in-place endian conversion of basic types // // Usage: // // double d; // SwapBytes(&d, sizeof(d)); // inline void SwapBytes(void *source, int size) { typedef unsigned char TwoBytes[2]; typedef unsigned char FourBytes[4]; typedef unsigned char EightBytes[8]; unsigned char temp; if(size == 2) { TwoBytes *src = (TwoBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[1]; (*src)[1] = temp; return; } if(size == 4) { FourBytes *src = (FourBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[3]; (*src)[3] = temp; temp = (*src)[1]; (*src)[1] = (*src)[2]; (*src)[2] = temp; return; } if(size == 8) { EightBytes *src = (EightBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[7]; (*src)[7] = temp; temp = (*src)[1]; (*src)[1] = (*src)[6]; (*src)[6] = temp; temp = (*src)[2]; (*src)[2] = (*src)[5]; (*src)[5] = temp; temp = (*src)[3]; (*src)[3] = (*src)[4]; (*src)[4] = temp; return; } }
источник
source
выравнивается по мере необходимости, но если это предположение не выполняется, код - UB.EDIT: эта функция меняет местами порядок байтов только выровненных 16-битных слов. Функция, часто необходимая для кодировок UTF-16 / UCS-2. РЕДАКТИРОВАТЬ КОНЕЦ.
Если вы хотите изменить порядок байтов блока памяти, вы можете использовать мой невероятно быстрый подход. Ваш массив памяти должен иметь размер, кратный 8.
#include <stddef.h> #include <limits.h> #include <stdint.h> void ChangeMemEndianness(uint64_t *mem, size_t size) { uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT; size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t); for(; size; size--, mem++) *mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT); }
Такая функция полезна для изменения порядка байтов файлов Unicode UCS-2 / UTF-16.
источник
t know if it
так же быстро, как и предложения, но он работает: github.com/heatblazer/helpers/blob/master/utils.hCHAR_BIT
а8
не любопытно, поскольку0xFF00FF00FF00FF00ULL
зависит отCHAR_BIT == 8
. Обратите внимание, чтоLL
в константе не требуется.CHAR_BIT
чтобы увеличить раскрытие этого макроса. Что касается LL, то это скорее аннотация, чем что-либо еще. Это также привычка, которую я давно уловил с компиляторами с ошибками (до стандарта), которые не работают правильно.Этот фрагмент кода может преобразовать 32-битное число с прямым порядком байтов в число с прямым порядком байтов.
#include <stdio.h> main(){ unsigned int i = 0xfafbfcfd; unsigned int j; j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24); printf("unsigned int j = %x\n ", j); }
источник
((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24);
может быть быстрее на некоторых платформах (например, повторное использование констант маски AND). Однако большинство компиляторов сделали бы это, но некоторые простые компиляторы не могут оптимизировать это за вас.Если вы работаете на процессоре x86 или x86_64, обратный порядок байтов является родным. так
для 16-битных значений
unsigned short wBigE = value; unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);
для 32-битных значений
unsigned int iBigE = value; unsigned int iLittleE = ((iBigE & 0xFF) << 24) | ((iBigE & 0xFF00) << 8) | ((iBigE >> 8) & 0xFF00) | (iBigE >> 24);
Это не самое эффективное решение, если компилятор не распознает, что это манипуляция на уровне байтов, и не сгенерирует код обмена байтами. Но это не зависит от каких-либо уловок с разметкой памяти и может быть довольно легко превращено в макрос.
источник