118 votes

Définition de la macro C pour déterminer la machine big endian ou little endian ?

Existe-t-il une macro de définition d'une ligne pour déterminer l'endiannité de la machine ? J'utilise le code suivant mais le convertir en macro serait trop long.

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

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

2 votes

Pourquoi ne pas inclure le même code dans une macro ?

4 votes

Vous ne pouvez pas déterminer l'endiveté de manière portative avec le préprocesseur C seul. Vous voulez aussi 0 au lieu de NULL dans votre test final, et modifiez l'un des éléments suivants test_endian les objets à quelque chose d'autre :-).

0 votes

Pourquoi avez-vous besoin d'une macro ?

110voto

Christoph Points 64389

Code supportant des ordres d'octets arbitraires, prêt à être placé dans un fichier appelé 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

Vous pouvez vérifier les systèmes little endian via

O32_HOST_ORDER == O32_LITTLE_ENDIAN

13 votes

Cela ne vous permet pas décider endian jusqu'au moment de l'exécution. Ce qui suit ne compile pas car. /** isLittleEndian::result --> 0 ou 1 */ struct isLittleEndian { enum isLittleEndianResult { result = (O32_HOST_ORDER == O32_LITTLE_ENDIAN) } ; } ;

4 votes

Est-il impossible d'obtenir le résultat avant l'exécution ?

9 votes

Por qué char ? Mieux utiliser uint8_t et échoue si ce type n'est pas disponible (ce qui peut être vérifié par l'outil #if UINT8_MAX ). Notez que CHAR_BIT est indépendant de uint8_t .

53voto

caf Points 114951

Si vous avez un compilateur qui supporte les littéraux composés C99 :

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

ou :

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

En général, cependant, vous devriez essayer d'écrire du code qui ne dépend pas de l'endive de la plate-forme hôte.


Exemple d'une mise en œuvre indépendante de l'endianisme de l'hôte de l'application 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];
}

0 votes

Cela ne pourrait-il pas être optimisé en utilisant le terminateur ? :)

4 votes

"vous devriez essayer d'écrire du code qui ne dépend pas de l'endianness de la plate-forme hôte". Malheureusement, mon plaidoyer, "Je sais que nous écrivons une couche de compatibilité POSIX, mais je ne veux pas implémenter ntoh, parce qu'il dépend de l'endianness de la plate-forme hôte" est toujours tombé dans l'oreille d'un sourd ;-). La gestion du format graphique et le code de conversion est l'autre candidat principal que j'ai vu - vous ne voulez pas tout baser sur l'appel de ntohl tout le temps.

8 votes

Vous pouvez mettre en œuvre ntohl d'une manière qui ne dépend pas de l'endiveté de la plate-forme hôte.

48voto

Il n'existe pas de norme, mais sur de nombreux systèmes, notamment <endian.h> vous donnera quelques définitions à rechercher.

39 votes

Testez l'endianness avec #if __BYTE_ORDER == __LITTLE_ENDIAN y #elif __BYTE_ORDER == __BIG_ENDIAN . Et générer un #error autrement.

7 votes

<endian.h> n'est pas disponible sur Windows

2 votes

Android y Chrome les projets utilisent endian.h sauf si __APPLE__ o _WIN32 est défini.

28voto

Norman Ramsey Points 115730

Pour détecter l'endiveté au moment de l'exécution, il faut pouvoir faire référence à la mémoire. Si vous vous en tenez au C standard, la déclaration d'une variable en mémoire nécessite une instruction, mais le retour d'une valeur nécessite une expression. Je ne sais pas comment faire cela dans une seule macro - c'est pourquoi gcc a des extensions :-)

Si vous êtes prêt à avoir un fichier .h, vous pouvez définir

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

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

et ensuite vous pouvez utiliser le ENDIANNESS macro comme vous le souhaitez.

6 votes

J'aime bien cela car cela reconnaît l'existence d'endianness autres que little et big.

9 votes

À ce propos, il peut être intéressant d'appeler la macro INT_ENDIANNESS, ou même UINT32_T_ENDIANNESS, car elle ne teste que la représentation de stockage d'un seul type. Il existe une ABI ARM où les types intégraux sont little-endian, mais les doubles sont middle-endian (chaque mot est little-endian, mais le mot avec le bit de signe vient avant l'autre mot). Cela a provoqué une certaine excitation au sein de l'équipe du compilateur pendant un jour ou deux, je peux vous le dire.

19voto

Gregory Pakosz Points 35546

Si vous voulez vous reposer uniquement sur le préprocesseur, vous devez trouver la liste des symboles prédéfinis. L'arithmétique du préprocesseur n'a pas de concept d'adressage.

CCG sur Mac définit __LITTLE_ENDIAN__ o __BIG_ENDIAN__

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

Ensuite, vous pouvez ajouter d'autres directives conditionnelles du préprocesseur basées sur la détection de la plate-forme comme #ifdef _WIN32 etc.

6 votes

GCC 4.1.2 sur Linux ne semble pas définir ces macros, bien que GCC 4.0.1 et 4.2.1 les définissent sur Macintosh. Il ne s'agit donc pas d'une méthode fiable pour le développement multiplateforme, même si vous êtes autorisé à dicter le compilateur à utiliser.

1 votes

Oh oui c'est parce que c'est seulement défini par GCC sur Mac.

0 votes

Note : Mon GCC (sur Mac) définit #define __BIG_ENDIAN__ 1 y #define _BIG_ENDIAN 1 .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X