Обнаружение порядка байтов программно в программе на C ++ - PullRequest
194 голосов
/ 16 июня 2009

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

Ответы [ 29 ]

1 голос
/ 16 июня 2009
int i=1;
char *c=(char*)&i;
bool littleendian=c;
1 голос
/ 16 июня 2009

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

#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 голосов
/ 17 мая 2019

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

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

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

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.
В отличие от использования union s, это явно разрешено системой типов C ++.

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

Если программа пытается получить доступ к сохраненному значению объекта через glvalue другого, чем один из следующих типов, поведение не определено: ... 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);
}
0 голосов
/ 27 декабря 2018

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

#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; 
} 
0 голосов
/ 16 июня 2009

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

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


ENDIANESS 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;
} 
0 голосов
/ 28 сентября 2014

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

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

#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 его битов), поэтому компилятор не может знать, что это случайное значение, и он не оптимизирует функция прочь.

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

0 голосов
/ 16 июня 2009

Вот еще одна версия Си. Он определяет макрос с именем 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);
}

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

0 голосов
/ 15 октября 2013

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

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

#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. Таким образом, если компьютер имеет младший порядковый номер, то char это char указатель указывает на значение со значением 1, в противном случае его значение должно быть 0.

0 голосов
/ 25 ноября 2012

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

...