Обнаружение порядка байтов программно в программе на C ++

211

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

Джей Т
источник
4
Для полноты, вот ссылка на чей-то вопрос о попытке измерения порядка байтов (во время компиляции): stackoverflow.com/questions/280162/…
Faisal Vali
14
Почему бы не определить порядок байтов во время компиляции? Это не может измениться во время выполнения.
Эфимент
3
AFAIK, нет надежного и универсального способа сделать это. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Ответы:

174

Мне не нравится метод, основанный на типе punning - его часто предупреждает компилятор. Именно для этого нужны профсоюзы!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

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

Это также намного лучше, чем исправление порядка байтов во время компиляции - для ОС, которые поддерживают мульти-архитектуру (например, толстый бинарный файл на Mac OS X), это будет работать как для ppc / i386, так как в противном случае очень легко все испортить ,

Дэвид Курнапо
источник
51
Я не рекомендую называть переменную "bint" :)
mkb
42
Вы уверены, что это хорошо определено? В C ++ только один член объединения может быть активным одновременно - то есть вы не можете назначать, используя одно имя члена, и читать, используя другое (хотя есть исключение для структур, совместимых с макетом)
Фейсал Вали
27
@Matt: я заглянул в Google, и bint, кажется, имеет значение на английском языке, о котором я не знал :)
David Cournapeau
17
Я проверял это, и в gcc 4.0.1 и в gcc 4.4.1 результат этой функции может быть определен во время компиляции и рассматриваться как константа. Это означает, что компилятор отключится, если ветви будут зависеть исключительно от результата этой функции и никогда не будут приняты на рассматриваемой платформе. Это, вероятно, не так для многих реализаций htonl.
Всемогущий
6
Это решение действительно портативное? Что если CHAR_BIT != 8?
зоргит
80

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

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

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

Эрик Петроэль
источник
1
Операции преобразования сети также можно использовать для преобразования всего в порядок с прямым порядком байтов, что позволяет решить другие проблемы, с которыми может столкнуться Джей.
Брайан
6
@sharptooth - медленный относительный термин, но да, если скорость действительно является проблемой, используйте ее один раз в начале программы и установите глобальную переменную с порядком байтов.
Эрик Петроэле
5
У htonl есть еще одна проблема: на некоторых платформах (windows?) он находится не в самой библиотеке времени выполнения C, а в дополнительных сетевых библиотеках (сокет и т. д.). Это довольно серьезная помеха только для одной функции, если вам не нужна библиотека в противном случае.
Дэвид Курнапо
7
Обратите внимание, что в Linux (gcc) htonl подвергается постоянному свертыванию во время компиляции, поэтому выражение этой формы вообще не имеет накладных расходов времени выполнения (т. Е. Оно постоянно сворачивается в 1 или 0, а затем удаление мертвого кода удаляет другая ветвь если)
bdonlan
2
Кроме того, в x86 htonl может быть (и в Linux / gcc) реализован очень эффективно с использованием встроенного ассемблера, особенно если вы нацелены на микроархитектуру с поддержкой этой BSWAPоперации.
bdonlan
61

Пожалуйста, смотрите эту статью :

Вот некоторый код, чтобы определить, какой тип вашей машины

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
Эндрю Хэйр
источник
25
Имейте в виду, что это зависит от разной длины int и char, что почти всегда так, но не гарантируется.
Дэвид Торнли
10
Я работал над встроенными системами, где short int и char были одинакового размера ... Я не могу вспомнить, был ли обычный int тоже такого размера (2 байта) или нет.
rmeador
2
почему ЭТОТ ответ в значительной степени ЕДИНСТВЕННЫЙ ОТВЕТ, который НЕ заставляет меня думать: «Чувак, что ты делаешь?», как и большинство ответов здесь: o
hanshenrik
2
@Shillard int должен быть по крайней мере таким большим, но в стандарте нет требования, чтобы char был ограничен меньшим! Если вы посмотрите на семейство TI F280x, вы обнаружите, что CHAR_BIT равен 16 и sizeof (int) == sizeof (char), в то время как указанные вами ограничения сохраняются абсолютно нормально ...
Aconcagua
5
Почему бы не использовать uint8_t и uint16_t?
Родриго
58

