Обнаружение Endianness - PullRequest
       54

Обнаружение Endianness

17 голосов
/ 24 января 2012

В настоящее время я пытаюсь создать исходный код C , который правильно обрабатывает ввод / вывод независимо от порядкового номера целевой системы.

Я выбрал "little endian" в качестве моегоСоглашение о вводе / выводе, которое означает, что для процессоров с прямым порядком байтов мне нужно преобразовывать данные во время записи или чтения.

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

До сих пор я использовал это:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
...
#else
...
#endif

Он задокументирован как предопределенный макрос GCC, и Visual, похоже, тоже это понимает.

Однако я получил сообщение о том, что проверка не выполнена для некоторых систем big_endian (PowerPC).

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

[Редактировать]: Большинство предлагаемых решений основаны на "тестах во время выполнения".Эти тесты иногда могут быть надлежащим образом оценены компиляторами во время компиляции, и, следовательно, не требуют реальной производительности во время выполнения.

Однако ветвления с некоторой разновидностью << <code>if (0) { ... } else { ... } >> недостаточно.В текущей реализации кода переменная и функции объявление зависят от обнаружения big_endian.Они не могут быть изменены с помощью оператора if.

Ну, очевидно, существует запасной план, который заключается в переписывании кода ...

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

[Правка 2]: Я проверил "тесты во время выполнения", глубоко изменив код.Хотя они и делают свою работу правильно, эти тесты также влияют на производительность.

Я ожидал, что, поскольку тесты имеют предсказуемый результат, компилятор может устранить плохие ветви.Но, к сожалению, это не работает все время.MSVC - хороший компилятор, и он успешно устраняет плохие ветки, но GCC дает неоднозначные результаты, в зависимости от версий, типа тестов и с большим влиянием на 64-битные, чем на 32-битные.

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

Edit 3 : В настоящее время я использую константу объединения времени компиляции,ожидая, что компилятор решит это с явным сигналом да / нет.И это работает довольно хорошо: https://godbolt.org/g/DAafKo

Ответы [ 14 ]

14 голосов
/ 24 января 2012

Вместо того, чтобы искать проверку во время компиляции, почему бы просто не использовать порядок с прямым порядком байтов (который многие считают "сетевым порядком" ) и использовать htons / htonl / ntohs / ntohl функций, предоставляемых большинством UNIX-систем и Windows. Они уже определены, чтобы делать работу, которую вы пытаетесь сделать. Зачем изобретать велосипед?

14 голосов
/ 06 февраля 2012

Как указывалось ранее, единственный "реальный" способ обнаружить Big Endian - это использовать тесты во время выполнения.

Однако иногда макрос может быть предпочтительнее.

К сожалению, я не нашел ни одного "теста" для обнаружения этой ситуации, а скорее их набор.

Например, GCC рекомендует: __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__. Однако это работает только с последними версиями, и более ранние версии (и другие компиляторы) придают этому тесту ложное значение "true", поскольку NULL == NULL. Итак, вам нужна более полная версия: defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)

Хорошо, теперь это работает для новейших GCC, но как насчет других компиляторов?

Вы можете попробовать __BIG_ENDIAN__ или __BIG_ENDIAN или _BIG_ENDIAN, которые часто определяются в компиляторах с прямым порядком байтов.

Это улучшит обнаружение. Но если вы специально нацелены на платформы PowerPC, вы можете добавить еще несколько тестов, чтобы улучшить обнаружение. Попробуйте _ARCH_PPC или __PPC__ или __PPC, или PPC, или __powerpc__, или __powerpc, или даже powerpc. Свяжите все эти определения вместе, и у вас будет достаточно шансов обнаружить системы с прямым порядком байтов, в частности, powerpc, независимо от компилятора и его версии.

Итак, подведем итог: не существует такой вещи, как «стандартные предопределенные макросы», которая гарантирует обнаружение процессоров с прямым порядком байтов на всех платформах и компиляторах, но есть много таких предопределенных макросов, которые в совокупности дают высокая вероятность правильного определения порядка байтов в большинстве случаев.

11 голосов
/ 24 января 2012

Во время компиляции в C вы не можете сделать гораздо больше, чем доверять препроцессору #define s, и не существует стандартных решений, потому что стандарт C не связан с порядком байтов.

Тем не менее, вы могли быдобавьте утверждение, которое делается во время выполнения в начале программы, чтобы убедиться, что предположение, сделанное при компиляции, было верным:

inline int IsBigEndian()
{
    int i=1;
    return ! *((char *)&i);
}

/* ... */

#ifdef COMPILED_FOR_BIG_ENDIAN
assert(IsBigEndian());
#elif COMPILED_FOR_LITTLE_ENDIAN
assert(!IsBigEndian());
#else
#error "No endianness macro defined"
#endif

(где COMPILED_FOR_BIG_ENDIAN и COMPILED_FOR_LITTLE_ENDIAN - макросы #define dранее в соответствии с вашими проверками порядка байтов препроцессора)

6 голосов
/ 24 января 2012

Попробуйте что-то вроде:

if(*(char *)(int[]){1}) {
    /* little endian code */
} else {
    /* big endian code */
}

и посмотрите, разрешит ли ваш компилятор это во время компиляции.Если нет, возможно, вам повезет, если вы сделаете то же самое с профсоюзом.На самом деле мне нравится определять макросы с помощью объединений, которые оцениваются в 0,1 или 1,0 (соответственно), чтобы я мог просто делать такие вещи, как доступ к buf[HI] и buf[LO].

4 голосов
/ 24 января 2012

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

Вотфункция, которая делает именно это:

bool IsLittleEndian () {

    int i=1;

    return (int)*((unsigned char *)&i)==1;

}
3 голосов
/ 24 января 2012

Как уже отмечали другие, не существует переносимого способа проверки порядка байтов во время компиляции. Однако одним из вариантов будет использование инструмента autoconf как части вашего скрипта сборки для определения, является ли система с прямым порядком байтов или прямым порядком байтов, а затем использование макроса AC_C_BIGENDIAN, который содержит эту информацию. В некотором смысле это создает программу, которая во время выполнения обнаруживает, является ли система прямым или прямым порядком байтов, а затем имеет выходную информацию этой программы, которая затем может статически использоваться основным исходным кодом.

Надеюсь, это поможет!

2 голосов
/ 15 июня 2017

Это прибывает из p.45 из Указатели в C :

#include <stdio.h>
#define BIG_ENDIAN 0
#define LITTLE_ENDIAN 1

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

int main(int argc, char* argv[])
{
   int value;
   value = endian();
   if (value == 1)
      printf("The machine is Little Endian\n");
   else
      printf("The machine is Big Endian\n");
   return 0;
}
1 голос
/ 06 августа 2018

Сокет ntohl функция может быть использована для этой цели. Источник

// Soner
#include <stdio.h>
#include <arpa/inet.h>


int main() {
    if (ntohl(0x12345678) == 0x12345678) {
        printf("big-endian\n");
    } else if (ntohl(0x12345678) == 0x78563412) {
        printf("little-endian\n");
    } else {
        printf("(stupid)-middle-endian\n");
    }
    return 0;
}
1 голос
/ 24 января 2012

Невозможно определить порядок байтов в C с помощью директив препроцессора.

1 голос
/ 24 января 2012

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

...