Как я могу преобразовать между значениями с прямым порядком байтов и значениями с прямым порядком байтов в C ++?
РЕДАКТИРОВАТЬ: Для ясности, я должен переводить двоичные данные (значения с плавающей запятой двойной точности и 32-разрядные и 64-разрядные целые числа) из одной архитектуры ЦП в другую. Это не связано с сетью, поэтому ntoh () и подобные функции здесь не будут работать.
РЕДАКТИРОВАТЬ # 2: Ответ, который я принял, относится непосредственно к компиляторам, на которые я нацеливаюсь (именно поэтому я выбрал его). Однако здесь есть и другие очень хорошие, более портативные ответы.
c++
endianness
Uhall
источник
источник
short swap(short x)
код, так как он сломается, если вы перейдете на платформу с другим порядком байтов. Matthieu M имеет единственный правильный ответ ниже.Ответы:
Если вы используете Visual C ++ сделайте следующее: Вы включаете intrin.h и вызываете следующие функции:
Для 16-битных чисел:
Для 32-битных чисел:
Для 64-битных чисел:
8-битные числа (символы) не нужно конвертировать.
Кроме того, они определены только для беззнаковых значений, они работают и для целых чисел со знаком.
Для чисел с плавающей запятой и двойников это сложнее, чем с простыми целыми числами, поскольку они могут быть или не быть в порядке байтов хост-машин. Вы можете получить поплавки с прямым порядком байтов на машинах с прямым порядком байтов и наоборот.
Другие компиляторы также имеют аналогичные свойства.
В GCC , например , вы можете напрямую позвонить некоторые встроенные команды как описано здесь :
(не нужно что-то включать). Afaik bits.h также объявляет ту же функцию не GCC-ориентированным способом.
16-битный своп это просто бит-поворот.
Вызов intrinsics вместо того, чтобы катиться самостоятельно, дает вам лучшую производительность и плотность кода.
источник
__builtin_bswapX
доступно только с GCC-4.3 и далееhtonl
,htons
и т.д. Вы должны знать из контекста ситуации , когда на самом деле поменять байты.htonl
иntohl
не беспокоясь о контексте работало бы при написании переносимого кода, так как платформа, определяющая эти функции, могла бы поменять его местами, если он имеет младший / средний порядок байтов, а на старшем - это не будет. Однако при декодировании стандартного типа файла, который определен как little-endian (скажем, BMP), нужно знать контекст и не может просто полагаться наhtonl
иntohl
.Проще говоря:
Использование:
swap_endian<uint32_t>(42)
.источник
От ошибки порядка байтов Роба Пайка:
TL; DR: не беспокойтесь о собственном порядке вашей платформы, все, что имеет значение, это порядок байтов потока, из которого вы читаете, и вам лучше надеяться, что он хорошо определен.
Примечание: в комментарии было отмечено, что при отсутствии явного преобразования типов важно, чтобы
data
это был массивunsigned char
илиuint8_t
. Использованиеsigned char
илиchar
(если подписано) приведетdata[x]
к увеличению до целого числа и,data[x] << 24
возможно, к сдвигу 1 в знаковый бит, который является UB.источник
Если вы делаете это в целях совместимости сети / хоста, вы должны использовать:
Если вы делаете это по какой-то другой причине, одно из представленных здесь решений byte_swap будет работать просто отлично.
источник
htonl
иntohl
не может перейти на little-endian на платформе с прямым порядком байтов.Я взял несколько предложений из этого поста и собрал их вместе, чтобы сформировать это:
источник
Процедура перехода от порядкового номера к порядку байтов аналогична переходу от порядкового номера к порядку байтов.
Вот пример кода:
источник
Существует инструкция по сборке под названием BSWAP, которая выполнит замену очень быстро . Вы можете прочитать об этом здесь .
Visual Studio, или, точнее, библиотека времени выполнения Visual C ++, имеет встроенную платформу для этого, называемую
_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
. Подобные должны существовать для других платформ, но я не знаю, как они будут называться.источник
Мы сделали это с помощью шаблонов. Вы могли бы сделать что-то вроде этого:
источник
Если вы делаете это для передачи данных между различными платформами, посмотрите на функции ntoh и hton.
источник
Так же, как вы делаете в C:
Вы также можете объявить вектор беззнаковых символов, записать в него входное значение, обратить байты в другой вектор и записать байты, но это займет на несколько порядков больше, чем сдвоение, особенно с 64-разрядными значениями.
источник
В большинстве систем POSIX (это не входит в стандарт POSIX) существует файл endian.h, который можно использовать для определения того, какую кодировку использует ваша система. Оттуда это примерно так:
Это меняет порядок (с обратного порядка байтов на младший):
Если у вас есть номер 0xDEADBEEF (в системе с прямым порядком байтов, сохраненной как 0xEFBEADDE), ptr [0] будет 0xEF, ptr [1] будет 0xBE и т. Д.
Но если вы хотите использовать его для работы в сети, то htons, htonl и htonll (и их обратные ntohs, ntohl и ntohll) будут полезны для преобразования порядка хостов в порядок сетей.
источник
htonl
и друзей независимо от того, имеет ли сценарий использования какое-либо отношение к сети. Порядок байтов в сети имеет порядок байтов, поэтому просто относитесь к этим функциям как host_to_be и be_to_host. (Не помогает, если вам нужен host_to_le, хотя.)Обратите внимание, что, по крайней мере, для Windows, htonl () намного медленнее, чем их внутренний аналог _byteswap_ulong (). Первый - это вызов библиотеки DLL в ws2_32.dll, второй - одна инструкция по сборке BSWAP. Поэтому, если вы пишете некоторый платформо-зависимый код, предпочтите использовать встроенные функции для скорости:
Это может быть особенно важно для обработки изображений .PNG, где все целые числа сохраняются в Big Endian с объяснением «Можно использовать htonl () ...» {для замедления обычных программ Windows, если вы не готовы}.
источник
Большинство платформ имеют системный заголовочный файл, который обеспечивает эффективные функции byteswap. В Linux он есть в
<endian.h>
. Вы можете красиво обернуть это в C ++:Вывод:
источник
Мне нравится этот, только для стиля :-)
источник
char[]
«Ошибка: недопустимый тип не разрешен»Серьезно ... Я не понимаю, почему все решения такие сложные ! Как насчет самой простой, самой общей функции шаблона, которая заменяет любой тип любого размера при любых обстоятельствах в любой операционной системе?
Это волшебная сила C и C ++ вместе! Просто поменяйте местами исходную переменную символ за символом.
Пункт 1 : Нет операторов: Помните, что я не использовал простой оператор присваивания "=", потому что некоторые объекты будут испорчены, когда порядок байтов перевернут и конструктор копирования (или оператор присваивания) не будет работать. Поэтому более надежно копировать их char на char.
Пункт 2. Помните о проблемах выравнивания. Обратите внимание, что мы копируем в массив и из него, что является правильным решением, поскольку компилятор C ++ не гарантирует, что мы можем получить доступ к невыровненной памяти (этот ответ был обновлен по сравнению с исходным Форма для этого). Например, если вы выделяете
uint64_t
, ваш компилятор не может гарантировать, что вы можете получить доступ к 3-му байту этого какuint8_t
. Следовательно, правильнее всего скопировать это в массив символов, заменить его, а затем скопировать обратно (так что нетreinterpret_cast
). Обратите внимание, что компиляторы в основном достаточно умны, чтобы преобразовать то, что вы делали обратно, в,reinterpret_cast
если они способны получить доступ к отдельным байтам независимо от выравнивания.Чтобы использовать эту функцию :
и теперь
x
отличается по порядку байтов.источник
new
/,delete
чтобы выделить буфер для этого?!?sizeof(var)
является константой времени компиляции, так что вы можете сделатьchar varSwapped[sizeof(var)]
. Или вы могли бы сделатьchar *p = reinterpret_cast<char*>(&var)
и поменять местами.for(size_t i = 0 ; i < sizeof(var) ; i++)
вместоstatic_cast<long>
. (Или на самом деле своп на месте будет использовать восходящий и нисходящий,char*
так что все равно уйдет).У меня есть этот код, который позволяет мне конвертировать из HOST_ENDIAN_ORDER (что бы это ни было) в LITTLE_ENDIAN_ORDER или BIG_ENDIAN_ORDER. Я использую шаблон, поэтому, если я попытаюсь преобразовать из HOST_ENDIAN_ORDER в LITTLE_ENDIAN_ORDER, и они окажутся одинаковыми для машины, которую я компилирую, код не будет сгенерирован.
Вот код с некоторыми комментариями:
источник
Если 32-разрядное целое число без знака с прямым порядком байтов выглядит как 0xAABBCCDD, равное 2864434397, то то же самое 32-разрядное целое число без знака выглядит как 0xDDCCBBAA на процессоре с прямым порядком байтов, который также равен 2864434397.
Если 16-разрядное короткое число без знака с прямым порядком байтов выглядит как 0xAABB, что равно 43707, то такое же короткое 16-разрядное без знака выглядит как 0xBBAA на процессоре с прямым порядком байтов, который также равен 43707.
Вот несколько удобных функций #define, чтобы поменять байты с младшего к старшему и наоборот ->
источник
Вот обобщенная версия, которую я придумал, чтобы поменять значение на месте. Другие предложения были бы лучше, если производительность является проблемой.
Отказ от ответственности: я еще не пытался скомпилировать или проверить это.
источник
Если вы возьмете общий шаблон для изменения порядка битов в слове и выберете часть, которая инвертирует биты в каждом байте, то у вас останется нечто, что инвертирует только байты в слове. Для 64-битных:
Компилятор должен убрать лишние операции маскирования битов (я оставил их, чтобы выделить шаблон), но если этого не произойдет, вы можете переписать первую строку следующим образом:
Обычно это должно упростить до одной инструкции поворота на большинстве архитектур (игнорируя, что вся операция, вероятно, является одной инструкцией).
На процессоре RISC большие сложные константы могут вызвать трудности компиляции. Вы можете легко вычислить каждую из констант из предыдущей. Вот так:
Если вам нравится, вы можете написать это в виде цикла. Это не будет эффективно, но просто для удовольствия:
А для полноты приведем упрощенную 32-битную версию первой формы:
источник
Просто подумал, что я добавил свое собственное решение здесь, так как я его нигде не видел. Это небольшая и переносимая шаблонная функция C ++ и переносимая, которая использует только битовые операции.
источник
Я действительно удивлен, что никто не упомянул функции htobeXX и betohXX. Они определены в endian.h и очень похожи на сетевые функции htonXX.
источник
Используя коды ниже, вы можете легко переключаться между BigEndian и LittleEndian
источник
Я недавно написал макрос для этого в C, но он одинаково действителен и в C ++:
Он принимает любой тип и переворачивает байты в переданном аргументе. Пример использования:
Какие отпечатки:
Вышесказанное прекрасно подходит для копирования / вставки, но здесь многое происходит, поэтому я расскажу, как это работает по частям:
Первая заметная вещь заключается в том, что весь макрос заключен в
do while(0)
блок. Это распространенная идиома разрешающая нормальное использование точки с запятой после макроса.Следующим является использование переменной с именем в
REVERSE_BYTES
качествеfor
счетчика цикла в. Имя самого макроса используется в качестве имени переменной, чтобы гарантировать, что он не конфликтует с любыми другими символами, которые могут находиться в области видимости везде, где используется макрос. Поскольку имя используется в расширении макроса, оно не будет расширено снова при использовании здесь в качестве имени переменной.Внутри
for
цикла есть два байта, на которые ссылаются и которые меняются местами XOR (поэтому временное имя переменной не требуется):__VA_ARGS__
представляет все, что было дано макросу, и используется для увеличения гибкости того, что может быть передано (хотя и ненамного). Адрес этого аргумента затем берется и приводится кunsigned char
указателю, чтобы разрешить обмен его байтов посредством[]
подписки массива .Последний специфический момент - отсутствие
{}
скобок. В них нет необходимости, поскольку все шаги в каждом свопе объединяются с помощью оператора запятой , что делает их одним оператором.Наконец, стоит отметить, что это не идеальный подход, если скорость является главным приоритетом. Если это важный фактор, некоторые из макросов, специфичных для типа, или директив, специфичных для платформы, на которые есть ссылки в других ответах, вероятно, являются лучшим вариантом. Этот подход, однако, переносим для всех типов, всех основных платформ и языков C и C ++.
источник
__VA_ARGS__
?Вау, я не мог поверить, что некоторые ответы я прочитал здесь. На самом деле есть инструкция по сборке, которая делает это быстрее, чем что-либо еще. BSWAP. Вы можете просто написать такую функцию ...
Это НАМНОГО быстрее, чем те, которые были предложены. Я их разобрал и посмотрел. Вышеупомянутая функция не имеет пролога / эпилога, поэтому практически не имеет накладных расходов вообще.
Делать 16 бит так же просто, за исключением того, что вы использовали бы xchg al, ах. bswap работает только на 32-битных регистрах.
64-битная версия немного сложнее, но не слишком. Гораздо лучше, чем все приведенные выше примеры с циклами, шаблонами и т. Д.
Здесь есть несколько предостережений ... Во-первых, bswap доступен только для процессоров 80x486 и выше. Кто-нибудь планирует запустить его на 386?!? Если это так, вы все равно можете заменить bswap на ...
Также встроенная сборка доступна только в коде x86 в Visual Studio. Голая функция не может быть выровнена и также не доступна в сборках x64. В этом случае вам придется использовать встроенные функции компилятора.
источник
_byteswap_ulong
и_uint64
(например, в принятом ответе) оба компилируют, чтобы использоватьbswap
инструкцию. Я был бы удивлен, но интересно узнать, настолько ли быстрее этот асм, поскольку он пропускает только пролог / эпилог - вы его тестировали?Портативная технология для реализации дружественных оптимизаторам невыровненных порядковых операций доступа не по месту. Они работают на каждом компиляторе, каждом выравнивании границ и каждом порядке байтов. Эти невыровненные подпрограммы дополняются или обсуждаются в зависимости от собственного порядка байтов и выравнивания. Частичное перечисление, но вы поняли идею. BO * - это постоянные значения, основанные на собственном порядке байтов.
Эти typedefs имеют преимущество в том, что выдают ошибки компилятора, если они не используются с аксессорами, таким образом уменьшая забытые ошибки аксессора.
источник
Ниже описано, как читать двойные данные, хранящиеся в 64-битном формате IEEE 754, даже если ваш хост-компьютер использует другую систему.
Для остальной части набора функций, включая процедуры записи и целочисленного типа, смотрите мой проект на github.
https://github.com/MalcolmMcLean/ieee754
источник
Замена байтов трюком ye olde 3-step-xor вокруг центра в функции шаблона дает гибкое, быстрое решение O (ln2), которое не требует библиотеки, стиль здесь также отклоняет типы 1 байта:
источник
Похоже, безопасным способом было бы использовать htons для каждого слова. Итак, если у вас есть ...
Выше было бы не работать, если бы вы работали в системе с прямым порядком байтов, поэтому я бы искал то, что ваша платформа использует в качестве условия времени компиляции, чтобы решить, является ли htons запретом. В конце концов, это O (n). На Mac это было бы что-то вроде ...
источник
Если у вас есть C ++ 17, добавьте этот заголовок
Используйте эту функцию шаблона, чтобы поменять местами байты:
назвать это как:
источник
Посмотрите на сдвиг битов, так как это в основном все, что вам нужно сделать, чтобы поменять местами маленькие -> большие порядковые номера. Затем, в зависимости от размера бита, вы меняете способ сдвига бита.
источник