Détecter l’endianness par programmation dans un programme C ++

Existe-t-il un moyen programmatique de détecter si vous êtes sur une architecture big endian ou little-endian? Je dois pouvoir écrire du code qui s’exécutera sur un système Intel ou PPC et utiliser exactement le même code (c.-à-d. Pas de compilation conditionnelle).

Je n’aime pas la méthode basée sur le type punning – elle sera souvent mise en garde par le compilateur. C’est exactement ce que sont les syndicats!

int is_big_endian(void) { union { uint32_t i; char c[4]; } bint = {0x01020304}; return bint.c[0] == 1; } 

Le principe est équivalent à celui du cas suggéré par d’autres, mais cela est plus clair – et selon C99, il est garanti qu’il est correct. gcc préfère ceci par rapport à la dissortingbution directe de pointeur.

C’est aussi bien mieux que de fixer l’endianité au moment de la compilation – pour les OS qui supportent la multi-architecture (gras sur Mac OS x par exemple), cela fonctionnera à la fois pour ppc / i386, alors qu’il est très facile de faire des erreurs. .

Vous pouvez le faire en définissant un int et en masquant les bits, mais le plus simple est probablement d’utiliser les opérations de conversion d’octets réseau intégrées (puisque l’ordre des octets du réseau est toujours big endian).

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

Le bidouillage des bits pourrait être plus rapide, mais cette méthode est simple, directe et impossible à mettre en œuvre.

S’il vous plaît voir cet article :

Voici un code pour déterminer le type de votre machine

 int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); } 

Cela se fait normalement au moment de la compilation (spécialement pour des raisons de performances) en utilisant les fichiers d’en-tête disponibles à partir du compilateur ou créez les vôtres. Sur Linux, vous avez le fichier d’en-tête “/usr/include/endian.h”

Vous pouvez utiliser std::endian si vous avez access au compilateur C ++ 20 tel que GCC 8+ ou Clang 7+:

 #include  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 } 

Ehm … Cela me surprend que personne n’ait réalisé que le compilateur optimisera simplement le test et mettra un résultat fixe comme valeur de retour. Cela rend tous les exemples de code ci-dessus, effectivement inutiles. La seule chose qui serait renvoyée est l’endianness à la compilation! Et oui, j’ai testé tous les exemples ci-dessus. Voici un exemple avec MSVC 9.0 (Visual Studio 2008).

Code C pur

 int32 DNA_GetEndianness(void) { union { uint8 c[4]; uint32 i; } u; ui = 0x01020304; if (0x04 == uc[0]) return DNA_ENDIAN_LITTLE; else if (0x01 == uc[0]) return DNA_ENDIAN_BIG; else return DNA_ENDIAN_UNKNOWN; } 

Déassembly

 PUBLIC _DNA_GetEndianness ; Function comstack 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 : ui = 1; ; 18 : ; 19 : if (1 == uc[0]) ; 20 : return DNA_ENDIAN_LITTLE; mov eax, 1 ; 21 : else if (1 == uc[3]) ; 22 : return DNA_ENDIAN_BIG; ; 23 : else ; 24 : return DNA_ENDIAN_UNKNOWN; ; 25 : } ret _DNA_GetEndianness ENDP END 

Peut-être est-il possible de désactiver TOUTE optimisation à la compilation pour cette fonction, mais je ne sais pas. Sinon, il est peut-être possible de le coder en dur dans l’assemblage, bien que ce ne soit pas portable. Et même alors, cela pourrait être optimisé. Cela me fait penser que j’ai besoin d’un assembleur vraiment nul, que j’implémente le même code pour tous les processeurs / ensembles d’instructions existants, et bien … tant pis.

En outre, quelqu’un ici a dit que l’endianness ne change pas pendant l’exécution. FAUX. Il y a des machines bi-endian là-bas. Leur endianness peut varier de l’exécution. De plus, il n’y a pas que Little Endian et Big Endian, mais aussi d’autres endiannesses (quel mot).

Je déteste et aime coder en même temps …

