Подтвердить что ты не робот

Обнаружение конечности

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

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

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

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

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

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

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

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

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

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

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

Я бы предпочел избежать этого, но, похоже, это уменьшает надежду...

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

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

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

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

4b9b3361

Ответ 1

Во время компиляции в 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 ранее в соответствии с вашими проверками проверки препроцессора)

Ответ 2

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

Ответ 3

Как было сказано ранее, единственным "реальным" способом обнаружения 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. Привязать все это вместе, и у вас есть довольно хороший шанс обнаружить большие системы endian и powerpc, в частности, независимо от компилятора и его версии.

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

Ответ 4

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

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

bool IsLittleEndian () {

    int i=1;

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

}

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

Это происходит из 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;
}

Ответ 8

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

Ответ 9

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

Ответ 10

ntohl можно использовать функцию 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;
}

Ответ 11

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

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

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

Ответ 12

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

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

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

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

union { unsigned u; unsigned char c[sizeof (unsigned)]; }

Ответ 13

#define BIG_ENDIAN ((1 >> 1 == 0) ? 0 : 1)