Détection de l'endianité par programme dans un programme C ++

211

Existe-t-il un moyen programmatique de détecter si vous êtes ou non sur une architecture big-endian ou little-endian? J'ai besoin d'être en mesure d'écrire du code qui s'exécutera sur un système Intel ou PPC et d'utiliser exactement le même code (c'est-à-dire pas de compilation conditionnelle).

Jay T
la source
4
Par souci d'exhaustivité, voici un lien vers la question de quelqu'un d'autre sur la mesure de l'endianité (au moment de la compilation): stackoverflow.com/questions/280162/…
Faisal Vali
14
Pourquoi ne pas déterminer l'endianité au moment de la compilation? Il ne peut pas changer au moment de l'exécution.
éphémient
3
AFAIK, il n'y a pas de moyen fiable et universel de le faire. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Réponses:

174

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 à cela que servent les syndicats!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

Le principe est équivalent au cas type comme suggéré par d'autres, mais c'est plus clair - et selon C99, il est garanti d'être correct. gcc préfère cela par rapport à la distribution de pointeur direct.

C'est aussi bien mieux que de corriger l'endianité au moment de la compilation - pour les OS qui prennent en charge la multi-architecture (binaire gras sur Mac os x par exemple), cela fonctionnera pour les deux ppc / i386, alors qu'il est très facile de gâcher les choses autrement .

David Cournapeau
la source
51
Je ne recommande pas de nommer une variable "bint" :)
mkb
42
êtes-vous sûr que c'est bien défini? En C ++, un seul membre de l'union peut être actif à la fois - c'est-à-dire que vous ne pouvez pas attribuer en utilisant un nom de membre et lire en utilisant un autre (bien qu'il y ait une exception pour les structures compatibles avec la mise en page)
Faisal Vali
27
@Matt: J'ai regardé dans Google, et bint semble avoir un sens en anglais que je n'étais pas au courant :)
David Cournapeau
17
J'ai testé cela, et dans gcc 4.0.1 et gcc 4.4.1 le résultat de cette fonction peut être déterminé au moment de la compilation et traité comme une constante. Cela signifie que le compilateur tombera si les branches qui dépendent uniquement du résultat de cette fonction et ne seront jamais prises sur la plateforme en question. Ce n'est probablement pas le cas de nombreuses implémentations de htonl.
Omnifarious
6
Cette solution est-elle vraiment portable? Et si CHAR_BIT != 8?
zorgit
80

