Détecter l'Endianness
Je suis en train d'essayer de créer un C code source qui gère correctement les I/O quel que soit le boutisme du système cible.
J'ai sélectionné l'option "little endian", comme ma I/O de la convention, ce qui signifie que, pour big endian CPU, j'ai besoin de convertir des données lors de l'écriture ou de la lecture.
La Conversion n'est pas la question. Le problème que j'ai le visage est de détecter l'endianness, de préférence au moment de la compilation (depuis le CPU ne change pas boutisme dans le milieu de l'exécution...).
Jusqu'à maintenant, j'ai été en utilisant ce :
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
...
#else
...
#endif
Il est connu comme un GCC pré-défini de macros et Visual semble le comprendre aussi.
Cependant, j'ai reçu le rapport que la vérification échoue pour certains big_endian systèmes (PowerPC).
Donc, je suis à la recherche d'une solution infaillible, qui assure que l'endianess est correctement détecté, quel que soit le compilateur et le système cible. eh bien, la plupart d'entre eux au moins...
[Edit] : la Plupart des solutions proposées s'appuient sur "l'exécution des tests de temps". Ces tests peuvent parfois être bien évalué par les compilateurs lors de la compilation, et donc pas de coût réel les performances d'exécution.
Cependant, la ramification avec une sorte de << if (0) { ... } else { ... }
>> n'est pas suffisant. Dans le code actuel de la mise en œuvre, de variables et de fonctions déclaration dépendent big_endian de détection. On ne peut pas être changé avec une instruction if.
Bien, évidemment, il est tomber en arrière plan, qui consiste à réécrire le code...
Je préfère l'éviter, mais, eh bien, il ressemble à une diminution de l'espérance...
[Edit 2] : j'ai testé "au moment de l'exécution des tests", de profondément modifier le code. Bien qu'ils fassent leur travail correctement, ces tests ont aussi un impact sur les performances.
Je m'attendais à ce que, depuis les tests ont prévisible de sortie, le compilateur pourrait éliminer les mauvaises branches. Mais malheureusement, il ne fonctionne pas tout le temps. MSVC est bon compilateur, et réussit à éliminer les mauvaises branches, mais GCC a des résultats mitigés, selon les versions, genre de tests, et avec plus d'impact sur les systèmes 64 bits que sur 32 bits.
C'est étrange. Et cela signifie aussi que le moment de l'exécution des tests ne peut être assuré d'être traité par le compilateur.
Modifier 3 : Ces jours-ci, je suis en utilisant une constante de compilation de l'union, s'attendant à le compilateur à résoudre pour un oui clair et net/pas de signal.
Et ça marche plutôt bien :
https://godbolt.org/g/DAafKo
source d'informationauteur Cyan | 2012-01-23
Vous devez vous connecter pour publier un commentaire.
Au moment de la compilation en C, vous ne pouvez pas faire beaucoup plus que de faire confiance à préprocesseur
#define
s, et il n'y a pas de solutions standard, car la norme n'est pas concerné par l'endianness.Encore, vous pourriez ajouter une assertion qui est fait à l'exécution, au démarrage du programme pour s'assurer que l'hypothèse faite lors de la compilation était vrai:
(où
COMPILED_FOR_BIG_ENDIAN
etCOMPILED_FOR_LITTLE_ENDIAN
sont des macros#define
d précédemment selon votre préprocesseur endianness contrôles)Au lieu de chercher une compilation vérification en temps, pourquoi ne pas simplement utiliser le big-endian (ce qui est considéré comme le "réseau commande" par beaucoup) et d'utiliser le
htons
/htonl
/ah
/ntohl
fonctions fournies par la plupart des systèmes UNIX et Windows. Ils sont déjà définis pour faire le travail que vous essayez de faire. Pourquoi réinventer la roue?Comme indiqué précédemment, la seule "vraie" façon de détecter les Big Endian est l'utilisation d'exécution des tests.
Cependant, parfois, une macro peut être préféré.
Malheureusement, je n'ai pas trouvé un seul "test" pour détecter cette situation, plutôt une collection d'entre eux.
Par exemple, GCC recommande :
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
. Toutefois, cela ne fonctionne qu'avec les versions les plus récentes et les versions antérieures (et d'autres compilateurs) donnera à ce test une fausse valeur "true", depuis NULL == NULL. Si vous avez besoin de la version la plus complète :defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
OK, maintenant cela fonctionne pour les plus récentes de GCC, mais quid des autres compilateurs ?
Vous pouvez essayer
__BIG_ENDIAN__
ou__BIG_ENDIAN
ou_BIG_ENDIAN
qui sont souvent définis sur big endian compilateurs.Cela permettra d'améliorer la détection. Mais si vous cibler spécifiquement les plates-formes PowerPC, vous pouvez ajouter un peu plus de tests afin d'améliorer encore plus la détection. Essayez
_ARCH_PPC
ou__PPC__
ou__PPC
ouPPC
ou__powerpc__
ou__powerpc
ou mêmepowerpc
. Lier toutes ces définit ensemble, et vous avez une assez bonne chance pour détecter big endian systèmes, et powerpc en particulier, quel que soit le compilateur et sa version.Donc, pour résumer, il n'y a pas une telle chose comme un "standard macros prédéfinies" qui garantit à détecter big-endian CPU sur toutes les plates-formes et les compilateurs, mais il ya beaucoup de ces macros prédéfinies qui, collectivement, à accorder une haute probabilité de détecter correctement big endian dans la plupart des circonstances.
Nonobstant compilateur macros définies, je ne pense pas qu'il y a un moment de la compilation moyen de détecter ce, depuis la détermination de la boutisme d'une architecture s'agit d'analyser la manière dans laquelle il stocke les données dans la mémoire.
Voici une fonction qui ne fait que cela:
Essayez quelque chose comme:
et de voir si votre compilateur résout au moment de la compilation. Si non, vous pourriez avoir plus de chance de faire la même chose avec un syndicat. En fait j'aime définir des macros à l'aide de syndicats qui évaluent à 0,1 ou 1,0 (respectivement), de sorte que je peux juste faire des choses comme l'accès
buf[HI]
etbuf[LO]
.Comme d'autres l'ont souligné, il n'y a pas un portable moyen de vérifier l'endianness au moment de la compilation. Toutefois, une option serait d'utiliser le
autoconf
outil dans le cadre de votre script de build pour détecter si le système est en big-endian ou little-endian, puis à utiliser laAC_C_BIGENDIAN
macro, qui détient cette information. En un sens, cela génère un programme qui détecte à l'exécution si le système est en big-endian ou little-endian, a alors qu'une sortie de programme de l'information qui peut ensuite être utilisé de manière statique par le code source principal.Espérons que cette aide!
Vous ne pouvez pas détecter au moment de la compilation pour être portable à travers tous les compilateurs. Peut-être que vous pouvez modifier le code pour le faire au moment de l'exécution - ce qui est réalisable.
Il n'est pas possible de détecter l'endianness de façon portable en C avec les directives du préprocesseur.
Cela vient de p. 45 de les Pointeurs en C:
Je sais je suis en retard pour cette partie, mais ici, c'est mon point de vue.
Ceci est basé sur le fait que
'0'
est de 48 décimal et'1'
49, donc'1'
a la LSB bit, tandis que'0'
pas. Je pouvais leur faire'\x00'
et'\x01'
mais je pense que ma version le rend plus lisible.