C Макроопределение для определения машины с прямым или прямым порядком байтов? - PullRequest
101 голосов
/ 20 января 2010

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

unsigned char test_endian( void )
{
    int test_var = 1;
    unsigned char test_endian* = (unsigned char*)&test_var;

    return (test_endian[0] == NULL);
}

Ответы [ 18 ]

92 голосов
/ 20 января 2010

Код, поддерживающий произвольные байтовые порядки, готовый для помещения в файл с именем order32.h:

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
    O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0, 1, 2, 3 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

Вы будете проверять системы с прямым порядком байтов через

O32_HOST_ORDER == O32_LITTLE_ENDIAN
44 голосов
/ 20 января 2010

Если у вас есть компилятор, который поддерживает составные литералы C99:

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

или:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

В общем, вы должны попытаться написать код, который не зависит отendianness платформы хоста.


Пример реализации, независимой от endanness хоста ntohl():

uint32_t ntohl(uint32_t n)
{
    unsigned char *np = (unsigned char *)&n;

    return ((uint32_t)np[0] << 24) |
        ((uint32_t)np[1] << 16) |
        ((uint32_t)np[2] << 8) |
        (uint32_t)np[3];
}
36 голосов
/ 20 января 2010

Стандартов не существует, но во многих системах, включая <endian.h>, вы найдете некоторые определения для поиска.

25 голосов
/ 20 января 2010

Чтобы определить порядок байтов во время выполнения, вы должны иметь возможность обращаться к памяти. Если вы придерживаетесь стандартного C, для объявления переменной в памяти требуется оператор, но для возврата значения требуется выражение. Я не знаю, как сделать это в одном макросе - вот почему gcc имеет расширения: -)

Если вы хотите иметь файл .h, вы можете определить

static uint32_t endianness = 0xdeadbeef; 
enum endianness { BIG, LITTLE };

#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                   : *(const char *)&endianness == 0xde ? BIG \
                   : assert(0))

и затем вы можете использовать макрос ENDIANNESS, как хотите.

19 голосов
/ 20 января 2010

Если вы хотите полагаться только на препроцессор, вам нужно составить список предопределенных символов. Препроцессорная арифметика не имеет понятия адресации.

GCC на Mac определяет __LITTLE_ENDIAN__ или __BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1

Затем вы можете добавить дополнительные условные директивы препроцессора, основанные на обнаружении платформы, например #ifdef _WIN32 и т. Д.

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

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

    #define LITTLE_ENDIAN 0x41424344UL 
    #define BIG_ENDIAN    0x44434241UL
    #define PDP_ENDIAN    0x42414443UL
    #define ENDIAN_ORDER  ('ABCD') 

    #if ENDIAN_ORDER==LITTLE_ENDIAN
        #error "machine is little endian"
    #elif ENDIAN_ORDER==BIG_ENDIAN
        #error "machine is big endian"
    #elif ENDIAN_ORDER==PDP_ENDIAN
        #error "jeez, machine is PDP!"
    #else
        #error "What kind of hardware is this?!"
    #endif

Как примечание (специфично для компилятора), с агрессивным компилятором вы можете использовать оптимизацию "удаления мертвого кода" для достижения того же эффекта, что и во время компиляции #if примерно так:

    unsigned yourOwnEndianSpecific_htonl(unsigned n)
    {
        static unsigned long signature= 0x01020304UL; 
        if (1 == (unsigned char&)signature) // big endian
            return n;
        if (2 == (unsigned char&)signature) // the PDP style
        {
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        if (4 == (unsigned char&)signature) // little endian
        {
            n = (n << 16) | (n >> 16);
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        // only weird machines get here
        return n; // ?
    }

Вышеприведенное опирается на тот факт, что компилятор распознает постоянные значения во время компиляции, полностью удаляет код в if (false) { ... } и заменяет код, подобный if (true) { foo(); }, на foo(); Наихудший сценарий: компилятор не выполняет оптимизация, вы все еще получаете правильный код, но немного медленнее.

9 голосов
/ 18 ноября 2016

Если вы ищете тест времени компиляции и используете gcc, вы можете сделать:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

См. gcc документация для получения дополнительной информации.

8 голосов
/ 11 января 2013

Вы можете фактически получить доступ к памяти временного объекта, используя составной литерал (C99):

#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})

Какой GCC будет оценивать во время компиляции.

7 голосов
/ 12 июня 2013

'Сетевая библиотека C' предлагает функции для обработки порядка байтов. А именно htons (), htonl (), ntohs () и ntohl () ... где n - это «сеть» (т. Е. Big-endian), а h - «хост» (т. Е. Порядковый номер машины, на которой выполняется код).

Эти кажущиеся «функции» (обычно) определяются как макросы [см. ], поэтому для их использования нет накладных расходов времени выполнения.

Следующие макросы используют эти «функции» для оценки порядка байтов.

#include <arpa/inet.h>
#define  IS_BIG_ENDIAN     (1 == htons(1))
#define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)

Дополнительно:

Единственный раз, когда мне нужно знать порядковый номер системы, - это когда я записываю переменную [в файл / другой файл], которая может быть прочитана другой системой с неизвестным порядковым номером (для перекрестного -платформенная совместимость) ... В таких случаях вы можете предпочесть непосредственное использование функций с порядком байтов:

#include <arpa/inet.h>

#define JPEG_MAGIC  (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F')

// Result will be in 'host' byte-order
unsigned long  jpeg_magic = JPEG_MAGIC;

// Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable)
unsigned long  jpeg_magic = htonl(JPEG_MAGIC);
6 голосов
/ 20 января 2010

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

Вы можете преобразовать его в короткий макрос, используя статическую или глобальную переменную, например:

static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
...