Vous pouvez le faire en définissant un int et en masquant les bits, mais le moyen le plus simple est probablement d'utiliser les opérations de conversion d'octets réseau intégrées (car l'ordre des octets réseau est toujours en gros bout).

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

Le bricolage des bits pourrait être plus rapide, mais cette façon est simple, directe et assez impossible à gâcher.

Eric Petroelje
la source
1
Les opérations de conversion réseau peuvent également être utilisées pour tout convertir en big endian, résolvant ainsi d'autres problèmes que Jay peut rencontrer.
Brian
6
@sharptooth - slow est un terme relatif, mais oui, si la vitesse est vraiment un problème, utilisez-le une fois au début du programme et définissez une variable globale avec l'endianness.
Eric Petroelje
5
htonl a un autre problème: sur certaines plates-formes (Windows?), il ne réside pas dans la bibliothèque d'exécution C proprement dite, mais dans des bibliothèques supplémentaires liées au réseau (socket, etc ...). C'est tout à fait un obstacle pour une seule fonction si vous n'avez pas besoin de la bibliothèque autrement.
David Cournapeau
7
Notez que sous Linux (gcc), htonl est soumis à un pliage constant au moment de la compilation, donc une expression de ce formulaire n'a aucun temps d'exécution (c'est-à-dire qu'il est constamment plié à 1 ou 0, puis l'élimination du code mort supprime le autre branche de l'if)
bdonlan
2
En outre, sur x86, htonl peut être (et est, sur Linux / gcc) implémenté très efficacement à l'aide de l'assembleur en ligne, en particulier si vous ciblez une micro-architecture prenant en charge l' BSWAPopération.
bdonlan
61

Veuillez consulter cet article :

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

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
Andrew Hare
la source
25
Gardez à l'esprit que cela dépend de la longueur différente de int et char, ce qui est presque toujours le cas mais n'est pas garanti.
David Thornley
10
J'ai travaillé sur des systèmes embarqués où short int et char étaient de la même taille ... Je ne me souviens pas si l'int régulier était aussi de cette taille (2 octets) ou non.
rmeador
2
pourquoi CETTE réponse est-elle à peu près LA SEULE RÉPONSE qui ne me fait PAS penser "mec, qu'est-ce que tu fais?", ce qui est le cas de la plupart des réponses ici: o
hanshenrik
2
@Shillard int doit être au moins aussi grand, mais il n'y a aucune exigence dans la norme pour que char soit limité à moins! Si vous jetez un coup d'œil à la famille TI F280x, vous découvrirez que CHAR_BIT est 16 et sizeof (int) == sizeof (char) tandis que les limites que vous mentionnez sont très bien gardées ...
Aconcagua
5
Pourquoi ne pas utiliser uint8_t et uint16_t?
Rodrigo
58

Vous pouvez utiliser std::endiansi vous avez accès au compilateur C ++ 20 tel que GCC 8+ ou Clang 7+.

Remarque: a std::endiancommencé en <type_traits>mais a été déplacé à la <bit>réunion de Cologne 2019. GCC 8, Clang 7, 8 et 9 l'ont en place <type_traits>tandis que GCC 9+ et Clang 10+ l'ont <bit>.

#include <bit>

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
}
Lyberta
la source
5
Comme tout le monde, j'ai accès aux brouillons / propositions C ++ 17 et 20, mais, pour l'instant, existe-t-il un compilateur C ++ 20?
Xeverous
@Xeverous Il ne nécessite que des énumérations de portée, donc je pense que la plupart des fournisseurs l'ajouteront à leur implémentation stdlib comme l'une de leurs modifications précédentes.
Pharap
@Xeverous GCC 8 est sorti et le supporte.
Lyberta
Sur les 30+ réponses à la question, cela semble être la seule, qui est complètement exacte (avec une autre réponse qui est au moins correcte en partie).
Inspectable
40

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. Sous Linux, vous avez le fichier d'en-tête "/usr/include/endian.h"

facture
la source
8
Je ne peux pas croire que cela n'ait pas été voté plus haut. Ce n'est pas comme si l'endianité allait changer sous un programme compilé, donc il n'y a jamais besoin d'un test d'exécution.
Dolda2000 du
@ Dolda2000 Cela pourrait potentiellement, voir les modes endiens ARM.
Tyzoid
10
@Tyzoid: Non, un programme compilé s'exécutera toujours sous le mode endian pour lequel il a été compilé, même si le processeur est capable de l'un ou l'autre.
Dolda2000
16

Je suis surpris que personne n'ait 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 d'avoir à faire votre propre chèque endian.

Par exemple; si nous regardons les macros intégrées définies par GCC (sur une machine X86-64):

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

Sur une machine PPC j'obtiens:

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

(La :| gcc -dM -E -x c -magie imprime toutes les macros intégrées).

DaveR
la source
7
Ces macros n'apparaissent pas du tout de manière cohérente. Par exemple, dans gcc 4.4.5 du dépôt Redhat 6, l'exécution echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'ne renvoie rien, tandis que gcc 3.4.3 (de /usr/sfw/bintoute façon) dans Solaris a une définition dans ce sens. J'ai vu des problèmes similaires sur VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).
Brian Vandenberg
15

Ehm ... Cela me surprend que personne ne se soit rendu compte 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 retournée est l'endianité à 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;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

Démontage

PUBLIC  _DNA_GetEndianness
; Function compile 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   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

Il est peut-être possible de désactiver N'IMPORTE QUELLE optimisation au moment de 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, même cela pourrait être optimisé. Cela me fait penser que j'ai besoin d'un assembleur vraiment merdique, d'implémenter le même code pour tous les CPU / jeux d'instructions existants, et bien ... tant pis.

De plus, quelqu'un ici a dit que l'endianisme ne change pas pendant l'exécution. FAUX. Il existe des machines bi-endiennes. Leur endianité peut varier pendant l'exécution. AUSSI, il n'y a pas seulement le Little Endian et le Big Endian, mais aussi d'autres endiannesses (quel mot).

