219 votes

Détection programmatique de l'endiannité 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'est-à-dire sans compilation conditionnelle).

4 votes

Dans un souci d'exhaustivité, voici un lien vers la question de quelqu'un d'autre concernant l'évaluation de l'endiannité (au moment de la compilation) : stackoverflow.com/questions/280162/

19 votes

Pourquoi ne pas déterminer l'endiveté au moment de la compilation ? Elle ne peut pas changer au moment de l'exécution.

3 votes

A priori, il n'existe pas de moyen fiable et universel de le faire. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html

177voto

David Cournapeau Points 21956

Je n'aime pas la méthode basée sur le détournement de type - elle sera souvent mise en garde par le compilateur. C'est exactement à cela que servent les unions !

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 du type tel que suggéré par d'autres, mais ceci est plus clair - et selon C99, il est garanti d'être correct. gcc préfère ceci comparé au cast direct du pointeur.

C'est aussi beaucoup mieux que de fixer l'endianness lors de la compilation - pour les OS qui supportent la multi-architecture (fat binary sur Mac os x par exemple), cela fonctionnera pour les deux ppc/i386, alors qu'il est très facile de faire des erreurs autrement.

55 votes

Je ne recommande pas de nommer une variable "bint" :)

48 votes

Ê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 assigner 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 disposition).

0 votes

Il est bien défini dans C99 AFAIK. Sur les plateformes plus anciennes, cela dépend de l'implémentation. Mais il en va de même pour le détournement de type par le biais du cast de pointeur, qui n'est pas non plus défini dans C99.

84voto

Eric Petroelje Points 40734

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 intégrées de conversion d'octets du réseau (puisque l'ordre des octets du réseau est toujours big endian).

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

Un peu de bricolage pourrait être plus rapide, mais cette méthode est simple, directe et pratiquement impossible à gâcher.

1 votes

Les opérations de conversion du réseau peuvent également être utilisées pour tout convertir en big endian, ce qui résout d'autres problèmes que Jay peut rencontrer.

0 votes

Il faut faire attention - l'implémentation de htonl peut être lente - sa vitesse doit être mesurée afin que son utilisation abusive n'introduise pas un goulot d'étranglement.

7 votes

@sharptooth - la lenteur 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.

66voto

Andrew Hare Points 159332

Veuillez consulter cet article :

Voici quelques codes 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");
}

26 votes

Gardez à l'esprit que cela dépend du fait que int et char soient de longueurs différentes, ce qui est presque toujours le cas mais pas garanti.

1 votes

David - C'est très vrai, mais je serais surpris d'apprendre qu'il existe une architecture dans laquelle les ints et les chars ont la même taille. Néanmoins, il est important de ne jamais faire d'hypothèses sur ce genre de choses.

0 votes

Cette méthode repose-t-elle sur le fait que le code est toujours compilé sur la même architecture ?

41voto

bill Points 669

Cela se fait normalement au moment de la compilation (surtout pour des raisons de performance) en utilisant les fichiers d'en-tête disponibles dans le compilateur ou en créant les vôtres. Sous linux, vous avez le fichier d'en-tête "/usr/include/endian.h".

9 votes

Je ne peux pas croire que ça n'ait pas été voté plus haut. Ce n'est pas comme si l'endiveté allait changer dans un programme compilé, donc il n'y a jamais besoin d'un test d'exécution.

1 votes

@Dolda2000 C'est potentiellement possible, voir les modes endian ARM.

13 votes

@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 des deux.

15voto

Coriiander Points 111

Ehm... Je suis surpris que personne n'ait réalisé que le compilateur va simplement optimiser le test, et mettre un résultat fixe comme valeur de retour. Cela rend tous les exemples de code ci-dessus, effectivement inutile. La seule chose qui serait retourné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;

    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 TOUTE optimisation à la compilation pour cette seule fonction, mais je ne sais pas. Sinon, il est peut-être possible de la coder en dur en assembleur, bien que ce ne soit pas portable. Et même dans ce cas, 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 processeurs/ensembles d'instructions existants, et bien.... peu importe.

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

Je déteste et j'aime le codage en même temps...

11 votes

Ne devez-vous pas de toute façon recompiler pour fonctionner sur une autre plateforme ?

2 votes

Bien que cela fonctionne bien pour MSVC, ce n'est pas le cas pour toutes les versions de GCC dans toutes les circonstances. Par conséquent, un "run-time check" à l'intérieur d'une boucle critique peut être correctement débranché au moment de la compilation, ou pas. Il n'y a pas de garantie à 100%.

0 votes

@bobobobo Pas nécessairement, je pourrais compiler sur une version bizarre d'Ubuntu pour un processeur x86 big-endian et ensuite mettre le programme sur Ubuntu pour un processeur x86 little-endian. Le fait qu'ils soient tous les deux x86 signifie que le code d'octet est le même pour le processeur et, en tant que tel, les entiers seront interprétés différemment.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X