Вы можете использовать, std::endianесли у вас есть доступ к компилятору C ++ 20, например, GCC 8+ или Clang 7+.

Примечание: std::endianначалось , <type_traits>но был перенесен на <bit>на 2019 Cologne заседания. GCC 8, Clang 7, 8 и 9 имеют его, в <type_traits>то время как GCC 9+ и Clang 10+ имеют его <bit>.

#include <bit>

if constexpr (std::endian::native == std::endian::big)
{
    // Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little endian system
}
else
{
    // Something else
}
Lyberta
источник
5
Как и у всех, у меня есть доступ к черновикам / предложениям C ++ 17 и 20, но существует ли когда-либо какой-либо компилятор C ++ 20?
Xeverous
@ Xeverous Для этого требуются только перечисления в пределах области видимости, поэтому я подозреваю, что большинство поставщиков добавят его в свою реализацию stdlib как одно из своих предыдущих изменений.
Pharap
@Xeverous GCC 8 был выпущен и поддерживает его.
Либерта
Из 30+ ответов на вопрос, это, кажется, единственный, который является абсолютно точным (с другим ответом, который, по крайней мере, правильно).
IInspectable
40

Обычно это делается во время компиляции (особенно по соображениям производительности), используя файлы заголовков, доступные из компилятора, или создавайте свои собственные. В Linux у вас есть заголовочный файл "/usr/include/endian.h"

законопроект
источник
8
Я не могу поверить, что за это не проголосовали выше. Это не значит, что в скомпилированной программе порядок байтов изменится, поэтому нет необходимости в тесте во время выполнения.
Dolda2000
@ Dolda2000 Потенциально мог видеть режимы байтов ARM.
Tyzoid
10
@ Tyzoid: Нет, скомпилированная программа всегда будет работать в том порядке, в котором она была скомпилирована, даже если процессор способен на это.
Dolda2000
16

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

Например; если мы посмотрим на встроенные макросы, которые определяет GCC (на машине X86-64):

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

На машине КПП я получаю:

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

( :| gcc -dM -E -x c -Волшебство распечатывает все встроенные макросы).

Daver
источник
7
Эти макросы не отображаются последовательно вообще. Например, в gcc 4.4.5 из репозитория Redhat 6 запуск echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'ничего не возвращает, тогда как gcc 3.4.3 (в /usr/sfw/binлюбом случае) в Solaris имеет определение в этом направлении. Я видел похожие проблемы на VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).
Брайан Ванденберг
15

Эмм ... Меня удивляет, что никто не понял, что компилятор просто оптимизирует тест и поместит фиксированный результат в качестве возвращаемого значения. Это делает все приведенные выше примеры кода практически бесполезными. Единственное, что будет возвращено - это порядок байтов во время компиляции! И да, я проверил все приведенные выше примеры. Вот пример с MSVC 9.0 (Visual Studio 2008).

Чистый код C

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

разборка

PUBLIC  _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
;   COMDAT _DNA_GetEndianness
_TEXT   SEGMENT
_DNA_GetEndianness PROC                 ; COMDAT

; 11   :     union 
; 12   :     {
; 13   :         uint8  c[4];
; 14   :         uint32 i;
; 15   :     } u;
; 16   : 
; 17   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

Возможно, можно отключить ЛЮБУЮ оптимизацию во время компиляции только для этой функции, но я не знаю. В противном случае это может быть возможно жестко закодировать в сборке, хотя это не переносимо. И даже тогда даже это может быть оптимизировано. Это заставляет меня думать, что мне нужен какой-то действительно дерьмовый ассемблер, реализовать один и тот же код для всех существующих процессоров / наборов инструкций, и, ну ... неважно.