Je déteste et j'adore coder en même temps ...

Coriiander
la source
11
N'avez-vous pas besoin de recompiler pour exécuter sur une plate-forme différente de toute façon?
bobobobo
2
Bien qu'il fonctionne bien pour MSVC, il ne l'est pas pour toutes les versions de GCC en toutes circonstances. Par conséquent, une "vérification au moment de l'exécution" à l'intérieur d'une boucle critique peut être correctement non-branchée au moment de la compilation, ou non. Il n'y a pas de garantie à 100%.
Cyan
21
Un processeur x86 big-endian n'existe pas. Même si vous exécutez Ubuntu sur un processeur biendian (comme ARM ou MIPS), les exécutables ELF sont toujours soit gros (MSB) ou petit (LSB) endian. Aucun exécutable biendien ne peut être créé, aucune vérification d'exécution n'est donc nécessaire.
Fabel
4
Pour désactiver l'optimisation dans cette méthode, utilisez 'union volatile ...' Cela indique au compilateur que 'u' peut être changé ailleurs et que les données doivent être chargées
mishmashru
1
Pour que cette fonction renvoie une valeur différente à l'exécution, l'optimiseur calcule que cela implique que l'optimiseur est buggé. Êtes-vous en train de dire qu'il existe des exemples de code binaire optimisé compilé qui peuvent fonctionner de manière portable sur deux architectures différentes d'endianité différente, malgré les hypothèses évidentes faites par l'optimiseur (tout au long du programme) pendant la compilation qui semblent incompatibles avec au moins une de celles architectures?
Scott
13

Déclarez une variable int:

int variable = 0xFF;

Utilisez maintenant des pointeurs char * vers différentes parties de celui-ci et vérifiez ce qu'il y a dans ces parties.

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

En fonction de celui qui pointe vers l'octet 0xFF, vous pouvez maintenant détecter l'endianité. Cela nécessite sizeof (int)> sizeof (char), mais c'est certainement vrai pour les plateformes discutées.

acéré
la source
8

Pour plus de détails, vous pouvez consulter cet article de projet de code Concepts de base sur l'endianité :

Comment tester dynamiquement 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 attribue la valeur 0001h à un entier 16 bits. Un pointeur char est ensuite affecté pour pointer sur le premier octet (le moins significatif) de la valeur entière. Si le premier octet de l'entier est 0x01h, alors le système est Little-Endian (0x01h est dans l'adresse la plus basse ou la moins significative). S'il s'agit de 0x00h, le système est Big-Endian.

aucun
la source
6

La manière C ++ a été d'utiliser boost , où les vérifications et les transtypages du préprocesseur sont compartimentés dans des bibliothèques très testées.

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

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, l'endianité fera partie de c ++ 20.

fuzzyTew
la source
1
Pour info, le lien "quatre différents types d'endianité" est rompu,
Remy Lebeau
wiki fixe et fait
fuzzyTew
5

À moins que vous n'utilisiez un framework qui a été porté sur des processeurs PPC et Intel, vous devrez effectuer des compilations conditionnelles, car les plates-formes PPC et Intel ont des architectures matérielles, des pipelines, des bus complètement différents, etc. Cela rend le code assembleur complètement différent entre les deux.

En ce qui concerne la recherche de l'endianité, procédez comme suit:

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

Vous obtiendrez soit tempChar soit 0x12 ou 0x34, à partir duquel vous connaîtrez l'endianness.

samoz
la source
3
Cela dépend du fait que short soit exactement 2 octets, ce qui n'est pas garanti.
sharptooth
3
Ce serait une valeur sûre, bien que basée sur les deux architectures données dans la question.
Daemin
8
Inclure stdint.het utiliser int16_tpour une preuve future contre les courts-circuits différents sur une autre plate-forme.
Denise Skidmore
4

Je ferais quelque chose comme ça:

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

Dans ce sens, vous obtiendriez une fonction efficace en temps qui ne fait le calcul qu'une seule fois.

Jeremy Mayhew
la source
pouvez-vous l'intégrer? Je ne sais pas si inline cause plusieurs blocs de mémoire des variables statiques
aah134
4

Comme indiqué ci-dessus, utilisez des astuces d'union.