Déclarez une variable int:

 int variable = 0xFF; 

Utilisez maintenant des pointeurs de caractères sur différentes parties et vérifiez ce qui se trouve dans ces parties.

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

Selon celui qui pointe vers l’octet 0xFF, vous pouvez maintenant détecter l’endianness. Cela nécessite sizeof (int)> sizeof (char), mais c’est tout à fait vrai pour les plateformes discutées.

J’ai surpris personne n’a mentionné les macros que le pré-processeur définit par défaut. Bien que ceux-ci varient en fonction de votre plate-forme; ils sont beaucoup plus propres que de devoir écrire votre propre chèque endian.

Par exemple; Si l’on regarde les macros intégrées que GCC définit (sur une machine X86-64):

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

Sur une machine PPC, je reçois:

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

(Le :| gcc -dM -E -xc - magic imprime toutes les macros intégrées).

Pour plus de détails, vous pouvez consulter cet article codeproject Concepts de base sur l’endianness :

Comment tester dynamicment le type Endian au moment de l’exécution?

Comme expliqué dans la FAQ sur l’animation par ordinateur, vous pouvez utiliser la fonction suivante pour voir si votre code s’exécute sur un système Little- ou Big-Endian: Réduire

 #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); } 

Ce code assigne la valeur 0001h à un entier 16 bits. Un pointeur de caractère est ensuite affecté au point sur le premier octet (le moins significatif) de la valeur entière. Si le premier octet de l’entier est 0x01h, le système est Little-Endian (l’adresse 0x01h est l’adresse la plus basse ou la moins significative). Si c’est 0x00h alors le système est Big-Endian.

Comme indiqué ci-dessus, utilisez des astuces syndicales.

Il y a peu de problèmes avec ceux indiqués ci-dessus, plus particulièrement que l’access à la mémoire non alignée est notoirement lent pour la plupart des architectures, et certains compilateurs ne reconnaissent même pas du tout ces constantes, à moins que le mot ne soit aligné.

Parce que le simple test endian est ennuyeux, voici la fonction (template) qui va retourner l’entrée / sortie d’un entier arbitraire en fonction de vos spécifications, quelle que soit l’architecture de l’hôte.

 #include  #define BIG_ENDIAN 1 #define LITTLE_ENDIAN 0 template  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 comstackrs 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; }; 

Usage:

Pour convertir un endian donné en hôte, utilisez:

host = endian(source, endian_of_source)

Pour convertir l’hôte endian en endian donné, utilisez:

output = endian(hostsource, endian_you_want_to_output)

Le code résultant est aussi rapide que l’écriture manuelle sur clang, sur gcc, il est plus lent (déroulé &, <<, >>, | pour chaque octet) mais rest correct.

Sauf si vous utilisez un framework qui a été porté sur les processeurs PPC et Intel, vous devrez faire des compilations conditionnelles, car les plates-formes PPC et Intel ont des architectures matérielles, des pipelines, des bus, etc. complètement différents. les deux.

En ce qui concerne la recherche de l’endianness, procédez comme suit:

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

Vous obtiendrez soit tempChar soit 0x12 ou 0x34, à partir de laquelle vous connaîtrez le caractère endian.

La méthode C ++ consistait à utiliser boost , où les vérifications et les dissortingbutions du préprocesseur étaient réparties dans des bibliothèques très bien testées.

La bibliothèque Predef (boost / predef.h) reconnaît quatre types différents d’endianness .

La bibliothèque Endian devait être soumise à la norme C ++ et prend en charge une grande variété d’opérations sur les données sensibles à l’endian.

Comme indiqué dans les réponses ci-dessus, Endianness fera partie de c ++ 20.

Je ferais quelque chose comme ça:

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

Dans ce sens, vous obtiendrez une fonction efficace dans le temps qui ne fait que le calcul une fois.

 bool isBigEndian() { static const uint16_t m_endianCheck(0x00ff); return ( *((uint8_t*)&m_endianCheck) == 0x0); } 