Кроме того, кто-то здесь сказал, что порядок байтов не изменяется во время выполнения. НЕПРАВИЛЬНО. Есть машины с прямым порядком байтов. Их порядок может меняться в процессе исполнения. ТАКЖЕ, есть не только Little Endian и Big Endian, но и другие порядки байтов (что за слово).

Я ненавижу и люблю кодировать одновременно ...

Coriiander
источник
11
Разве вам не нужно перекомпилировать для запуска на другой платформе в любом случае?
Бобобобо
2
Хотя он хорошо работает для MSVC, он не подходит для всех версий GCC при любых обстоятельствах. Следовательно, «проверка во время выполнения» внутри критического цикла может быть корректно неразветвленной во время компиляции или нет. Там нет 100% гарантии.
Голубой
21
Не существует такого понятия, как процессор x86 с прямым порядком байтов. Даже если вы запускаете Ubuntu на двухпроцессорном процессоре (например, ARM или MIPS), исполняемые файлы ELF всегда имеют большой (MSB) или маленький (LSB) порядковый номер. Невозможно создать исполняемые файлы Biendian, поэтому проверки во время выполнения не требуются.
Фейбл
4
Чтобы отключить оптимизацию в этом методе, используйте «volatile union ...». Это говорит компилятору, что «u» может быть изменено где-то еще, и данные должны быть загружены
mishmashru
1
Если эта функция возвращает другое значение во время выполнения, чем рассчитывает оптимизатор, это означает, что оптимизатор содержит ошибки. Вы говорите, что есть примеры скомпилированного оптимизированного бинарного кода, который может работать на двух разных архитектурах с разным порядком байтов, несмотря на очевидные предположения, сделанные оптимизатором (во всей программе) во время компиляции, которые могут показаться несовместимыми хотя бы с одним из них? архитектуры?
Скотт
13

Объявите переменную int:

int variable = 0xFF;

Теперь используйте char * указатели на различные его части и проверяйте, что находится в этих частях.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

В зависимости от того, какой из них указывает на байт 0xFF, теперь вы можете обнаружить порядок байтов. Это требует sizeof (int)> sizeof (char), но это определенно верно для обсуждаемых платформ.

Sharptooth
источник
8

Для получения более подробной информации, вы можете проверить эту статью codeproject Основные понятия о порядке байтов :

Как динамически тестировать тип Endian во время выполнения?

Как объясняется в разделе Часто задаваемые вопросы по компьютерной анимации, вы можете использовать следующую функцию, чтобы увидеть, работает ли ваш код в системе Little- или Big-Endian: Свернуть

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

Этот код присваивает значение 0001h 16-разрядному целому числу. Затем назначается указатель на символ, указывающий на первый (наименее значимый) байт целочисленного значения. Если первый байт целого числа равен 0x01h, то система имеет младший порядок байтов (0x01h находится в самом младшем или наименее значимом адресе). Если это 0x00h, то система является Big-Endian.

никто
источник
6

C ++ способ был использовать повышение , где препроцессорные проверки и приведения разделены на части внутри очень тщательно протестированных библиотек.

Библиотека Predef (boost / prefn.h) распознает четыре различных типа байтов .

Endian библиотека была запланирована для представления стандарта C ++, а также поддерживает широкий спектр операций на обратный порядок байт-чувствительных данных.

Как указано в ответах выше, Endianness будет частью c ++ 20.

оборота fuzzyTew
источник
1
К вашему сведению, связь с «четырьмя различными типами байтов» разорвана,
Реми Лебо,
исправлено и сделано вики
fuzzyTew
5

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

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

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

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

samoz
источник
3
Это основано на том, что short составляет ровно 2 байта, что не гарантируется.
sharptooth
3
Хотя это будет довольно безопасная ставка, основанная на двух архитектурах, приведенных в вопросе.
Daemin
8
Включите stdint.hи используйте int16_tв качестве доказательства будущего против короткого отличия на другой платформе.
Дениз Скидмор
4

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

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

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

Джереми Мэйхью
источник
Вы можете включить это? не уверен, что встроенные вызовут несколько блоков памяти статических переменных
aah134
4

Как указано выше, используйте трюки союза.

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

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

