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

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

Ответы [ 29 ]

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

Если вы не используете платформу, которая была портирована на процессоры PPC и Intel, вам придется выполнять условные компиляции, поскольку платформы PPC и Intel имеют совершенно разные аппаратные архитектуры, конвейеры, шины и т. Д. Это отрисовывает код сборки совершенно разные между ними.

Что касается нахождения порядка байтов, сделайте следующее:

short temp = 0x1234;
char* tempChar = (char*)&temp;

Вы получите либо tempChar равным 0x12, либо 0x34, из которого вы будете знать порядок байтов.

5 голосов
/ 13 октября 2012

Как указано выше, используйте приемы объединения.

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

Поскольку простой тест с порядком байтов скучен, здесь идет функция (шаблон), которая переворачивает ввод / вывод произвольного целого числа в соответствии с вашей спецификацией, независимо от архитектуры хоста.

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
T endian(T w, uint32_t endian)
{
    // this gets optimized out into if (endian == host_endian) return w;
    union { uint64_t quad; uint32_t islittle; } t;
    t.quad = 1;
    if (t.islittle ^ endian) return w;
    T r = 0;

    // decent compilers will unroll this (gcc)
    // or even convert straight into single bswap (clang)
    for (int i = 0; i < sizeof(r); i++) {
        r <<= 8;
        r |= w & 0xff;
        w >>= 8;
    }
    return r;
};

Использование:

Для преобразования из данного порядкового номера в хост используйте:

host = endian(source, endian_of_source)

Чтобы преобразовать порядковый номер узла в указанный, используйте:

output = endian(hostsource, endian_you_want_to_output)

Результирующий код работает так же быстро, как при написании сборки вручную на clang, на gcc он немного медленнее (развернутый &, <<, >>, | для каждого байта), но все еще приличный.

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

Я бы сделал что-то вроде этого:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

Вдоль этих строк вы получите эффективную по времени функцию, которая выполняет вычисления только один раз.

4 голосов
/ 25 ноября 2012
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
3 голосов
/ 02 октября 2012
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

Это еще одно решение. Аналогично решению Эндрю Хэра.

3 голосов
/ 14 февраля 2015

не проверено, но на мой взгляд, это должно работать? потому что это будет 0x01 для байтов с прямым порядком байтов и 0x00 для байтов с обратным порядком байтов?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
3 голосов
/ 21 мая 2014

Declare:

Мой первоначальный пост ошибочно объявлен как "время компиляции". Это не так, это даже невозможно в текущем стандарте C ++. Constexpr НЕ означает, что функция всегда выполняет вычисления во время компиляции. Спасибо Ричарду Ходжесу за исправление.

время компиляции, не-макрос, решение Ctex 11 constexpr:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
2 голосов
/ 16 июня 2009

Вы также можете сделать это через препроцессор, используя что-то вроде файла заголовка boost, который можно найти boost endian

2 голосов
/ 17 февраля 2017

Если вам не нужна условная компиляция, вы можете просто написать независимый от байтов код. Вот пример (взят из Роб Пайк ):

Чтение целого числа, хранящегося в порядке с прямым порядком байтов на диске, в независимом порядке:

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Тот же код, пытающийся учесть машинный порядковый номер:

i = *((int*)data);
#ifdef BIG_ENDIAN
/* swap the bytes */
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif
1 голос
/ 18 апреля 2015

Если заголовок с порядком байтов не только для GCC, он предоставляет макросы, которые вы можете использовать.

#include "endian.h"
...
if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
...
...