Обнаружение порядка байтов программно в программе на C ++ - PullRequest
194 голосов
/ 16 июня 2009

Есть ли программный способ определить, используете ли вы архитектуру с прямым или прямым порядком байтов? Мне нужно иметь возможность писать код, который будет выполняться в системе Intel или PPC, и использовать точно такой же код (т.е. без условной компиляции).

Ответы [ 29 ]

166 голосов
/ 16 июня 2009

Мне не нравится метод, основанный на типе punning - его часто предупреждает компилятор. Именно для этого нужны профсоюзы!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

Этот принцип эквивалентен регистру типов, как было предложено другими, но это более ясно - и в соответствии с C99 гарантированно будет правильным. GCC предпочитает это по сравнению с прямым указателем.

Это также намного лучше, чем исправление порядка байтов во время компиляции - для ОС, которые поддерживают мульти-архитектуру (например, толстый бинарный файл на Mac OS X), это будет работать как для ppc / i386, так как очень легко запутаться все иначе.

80 голосов
/ 16 июня 2009

Вы можете сделать это, установив int и маскируя биты, но, вероятно, самый простой способ - это просто использовать встроенные операции преобразования сетевых байтов (поскольку сетевой порядок байтов всегда имеет старший порядковый номер).

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

Переплетение битов может быть быстрее, но этот способ прост, понятен и его практически невозможно испортить.

60 голосов
/ 16 июня 2009

Пожалуйста, смотрите эту статью :

Вот код, чтобы определить, что тип вашей машины

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
33 голосов
/ 16 июня 2009

Обычно это делается во время компиляции (особенно по соображениям производительности), используя файлы заголовков, доступные из компилятора, или создавайте свои собственные. В Linux у вас есть заголовочный файл "/usr/include/endian.h"

32 голосов
/ 01 июля 2016

Вы можете использовать std::endian, если у вас есть доступ к компилятору C ++ 20, например GCC 8+ или Clang 7 +:

#include <type_traits>

if constexpr (std::endian::native == std::endian::big)
{
    // Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little endian system
}
else
{
    // Something else
}
15 голосов
/ 04 мая 2011

Эмм ... Меня удивляет, что никто не понял, что компилятор просто оптимизирует тест и поместит фиксированный результат в качестве возвращаемого значения. Это делает все приведенные выше примеры кода практически бесполезными. Единственное, что будет возвращено - это порядок байтов во время компиляции! И да, я проверил все приведенные выше примеры. Вот пример с MSVC 9.0 (Visual Studio 2008).

Код чистого C

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

Демонтажные

PUBLIC  _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
;   COMDAT _DNA_GetEndianness
_TEXT   SEGMENT
_DNA_GetEndianness PROC                 ; COMDAT

; 11   :     union 
; 12   :     {
; 13   :         uint8  c[4];
; 14   :         uint32 i;
; 15   :     } u;
; 16   : 
; 17   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

Возможно, возможно отключить ЛЮБУЮ оптимизацию во время компиляции только для этой функции, но я не знаю. В противном случае это может быть возможно жестко закодировать в сборке, хотя это не переносимо. И даже тогда даже это может быть оптимизировано. Это заставляет меня думать, что мне нужен какой-то действительно дерьмовый ассемблер, реализовывать один и тот же код для всех существующих процессоров / наборов команд, и, ну ... неважно.

Кроме того, кто-то здесь сказал, что порядок байтов не изменяется во время выполнения. НЕПРАВИЛЬНО. Есть машины с прямым порядком байтов. Их порядок может варьироваться в процессе исполнения. ТАКЖЕ, есть не только Little Endian и Big Endian, но и другие порядки байтов (что за слово).

Я ненавижу и люблю кодировать одновременно ...

14 голосов
/ 20 июня 2009

Я удивился, что никто не упомянул макросы, которые препроцессор определяет по умолчанию. Хотя они будут варьироваться в зависимости от вашей платформы; они намного чище, чем необходимость написания собственного чека с порядком байтов.

Например; если мы посмотрим на встроенные макросы, которые определяет GCC (на компьютере с архитектурой X86-64):

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

На машине КПП я получаю:

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

(Волшебство :| gcc -dM -E -x c - распечатывает все встроенные макросы).

14 голосов
/ 16 июня 2009

Объявите переменную int:

int variable = 0xFF;

Теперь используйте указатели char * на различные его части и проверяйте, что находится в этих частях.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

В зависимости от того, какой из них указывает на байт 0xFF, теперь вы можете определить порядок байтов Для этого требуется sizeof (int)> sizeof (char), но это определенно верно для обсуждаемых платформ.

8 голосов
/ 16 июня 2009

Для получения более подробной информации, вы можете проверить эту статью кода проекта Основные понятия по Endianness :

Как динамически проверить тип Endian во время выполнения?

Как объяснено в компьютере FAQ по анимации, вы можете использовать следующая функция, чтобы увидеть, если ваш код работает на Little- или Big-Endian Система: Свернуть

#define BIG_ENDIAN      0
#define LITTLE_ENDIAN   1
int TestByteOrder()
{
   short int word = 0x0001;
   char *byte = (char *) &word;
   return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
}

Этот код присваивает значение 0001h 16-битное целое число Указатель на символ тогда назначено указать на первый (наименее значимый) байт целочисленное значение. Если первый байт целое число 0x01h, то система Little-Endian (0x01h находится в самый низкий или наименее значимый, адрес). Если это 0x00h, то система является Big-Endian.

6 голосов
/ 11 сентября 2015

C ++ использует boost , где проверки и приведения препроцессора разделяются внутри очень тщательно протестированных библиотек.

Библиотека Predef (boost / prefn.h) распознает четыре различных вида порядка байтов .

Библиотеку Endian планировалось передать в стандарт C ++, и она поддерживает широкий спектр операций с данными, чувствительными к порядку байтов.

Как указано в ответах выше, Endianness будет частью c ++ 20.

...