#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 он немного медленнее (развернутый &, <<, >>, | для каждого байта), но все еще приличный.

Kat
источник
4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
Паоло Брандоли
источник
1
Будет ли это эквивалентно? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Эмануэль
4

Не используйте union!

C ++ не разрешает пробивание типов через unions!
Чтение из поля объединения, которое не было последним записанным полем, является неопределенным поведением !
Многие компиляторы поддерживают это как расширения, но язык не дает никаких гарантий.

Смотрите этот ответ для более подробной информации:

https://stackoverflow.com/a/11996970


Есть только два правильных ответа, которые гарантированно будут переносимыми.

Первый ответ, если у вас есть доступ к системе, которая поддерживает C ++ 20,
это использовать std::endianиз <type_traits>заголовка.

(На момент написания C ++ 20 еще не был выпущен, но если что-то не влияет на std::endian на включение, это должно быть предпочтительным способом проверки порядка байтов во время компиляции начиная с C ++ 20 и далее.)

C ++ 20 г.в.

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

До C ++ 20 единственный верный ответ - хранить целое число, а затем проверять его первый байт через тип punning.
В отличие от использования unions, это явно разрешено системой типов C ++.

Также важно помнить, что для оптимальной переносимости static_castследует использовать,
потому что reinterpret_castреализация определена.

Если программа пытается получить доступ к сохраненному значению объекта через glvalue, отличный от одного из следующих типов, поведение не определено: ... a charили unsigned charтип.

C ++ 11 и далее

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

C ++ 11 и далее (без перечисления)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C ++ 98 / C ++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}
Pharap
источник
3
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");

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

Neeraj
источник
3

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

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
hanshenrik
источник
3

Объявляет:

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

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

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

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
zhaorufei
источник
2
Есть ли конкретная причина, по которой вы использовали unsigned char вместо uint8_t?
Кевин
0 времени выполнения ... мне нравится!
hanshenrik
Я полагаю, это обнаруживает нечеткие данные машины сборки, а не цели?
Hutorny
2
Разве это не UB в C ++?
29-16
6
это не законно в контексте constexpr. Вы не можете получить доступ к члену союза, который не был инициализирован напрямую. Нет никакого способа легально определить порядок байтов во время компиляции без магии препроцессора.
Ричард Ходжес
2

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


источник
1

Если заголовок endian не предназначен только для 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!");
...
Марк А. Либби
источник
Разве это не так __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__а __ORDER_BIG_ENDIAN__?
Xeverous
1

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

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

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
fjardon
источник
Какая хорошая идея! А теперь давайте перенесем ваши целые числа через сетевой сокет на неизвестное устройство.
Максим Ганенко
@MaksymGanenko Я не получил ваш комментарий. Это ирония? Я не предлагаю не указывать порядковый номер сериализованных данных. Я предлагаю не писать код, зависящий от порядкового номера машины, получающей данные.
Фьардон
@MaksymGanenko Если вы отрицаете, вы могли бы объяснить, почему ответ неправильный. Как минимум, чтобы помочь потенциальным читателям понять, почему они не должны следовать моему ответу.
Фьардон
0
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Джон Брайт
источник
0

Как насчет этого?

#include <cstdio>

int main()
{
    unsigned int n = 1;
    char *p = 0;

    p = (char*)&n;
    if (*p == 1)
        std::printf("Little Endian\n");
    else 
        if (*(p + sizeof(int) - 1) == 1)
            std::printf("Big Endian\n");
        else
            std::printf("What the crap?\n");
    return 0;
}
Абхай
источник
0

Вот еще одна версия C. Он определяет макрос, который вызывается wicked_cast()для вставки строкового типа через объединяющие литералы C99 и нестандартный __typeof__оператор.

#include <limits.h>

#if UCHAR_MAX == UINT_MAX
#error endianness irrelevant as sizeof(int) == 1
#endif

#define wicked_cast(TYPE, VALUE) \
    (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)

_Bool is_little_endian(void)
{
    return wicked_cast(unsigned char, 1u);
}

