web-dev-qa-db-fra.com

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

Existe-t-il une définition de macro sur une ligne pour déterminer l’endianité 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] == NULL);
}
91
manav m-n

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 devriez vérifier les systèmes little endian via

O32_Host_ORDER == O32_LITTLE_ENDIAN
90
Christoph

Si vous avez un compilateur prenant en charge 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’endianisme de la plate-forme hôte.


Exemple d'implémentation de ntohl() indépendante de l'hôte-endianisme:

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];
}
41
caf

Il n'y a pas de standard, mais sur de nombreux systèmes, dont <endian.h>, vous aurez des définitions à rechercher.

33

Pour détecter l’endianité au moment de l’exécution, vous devez pouvoir vous référer à la mémoire. Si vous vous en tenez à la norme C, déclarer une variable en mémoire nécessite une instruction, mais renvoyer 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 souhaitez 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 vous pouvez ensuite utiliser la macro ENDIANNESS comme vous le souhaitez.

25
Norman Ramsey

Si vous voulez vous fier uniquement au préprocesseur, vous devez déterminer la liste des symboles prédéfinis. L'arithmétique de pré-traitement n'a aucun concept d'adressage.

GCC sur Mac définit __LITTLE_ENDIAN__ ou __BIG_ENDIAN__

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

Ensuite, vous pouvez ajouter davantage de directives conditionnelles de préprocesseur basées sur la détection de plate-forme, telles que #ifdef _WIN32 etc.

19
Gregory Pakosz

Je crois que c’est ce qui a été demandé… .. Je n’ai testé cela que sur une petite machine endian sous msvc… .. Quelqu'un a-t-il confirmé sur une grosse machine endian. 

    #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

En guise de remarque (spécifique au compilateur), avec un compilateur agressif, vous pouvez utiliser l'optimisation "élimination de code mort" pour obtenir le même effet qu'un code de compilation #if comme suit:

    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; // ?
    }

Ce qui précède repose sur le fait que le compilateur reconnaît les valeurs constantes au moment de la compilation, supprime totalement le code dans if (false) { ... } et remplace le code tel que if (true) { foo(); } par foo();. Dans le pire des cas: le compilateur ne fait pas l'optimisation, vous obtenez toujours le code un peu plus lent.

14
ggpp23

Si vous recherchez un test de compilation et que vous utilisez gcc, vous pouvez faire:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

Voir documentation gcc pour plus d'informations.

9
Jezz

Vous pouvez en fait accéder à la mémoire d’un objet temporaire en utilisant un littéral composé (C99):

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

Quel GCC évaluera au moment de la compilation.

7
u0b34a0f6ae

La «bibliothèque de réseau C» offre des fonctions permettant de gérer l’endianisme. À savoir htons (), htonl (), ntohs () et ntohl () ... où n est "réseau" (c'est-à-dire big-endian) et h est "hôte" (c'est-à-dire le caractère final de la machine qui exécute le code).

Ces «fonctions» apparentes sont (généralement) définies comme des macros [voir <netinet/in.h>], de sorte qu'il n'y a pas de temps d'exécution supplémentaire pour les utiliser.

Les macros suivantes utilisent ces "fonctions" pour évaluer la finalité.

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

En outre:

La seule fois où j'ai besoin de connaître la finalité d'un système, c'est quand j'écris une variable [dans un fichier/autre] pouvant être lue par un autre système de fin inconnue (pour la compatibilité entre plates-formes). ) ... Dans de tels cas, vous préférerez peut-être utiliser directement les fonctions 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);
7
BlueChip

Utilisez une fonction en ligne plutôt qu'une macro. En outre, vous devez stocker dans la mémoire un élément qui n’est pas l’effet secondaire d’une macro.

Vous pouvez le convertir en une courte macro utilisant une variable statique ou globale, comme ceci:

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

Bien qu'il n'y ait pas de #define portable ou quelque chose sur lequel compter, les plateformes fournissent des fonctions standard pour la conversion vers et depuis votre endian 'Host'.

Généralement, vous effectuez un stockage - sur disque ou réseau - en utilisant 'network endian', qui estBIGendian, et un calcul local en utilisant Host endian (qui sur x86 estLITTLEendian). Vous utilisez htons() et ntohs() ainsi que vos amis pour effectuer la conversion entre les deux.

5
Will

N'oubliez pas que la finalité n'est pas toute l'histoire - la taille de char peut ne pas être de 8 bits (par exemple, les DSP), la négation du complément à deux n'est pas garantie (par exemple, Cray), un alignement strict peut être requis (par exemple, SPARC, ainsi ARM jaillit dans middle-endian lorsqu'il n'est pas aligné), etc., etc.

Il serait peut-être préférable de cibler une architecture spécifique CPU à la place.

Par exemple:

#if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64)
  #define USE_LITTLE_ENDIAN_IMPL
#endif

void my_func()
{
#ifdef USE_LITTLE_ENDIAN_IMPL
  // Intel x86-optimized, LE implementation
#else
  // slow but safe implementation
#endif
}

Notez que cette solution n’est malheureusement pas non plus ultra-portable, car elle dépend de définitions spécifiques au compilateur (il n’existe pas de norme, mais voici une belle compilation de ces définitions).

3
rustyx
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)
3
user1207334

Essaye ça:

#include<stdio.h>        
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{

   TEST;
}
3
Prasoon Saurav

Veuillez noter que la plupart des réponses ne sont pas portables, car les compilateurs les évaluent au moment de la compilation (en fonction de l'optimisation) et renvoient une valeur spécifique basée sur une finalité spécifique, alors que la finalité réelle de la machine peut différer. Les valeurs sur lesquelles l'endianness est testée n'atteindront jamais la mémoire système. Le code réellement exécuté retournera le même résultat, quelle que soit la finalité actuelle.

Pour exemple , dans ARM Cortex-M3, l'endianité implémentée est reflétée dans un bit d'état AIRCR.ENDIANNESS et le compilateur ne peut pas connaître cette valeur lors de la compilation.

Sortie de compilation pour certaines des réponses suggérées ici:

https://godbolt.org/z/GJGNE2 pour this réponse,

https://godbolt.org/z/Yv-pyJ pour this réponse, etc.

Pour le résoudre, vous devrez utiliser le qualificatif volatile. La réponse de Yogeesh H T est la plus proche de la réalité, mais puisque Christoph suggère une solution plus complète, une légère correction de son answer rendrait la réponse complète. Ajoutez simplement volatile à la déclaration de l’union: static const volatile union

Cela assurerait le stockage et la lecture de la mémoire, ce qui est nécessaire pour déterminer la finalité. 

1
user2162550

C Code pour vérifier si un système est little-endian ou big-indian.

int i = 7;
char* pc = (char*)(&i);
if (pc[0] == '\x7') // aliasing through char is ok
    puts("This system is little-endian");
else
    puts("This system is big-endian");
0
S. M. AMRAN

Ma réponse n’est pas celle demandée mais c’est très simple de trouver si votre système est little endian ou big endian?

Code:

#include<stdio.h>

int main()
{
  int a = 1;
  char *b;

  b = (char *)&a;
  if (*b)
    printf("Little Endian\n");
  else
    printf("Big Endian\n");
}
0
roottraveller