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

Обнаружение 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 ]

0 голосов
/ 28 мая 2019

Как уже упоминалось, время компиляции - ваш лучший выбор. Предполагая, что вы не выполняете кросс-компиляцию и используете cmake (он также будет работать с другими инструментами, такими как скрипт configure, конечно), тогда вы можете использовать предварительное тестирование, которое представляет собой скомпилированный файл .c или .cpp и это дает вам фактическую подтвержденную последовательность процессора, на котором вы работаете.

С cmake вы используете макрос TestBigEndian. Он устанавливает переменную, которую вы можете затем передать вашему программному обеспечению. Примерно так (не проверено):

TestBigEndian(IS_BIG_ENDIAN)
...
set(CFLAGS ${CFLAGS} -DIS_BIG_ENDIAN=${IS_BIG_ENDIAN}) // C
set(CXXFLAGS ${CXXFLAGS} -DIS_BIG_ENDIAN=${IS_BIG_ENDIAN}) // C++

Затем в своем коде C / C ++ вы можете проверить, что IS_BIG_ENDIAN определяет:

#if IS_BIG_ENDIAN
    ...do big endian stuff here...
#else
    ...do little endian stuff here...
#endif

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

0 голосов
/ 01 февраля 2019

Я позволил себе переформатировать цитируемый текст

По состоянию на 2017-07-18 я использую union { unsigned u; unsigned char c[4]; }

Если sizeof (unsigned) != 4, ваш тест может не пройти.

Может быть лучше использовать

union { unsigned u; unsigned char c[sizeof (unsigned)]; }
0 голосов
/ 18 июля 2017

Я знаю, что опоздал на эту вечеринку, но вот мое мнение.

int is_big_endian() {
    return 1 & *(uint16_t*)"01";
}

Это основано на том факте, что '0' - это 48 в десятичном виде и '1' 49, поэтому '1' имеет установленный бит LSB, а '0' - нет. Я мог бы сделать их '\x00' и '\x01', но я думаю, что моя версия делает его более читабельным.

0 голосов
/ 24 января 2012
#define BIG_ENDIAN ((1 >> 1 == 0) ? 0 : 1)
...