Если целые числа являются однобайтовыми значениями, порядок байтов не имеет смысла и будет сгенерирована ошибка времени компиляции.

Christoph
источник
0

То, как компиляторы C (по крайней мере, все, кого я знаю) работают с порядком байтов , должно быть решено во время компиляции. Даже для biendian процессоров (таких как ARM и MIPS) вы должны выбирать порядковый номер во время компиляции. Более того, порядок байтов определяется во всех распространенных форматах файлов для исполняемых файлов (таких как ELF). Несмотря на то, что можно создать двоичный двоичный код (возможно, для какого-нибудь эксплойта ARM-сервера?), Это, вероятно, должно быть сделано в сборке.

Fabel
источник
-1

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

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

#include <stdint.h>

int* _BE = 0;

int is_big_endian() {
    if (_BE == 0) {
        uint16_t* teste = (uint16_t*)malloc(4);
        *teste = (*teste & 0x01FE) | 0x0100;
        uint8_t teste2 = ((uint8_t*) teste)[0];
        free(teste);
        _BE = (int*)malloc(sizeof(int));
        *_BE = (0x01 == teste2);
    }
    return *_BE;
}

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

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

Текс Киллер
источник
Зачем выделять 4 байта для работы с 2-байтовым значением? Зачем маскировать неопределенное значение с помощью 0x7FE? Зачем malloc()вообще использовать ? это расточительно. И _BEесли (хотя и небольшая) утечка памяти и состояние гонки ожидают, то преимущества динамического кэширования результата не стоят проблем. Вместо этого я хотел бы сделать что-то более похожее на это: static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }просто и эффективно, и гораздо меньше работы для выполнения во время выполнения.
Реми Лебо
@RemyLebeau, весь смысл моего ответа заключался в создании кода, который не был оптимизирован компилятором. Конечно, ваш код намного проще, но с включенной оптимизацией он просто станет постоянным логическим значением после компиляции. Как я уже говорил в своем ответе, на самом деле я не знаю, есть ли какой-нибудь способ компилировать код C таким образом, чтобы один и тот же исполняемый файл выполнялся с обоими порядками байтов, и мне также было любопытно посмотреть, смогу ли я выполнить проверку во время выполнения несмотря на проводимую оптимизацию.
Убийца Текса
@TexKiller, тогда почему бы просто не отключить оптимизации для кода? Используя volatile, или #pragmaи т. Д.
Реми Лебо
@ RemyLebeau, я не знал этих ключевых слов в то время, и я просто воспринял это как небольшую проблему, чтобы предотвратить оптимизацию компилятора с помощью того, что я знал.
Текс убийца
-1

хотя не существует быстрого и стандартного способа определить его, он выведет его:

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 
yekanchi
источник
-1

См. Endianness - Иллюстрация кода уровня C.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS CheckArchEndianalityV1( void )
{
    int Endian = 0x00000001; // assuming target architecture is 32-bit    

    // as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least     Significant Byte) = 0x01
    // casting down to a single byte value LSB discarding higher bytes    

    return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
} 
гимель
источник
-2

Я изучал учебник: Компьютерная система: взгляд программиста , и есть проблема, чтобы определить, какой это порядковый номер в C-программе.

Я использовал функцию указателя, чтобы сделать это следующим образом:

#include <stdio.h>

int main(void){
    int i=1;
    unsigned char* ii = &i;

    printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
    return 0;
}

Поскольку int занимает 4 байта, а char занимает только 1 байт. Мы могли бы использовать указатель на символ, чтобы указывать на int со значением 1. Таким образом, если компьютер имеет младший порядковый номер, символ, на который указывает указатель на символ , имеет значение 1, в противном случае его значение должно быть 0.

Archimedes520
источник
это можно улучшить с помощью int32t.
Шаттл87
1
^ если вы хотите придираться, лучше всего это int16_fast_t. и текущий код @ Archimedes520 не будет работать на арке, где int изначально является int8;) (хотя это может пойти вразрез со стандартами c),
hanshenrik