Existe-t-il un moyen sûr et portable de déterminer (au moment de la compilation) l'endianité de la plate-forme sur laquelle mon programme est compilé? J'écris en C.
[EDIT] Merci pour les réponses, j'ai décidé de m'en tenir à la solution d'exécution!
Ceci est pour la vérification du temps de compilation
Vous pouvez utiliser les informations du fichier d'en-tête boost endian.hpp
, qui couvre de nombreuses plates-formes.
modifier pour la vérification de l'exécution
bool isLittleEndian()
{
short int number = 0x1;
char *numPtr = (char*)&number;
return (numPtr[0] == 1);
}
Créez un entier et lisez son premier octet (octet de poids faible). Si cet octet vaut 1, le système est petit endian, sinon c'est big endian.
modifier En y réfléchissant
Oui, vous pourriez rencontrer un problème potentiel sur certaines plates-formes (vous ne pouvez pas penser à aucun) où sizeof(char) == sizeof(short int)
. Vous pouvez utiliser des types intégraux multi-octets de largeur fixe disponibles dans <stdint.h>
, Ou si votre plate-forme ne l'a pas, vous pouvez à nouveau adapter un en-tête boost pour votre usage: stdint.hpp
Pour répondre à la question initiale d'une vérification à la compilation , il n'existe aucun moyen standardisé de le faire qui fonctionnera sur tous les compilateurs existants et futurs, car aucun des normes C, C++ et POSIX existantes définissent des macros pour détecter l'endianité.
Mais, si vous êtes prêt à vous limiter à un ensemble connu de compilateurs, vous pouvez consulter chacune des documentations de ces compilateurs pour savoir quelles macros prédéfinies (le cas échéant) elles utilisent pour définir l'endianité. Cette page répertorie plusieurs macros que vous pouvez rechercher, alors voici un code qui fonctionnerait pour celles-ci:
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
defined(__BIG_ENDIAN__) || \
defined(__ARMEB__) || \
defined(__THUMBEB__) || \
defined(__AARCH64EB__) || \
defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
// It's a big-endian target architecture
#Elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
defined(__LITTLE_ENDIAN__) || \
defined(__ARMEL__) || \
defined(__THUMBEL__) || \
defined(__AARCH64EL__) || \
defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
// It's a little-endian target architecture
#else
#error "I don't know what architecture this is!"
#endif
Si vous ne trouvez pas les macros prédéfinies que votre compilateur utilise dans sa documentation, vous pouvez également essayer de le contraindre à cracher sa liste complète de macros prédéfinies et deviner à partir de là ce qui fonctionnera (recherchez quoi que ce soit avec ENDIAN, ORDER ou le processeur nom de l'architecture). Cette page répertorie un certain nombre de méthodes pour le faire dans différents compilateurs:
Compiler C macros C++ macros
Clang/LLVM clang -dM -E -x c /dev/null clang++ -dM -E -x c++ /dev/null
GNU GCC/G++ gcc -dM -E -x c /dev/null g++ -dM -E -x c++ /dev/null
Hewlett-Packard C/aC++ cc -dM -E -x c /dev/null aCC -dM -E -x c++ /dev/null
IBM XL C/C++ xlc -qshowmacros -E /dev/null xlc++ -qshowmacros -E /dev/null
Intel ICC/ICPC icc -dM -E -x c /dev/null icpc -dM -E -x c++ /dev/null
Microsoft Visual Studio (none) (none)
Oracle Solaris Studio cc -xdumpmacros -E /dev/null CC -xdumpmacros -E /dev/null
Portland Group PGCC/PGCPP pgcc -dM -E (none)
Enfin, pour terminer, les compilateurs Microsoft Visual C/C++ sont les plus étranges et n'ont aucun des éléments ci-dessus. Heureusement, ils ont documenté leurs macros prédéfinies ici , et vous pouvez utiliser l'architecture du processeur cible pour en déduire l'endianité. Bien que tous les processeurs actuellement pris en charge dans Windows soient de petite taille (_M_IX86
, _M_X64
, _M_IA64
, et _M_ARM
sont peu endian), certains processeurs historiquement pris en charge comme le PowerPC (_M_PPC
) étaient big-endian. Mais plus important encore, la Xbox 360 est une machine PowerPC big-endian, donc si vous écrivez un en-tête de bibliothèque multiplateforme, cela ne peut pas faire de mal de vérifier _M_PPC
.
Avec C99, vous pouvez effectuer la vérification comme:
#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)
Les conditions comme if (I_AM_LITTLE)
seront évaluées au moment de la compilation et permettront au compilateur d'optimiser des blocs entiers.
Je n'ai pas la référence pour savoir si c'est à proprement parler un expression constante en C99 (ce qui lui permettrait d'être utilisé dans les initialiseurs pour les données de durée de stockage statique), mais sinon, c'est la prochaine meilleure chose.
Lecture intéressante de la FAQ C :
Vous ne pouvez probablement pas. Les techniques habituelles de détection de l'endianité impliquent des pointeurs ou des tableaux de caractères, ou peut-être des unions, mais l'arithmétique du préprocesseur n'utilise que des entiers longs, et il n'y a pas de concept d'adressage. Une autre possibilité tentante est quelque chose comme
#if 'ABCD' == 0x41424344
mais ce n'est pas fiable non plus.
Je voudrais étendre les réponses pour fournir une fonction constexpr
pour C++
union Mix {
int sdat;
char cdat[4];
};
static constexpr Mix mix { 0x1 };
constexpr bool isLittleEndian() {
return mix.cdat[0] == 1;
}
Puisque mix
est aussi constexpr
, c'est le temps de compilation et peut être utilisé dans constexpr bool isLittleEndian()
. Doit être sûr à utiliser.
Comme @Cheersandhth l'a souligné ci-dessous, cela semble être problématique.
La raison en est qu'il est non conforme à C++ 11-Standard, où type punning est interdit. Il ne peut toujours y avoir qu'un seul membre du syndicat actif à la fois. Avec un compilateur conforme standard, vous obtiendrez une erreur.
Donc, ne l'utilisez pas en C++. Il semble que vous puissiez le faire en C cependant. Je laisse ma réponse à des fins éducatives :-) et parce que la question concerne C ...
Cela suppose que int
a la taille de 4 char
s, ce qui n'est pas toujours indiqué comme @ PetrVepřek l'a correctement indiqué ci-dessous. Pour rendre votre code vraiment portable, vous devez être plus intelligent ici. Cela devrait cependant suffire dans de nombreux cas. Notez que sizeof(char)
est toujours 1
, Par définition. Le code ci-dessus suppose sizeof(int)==4
.
Pas pendant la compilation, mais peut-être pendant l'exécution. Voici une fonction C que j'ai écrite pour déterminer l'endianité:
/* Returns 1 if LITTLE-ENDIAN or 0 if BIG-ENDIAN */
#include <inttypes.h>
int endianness()
{
union { uint8_t c[4]; uint32_t i; } data;
data.i = 0x12345678;
return (data.c[0] == 0x78);
}
De Enfin, détection d'endianité sur une ligne dans le préprocesseur C :
#include <stdint.h>
#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
Tout optimiseur décent résoudra cela au moment de la compilation. gcc fait à -O1
.
Bien sûr stdint.h
est C99. Pour la portabilité ANSI/C89, voir la bibliothèque de Doug Gwyn Instant C9x .
J'ai utilisé une fois une construction comme celle-ci:
uint16_t HI_BYTE = 0,
LO_BYTE = 1;
uint16_t s = 1;
if(*(uint8_t *) &s == 1) {
HI_BYTE = 1;
LO_BYTE = 0;
}
pByte[HI_BYTE] = 0x10;
pByte[LO_BYTE] = 0x20;
gcc avec -O2 a été en mesure de rendre complètement le temps de compilation. Cela signifie que les variables HI_BYTE
Et LO_BYTE
Ont été entièrement remplacées et même l'accès pByte a été remplacé dans l'assembleur par l'équivalent de *(unit16_t *pByte) = 0x1020;
.
C'est autant de temps de compilation que possible.
À ma connaissance non, pas pendant la compilation.
Au moment de l'exécution, vous pouvez effectuer des vérifications triviales telles que la définition d'une valeur multi-octets sur une chaîne de bits connue et inspecter les octets qui en résultent. Par exemple, en utilisant une union,
typedef union {
uint32_t Word;
uint8_t bytes[4];
} byte_check;
ou casting,
uint32_t Word;
uint8_t * bytes = &Word;
Veuillez noter que pour les contrôles d'endianité entièrement portables, vous devez prendre en compte les systèmes big-endian, little-endian et mixed-endian.
Utilisez CMake TestBigEndian as
INCLUDE(TestBigEndian)
TEST_BIG_ENDIAN(ENDIAN)
IF (ENDIAN)
# big endian
ELSE (ENDIAN)
# little endian
ENDIF (ENDIAN)
EDIT2: Cette méthode ne fonctionne pas. La représentation de la constante multi-octets est spécifique au compilateur/plateforme et ne peut pas être utilisée de manière fiable. Le lien fourni par interjay ( http://www.ideone.com/LaKpj ) donne un exemple où il échoue. Sur Solaris/SPARC, le même compilateur gcc 4.3.3 donne la bonne réponse, mais le compilateur SUNStudio 12 aura le même comportement que le gcc 4.3.4 sur x86 utilisé sur ce lien.
Donc, nous pouvons conclure, toujours pas une bonne utilisation du caractère multi-octets
Trouvé cette nouvelle méthode qui a l'avantage d'être simple et de compiler le temps.
switch('AB') { case 0x4142: printf("ASCII Big endian\n"); break; case 0x4241: printf("ASCII Little endian\n"); break; case 0xC1C2: printf("EBCDIC Big endian\n"); break; case 0xC2C1: printf("EBCDIC Little endian\n"); break; }
ÉDITER:
Trouvé même un moyen de le faire dans le pré-processeur:
#if 'AB' == 0x4142
#error "ASCII Big endian\n"
#Elif 'AB' == 0x4241
#error "ASCII Little endian\n"
#Elif 'AB' == 0xC1C2
#error "EBCDIC Big endian\n"
#Elif 'AB' == 0xC2C1
#error "EBCDIC Little endian\n"
#else
#error "unknown coding and endianness\n"
#endif
Et avant que quelqu'un ne demande, les constantes de caractères multi-octets sont ANSI-C (même C90) mais sont définies par l'implémentation. Voici la première application utile que j'ai trouvée pour eux.