Cependant, il y a peu de problèmes avec ceux recommandés ci-dessus, notamment que l'accès à la mémoire non aligné est notoirement lent pour la plupart des architectures, et certains compilateurs ne reconnaissent même pas du tout ces prédicats constants, à moins que le mot ne soit aligné.

Parce qu'un simple test endian est ennuyeux, voici la fonction (modèle) qui inversera l'entrée / sortie d'un entier arbitraire selon vos spécifications, quelle que soit l'architecture de l'hôte.

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
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 compilers 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 de l'endian hôte en endian donné, utilisez:

output = endian(hostsource, endian_you_want_to_output)

Le code résultant est aussi rapide que d'écrire l'assemblage manuel sur clang, sur gcc, il est un peu plus lent (déroulé &, <<, >>, | pour chaque octet) mais toujours décent.

Kat
la source
4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
Paolo Brandoli
la source
1
Serait-ce équivalent? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel
4

N'utilisez pas de union!

C ++ n'autorise pas la punition de type via unions!
Lire à partir d'un champ union qui n'était pas le dernier champ écrit est un comportement indéfini !
De nombreux compilateurs prennent en charge cette fonctionnalité en tant qu'extensions, mais le langage n'offre aucune garantie.

Voir cette réponse pour plus de détails:

https://stackoverflow.com/a/11996970


Il n'y a que deux réponses valides qui sont garanties d'être portables.

La première réponse, si vous avez accès à un système qui prend en charge C ++ 20,
est d'utiliser à std::endianpartir de l'en- <type_traits>tête.

(Au moment de la rédaction de ce document, C ++ 20 n'a pas encore été publié, mais à moins que quelque chose n'affecte std::endianl'inclusion de, cela doit être le moyen préféré de tester l'endianité au moment de la compilation à partir de C ++ 20.)

À partir de C ++ 20

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

Avant C ++ 20, la seule réponse valide consiste à stocker un entier, puis à inspecter son premier octet via la punition de type.
Contrairement à l'utilisation de unions, cela est expressément autorisé par le système de type C ++.

Il est également important de se rappeler que pour une portabilité optimale static_castdoit être utilisé,
car la reinterpret_castmise en œuvre est définie.

Si un programme tente d'accéder à la valeur stockée d'un objet via une valeur gl autre que l'un des types suivants, le comportement n'est pas défini: ... a charou unsigned chartype.

À partir de C ++ 11

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

C ++ 11 et suivants (sans énumération)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C ++ 98 / C ++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}
Pharap
la source
3
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

Ceci est une autre solution. Similaire à la solution d'Andrew Hare.

Neeraj
la source
3

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

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

Déclarer:

Mon message initial est incorrectement déclaré comme "temps de compilation". Ce n'est pas le cas, c'est même impossible dans la norme C ++ actuelle. Constexpr ne signifie PAS que la fonction effectue toujours le calcul au moment de la compilation. Merci Richard Hodges pour la correction.

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

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
zhaorufei
la source
2
Y a-t-il une raison particulière pour laquelle vous avez utilisé un caractère non signé sur uint8_t?
Kevin
0 temps d'exécution ... j'aime ça!
hanshenrik
Je suppose que cela détecte les endiannes de la machine de génération, pas la cible?
hutorny
2
N'est-ce pas UB en C ++?
rr-
6
ce n'est pas légal dans le contexte constexpr. Vous ne pouvez pas accéder à un membre d'un syndicat qui n'a pas été initialisé directement. Il n'y a aucun moyen de détecter légalement l'endianité au moment de la compilation sans la magie du préprocesseur.
Richard Hodges
2

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


la source
1

À 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!");
...
Mark A. Libby
la source
N'est-ce pas __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__et __ORDER_BIG_ENDIAN__?
Xeverous
1

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

Lecture d'un entier stocké en little-endian sur le disque, d'une manière indépendante de l'endian:

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

