Определение порядка следования байтов во время компиляции



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



[редактирование]
Спасибо за ответы, я решил придерживаться решения времени выполнения!

582   10  

10 ответов:

это для проверки времени компиляции

вы можете использовать информацию из файла заголовка boost endian.hpp, который охватывает множество платформ.

редактировать для проверки во время выполнения

bool isLittleEndian()
{
    short int number = 0x1;
    char *numPtr = (char*)&number;
    return (numPtr[0] == 1);
}

создайте целое число и прочитайте его первый байт (наименее значимый байт). Если этот байт равен 1, то система является обратным порядком байтов, в противном случае это большой endian.

редактировать думал о

да вы можете столкнуться с потенциальной проблемой на некоторых платформах (не могу придумать), где sizeof(char) == sizeof(short int). Вы можете использовать многобайтовые интегральные типы фиксированной ширины, доступные в <stdint.h>, или если ваша платформа не имеет его, снова вы можете адаптировать заголовок boost для вашего использования:stdint.hpp

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

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

#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
    defined(__BIG_ENDIAN__) || \
    defined(__ARMEB__) || \
    defined(__THUMBEB__) || \
    defined(__AARCH64EB__) || \
    defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
// It's a big-endian target architecture
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
    defined(__LITTLE_ENDIAN__) || \
    defined(__ARMEL__) || \
    defined(__THUMBEL__) || \
    defined(__AARCH64EL__) || \
    defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
// It's a little-endian target architecture
#else
#error "I don't know what architecture this is!"
#endif

если вы не можете найти, какие предопределенные макросы использует ваш компилятор из своей документации, вы также можете попробовать заставить его выплюнуть свой полный список предопределенных макросов и угадать оттуда, что будет работать (ищите что-нибудь с именем ENDIAN, ORDER или Processor architecture в нем). на этой странице перечисляет ряд методов для ведения что в разных компиляторах:

Compiler                   C macros                         C++ macros
Clang/LLVM                 clang -dM -E -x c /dev/null      clang++ -dM -E -x c++ /dev/null
GNU GCC/G++                gcc   -dM -E -x c /dev/null      g++     -dM -E -x c++ /dev/null
Hewlett-Packard C/aC++     cc    -dM -E -x c /dev/null      aCC     -dM -E -x c++ /dev/null
IBM XL C/C++               xlc   -qshowmacros -E /dev/null  xlc++   -qshowmacros -E /dev/null
Intel ICC/ICPC             icc   -dM -E -x c /dev/null      icpc    -dM -E -x c++ /dev/null
Microsoft Visual Studio (none)                              (none)
Oracle Solaris Studio      cc    -xdumpmacros -E /dev/null  CC      -xdumpmacros -E /dev/null
Portland Group PGCC/PGCPP  pgcc  -dM -E                     (none)

наконец, чтобы завершить его, компиляторы Microsoft Visual C / C++ являются нечетными и не имеют ничего из вышеперечисленного. К счастью, они задокументировали свои предопределенные макросы здесь, и вы можете использовать целевую архитектуру процессора для вывода endianness. В то время как все поддерживаемые в настоящее время процессоры в Windows являются мало-endian (_M_IX86,_M_X64,_M_IA64 и _M_ARM мало-endian), некоторые исторически поддерживаемые процессоры, такие как PowerPC (_M_PPC) были тупоконечников. Но более уместно, что Xbox 360-это машина PowerPC с большим концом, поэтому, если вы пишете кросс-платформенный заголовок библиотеки, не помешает проверить _M_PPC.

С помощью C99 вы можете выполнить проверку как:

#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)

условные конструкции типа if (I_AM_LITTLE) будет вычисляться во время компиляции и позволить компилятору оптимизировать целые кварталы.

у меня нет ссылки сразу на то, является ли это строго говоря константное выражение в C99 (что позволит использовать его в инициализаторах для данных статического хранения), но если нет, то это следующая лучшая вещь.

интересно читать от C FAQ:

Вы, наверное, не могу. Обычные методики для выявления байтов задействуйте указатели или массивы символов, или, может быть, объединения, но препроцессор арифметика использует только длинные целые числа, и нет понятия адресация. Еще одна заманчивая возможность-это что-то вроде

  #if 'ABCD' == 0x41424344

но это тоже ненадежно.

Я хотел бы расширить ответы на предоставление

Не во время компиляции, но, возможно, во время выполнения. Вот функция C, которую я написал, чтобы определить endianness:

/*  Returns 1 if LITTLE-ENDIAN or 0 if BIG-ENDIAN  */
#include <inttypes.h>
int endianness()
{
  union { uint8_t c[4]; uint32_t i; } data;
  data.i = 0x12345678;
  return (data.c[0] == 0x78);
}

С наконец, в одну линию обнаружения байтов в C препроцессор:

#include <stdint.h>

#define IS_BIG_ENDIAN (*(uint16_t *)"\xff" < 0x100)

любой приличный оптимизатор разрешит это во время компиляции. НКУ не менее!--1-->.

конечно stdint.h это C99. Для переносимости ANSI/C89 см. Doug Gwyn's Мгновенный C9x библиотека.

однажды я использовал такую конструкцию:

uint16_t  HI_BYTE  = 0,
          LO_BYTE  = 1;
uint16_t  s = 1;

if(*(uint8_t *) &s == 1) {   
   HI_BYTE = 1;
   LO_BYTE = 0;
} 

pByte[HI_BYTE] = 0x10;
pByte[LO_BYTE] = 0x20;

gcc с -O2 удалось сделать его полностью время компиляции. Это значит, что HI_BYTE и LO_BYTE переменные были полностью заменены, и даже доступ к pByte был заменен в ассемблере эквивалентом *(unit16_t *pByte) = 0x1020;.

это как время компиляции, как он получает.

насколько мне известно, нет, не во время компиляции.

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

typedef union {
    uint32_t word;
    uint8_t bytes[4];
} byte_check;

или литья,

uint32_t word;
uint8_t * bytes = &word;

обратите внимание, что для полностью переносимых проверок endianness вам необходимо учитывать как большие, так и малые системы endian и mixed-endian.

EDIT2: этот метод не работает. Представление многобайтовой константы является специфичным для компилятора / платформы и не может быть надежно использовано. Ссылка interjay дала (http://www.ideone.com/LaKpj) дает пример, где он терпит неудачу. На Solaris / SPARC тот же компилятор gcc 4.3.3 дает правильный ответ, но компилятор SUNStudio 12 будет иметь то же поведение, что и gcc 4.3.4 на x86, используемый в этой ссылке.

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

Нашел этот новый метод, который имеет то преимущество, что он прост и время компиляции.

switch('AB') {
  case 0x4142: printf("ASCII  Big endian\n"); break;
  case 0x4241: printf("ASCII  Little endian\n"); break;
  case 0xC1C2: printf("EBCDIC Big endian\n"); break;
  case 0xC2C1: printf("EBCDIC Little endian\n"); break;
}

Редактировать:

нашел даже способ сделать это в предпроцессоре:

#if 'AB' == 0x4142
#error "ASCII  Big endian\n"
#elif 'AB' == 0x4241
#error "ASCII  Little endian\n"
#elif 'AB' == 0xC1C2
#error "EBCDIC Big endian\n"
#elif 'AB' == 0xC2C1
#error "EBCDIC Little endian\n"
#else
#error "unknown coding and endianness\n"
#endif

и прежде чем кто-то спросит, многобайтовые символьные константы являются ANSI-C (даже C90), но определены реализации. Вот первое полезное приложение, которое я нашел для них.

Comments

    Ничего не найдено.