temps de compilation, non macro, solution C ++ 11 constexpr:

 union { uint16_t s; unsigned char c[2]; } constexpr static d {1}; constexpr bool is_little_endian() { return dc[0] == 1; } 
 union { int i; char c[sizeof(int)]; } x; xi = 1; if(xc[0] == 1) printf("little-endian\n"); else printf("big-endian\n"); 

Ceci est une autre solution. Semblable à la solution d’Andrew Hare.

non testé, mais dans mon esprit, cela devrait fonctionner? Parce que ce sera 0x01 sur petit boutiste et 0x00 sur grand endian?

 bool runtimeIsLittleEndian(void) { volatile uint16_t i=1; return ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big } 

Vous pouvez également le faire via le préprocesseur en utilisant quelque chose comme le fichier d’en-tête boost qui peut être trouvé boost endian

 int i=1; char *c=(char*)&i; bool littleendian=c; 

Que dis-tu de ça?

 #include  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; } 

À moins que l’en-tête Endian soit uniquement GCC, il fournit des macros que vous pouvez utiliser.

 #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!"); ... 

Si vous ne voulez pas de compilation conditionnelle, vous pouvez simplement écrire du code indépendant de l’endian. Voici un exemple (tiré de Rob Pike ):

Lire un entier stocké dans little-endian sur le disque, de manière indépendante endian:

 i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); 

Le même code, en essayant de prendre en compte la nature de la machine:

 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 

Voir Endianness – Illustration du code C-Level.

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

Voici une autre version C. Il définit une macro appelée wicked_cast() pour la wicked_cast() de type en ligne via les littéraux d’union C99 et l’opérateur __typeof__ non standard.

 #include  #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); } 

Si les entiers sont des valeurs à un seul octet, l’endianness n’a aucun sens et une erreur de compilation sera générée.

La manière dont les compilateurs C (au moins tout le monde que je connais) fonctionnent de la manière nécessaire pour la compilation. Même pour les processeurs biendiens (comme ARM och MIPS), vous devez choisir l’endianness au moment de la compilation. De plus, la finalité est définie dans tous les formats de fichiers courants pour les exécutables (tels que ELF). Bien qu’il soit possible de créer un blob binary de code biandian (pour un exploit de serveur ARM peut-être?), Cela doit probablement être fait en assemblage.

Je parcourais le manuel: Système informatique: le sharepoint vue d’un programmeur , et il y a un problème pour déterminer quelle est cette endian par programme C.

J’ai utilisé la fonctionnalité du pointeur pour faire comme suit:

 #include  int main(void){ int i=1; unsigned char* ii = &i; printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big")); return 0; } 

Comme l’ int prend 4 octets, et que char ne prend que 1 octet. Nous pourrions utiliser un pointeur de caractère pour pointer vers l’ int avec la valeur 1. Ainsi, si l’ordinateur est peu endian, le caractère pointé par le pointeur char correspond à la valeur 1, sinon sa valeur devrait être 0.

Comme l’a souligné Coriiander, la plupart (sinon tous) de ces codes ici seront optimisés au moment de la compilation, de sorte que les fichiers binarys générés ne vérifieront pas le caractère “endianness” au moment de l’exécution.

Il a été observé qu’un exécutable donné ne devrait pas être exécuté dans deux ordres d’octets différents, mais je n’ai aucune idée si cela est toujours le cas, et cela me semble un hack à vérifier au moment de la compilation. J’ai donc codé cette fonction:

 #include  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 n’a pas réussi à optimiser ce code, même s’il optimise les autres codes ici. Je pense que c’est parce que je laisse la valeur “aléatoire” qui a été allouée sur la mémoire d’octets plus petite telle qu’elle était (au moins 7 bits), de sorte que le compilateur ne peut pas savoir quelle est cette valeur aléatoire et n’optimise pas la fonction loin.

J’ai également codé la fonction pour que la vérification ne soit effectuée qu’une seule fois et que la valeur de retour soit stockée pour les prochains tests.