Le même code, en essayant de prendre en compte l'endianité 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
fjardon
la source
Quelle belle idée! Et maintenant, transférons vos entiers via une prise réseau vers un périphérique inconnu.
Maksym Ganenko
@MaksymGanenko Je n'ai pas votre commentaire. Est-ce de l'ironie? Je ne suggère pas de ne pas spécifier l'endianité des données sérialisées. Je suggère de ne pas écrire de code en fonction de l'endianité de la machine qui reçoit les données.
fjardon
@MaksymGanenko Si vous dévaluez, vous pourriez expliquer pourquoi la réponse est fausse. Au minimum pour aider les lecteurs potentiels à comprendre pourquoi ils ne devraient pas suivre ma réponse.
fjardon
0
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Jon Bright
la source
0

Que dis-tu de ça?

#include <cstdio>

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;
}
Abhay
la source
0

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

#include <limits.h>

#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 octet, l'endianité n'a aucun sens et une erreur de compilation sera générée.

Christoph
la source
0

La façon dont les compilateurs C (au moins tous ceux que je connais) fonctionnent, l'endianité doit être décidée au moment de la compilation. Même pour les processeurs biendiens (comme ARM och MIPS), vous devez choisir l'endianité au moment de la compilation. De plus, l'endianité 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 binaire de code biandien (pour certains exploits de serveur ARM peut-être?), Cela doit probablement être fait en assembleur.

Fabel
la source
-1

Comme l'a souligné Coriiander, la plupart (sinon la totalité) de ces codes seront optimisés lors de la compilation, de sorte que les binaires générés ne vérifieront pas "l'endianité" au moment de l'exécution.

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

#include <stdint.h>

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 pu optimiser ce code, même s'il optimise les autres codes ici même. Je crois que c'est parce que je laisse la valeur "aléatoire" qui a été affectée à la mémoire d'octets plus petite telle qu'elle était (au moins 7 de ses bits), donc le compilateur ne peut pas savoir quelle est cette valeur aléatoire et il n'optimise pas la fonction loin.

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

Tex Killer
la source
Pourquoi allouer 4 octets pour travailler sur une valeur de 2 octets? Pourquoi masquer une valeur indéterminée avec 0x7FE? Pourquoi utiliser malloc()du tout? c'est du gaspillage. Et _BEsi une fuite de mémoire (quoique petite) et une condition de concurrence attendaient, les avantages de la mise en cache dynamique du résultat ne valent pas la peine. Je ferais quelque chose de plus comme ceci à la place: static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }simple et efficace, et beaucoup moins de travail à effectuer à l'exécution.
Remy Lebeau
@RemyLebeau, le but de ma réponse était de produire un code qui n'est pas optimisé par le compilateur. Bien sûr, votre code est beaucoup plus simple, mais avec les optimisations activées, il deviendra juste un booléen constant après sa compilation. Comme je l'ai dit dans ma réponse, je ne sais pas vraiment s'il existe un moyen de compiler du code C de manière à ce que le même exécutable s'exécute sur les deux ordres d'octets, et j'étais également curieux de voir si je pouvais faire la vérification au moment de l'exécution malgré les optimisations en cours.
Tex Killer
@TexKiller alors pourquoi ne pas simplement désactiver les optimisations pour le code? Utilisation de volatile, ou #pragma, etc.
Remy Lebeau
@RemyLebeau, je ne connaissais pas ces mots-clés à l'époque, et je l'ai pris comme un petit défi pour empêcher l'optimisation du compilateur avec ce que je savais.
Tex Killer
-1

bien qu'il n'y ait pas de moyen rapide et standard de le déterminer, cela le produira:

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 
yekanchi
la source
-1

Voir l' illustration Endianness - C-Level Code.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS 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;
} 
gimel
la source
-2

Je parcourais le manuel: Système informatique: le point de vue d'un programmeur , et il y a un problème pour déterminer quel endian est-ce par le programme C.

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

#include <stdio.h>

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 char pour pointer vers l' int avec la valeur 1. Ainsi , si l'ordinateur est peu endian, l' omble chevalier que pointeur char pointe est à la valeur 1, sinon, sa valeur doit être 0.

Archimedes520
la source
cela serait amélioré en utilisant int32t.
shuttle87
1
^ si vous voulez faire une pioche, le meilleur ici est int16_fast_t. et le code actuel de @ Archimedes520 ne fonctionnera pas sur un arc où int est nativement int8;) (cependant, cela pourrait aller à l'encontre des normes c)
hanshenrik