240 votes

Comment convertir les valeurs big-endian et little-endian en C++ ?

Comment convertir les valeurs big-endian et little-endian en C++ ?

Pour plus de clarté, je dois traduire des données binaires (valeurs à virgule flottante en double précision et entiers 32 et 64 bits) d'une architecture de CPU à une autre. Cela n'implique pas de mise en réseau, donc ntoh() et les fonctions similaires ne fonctionneront pas ici.


Note : La réponse que j'ai acceptée s'applique directement aux compilateurs que je vise (c'est pourquoi je l'ai choisie). Cependant, il y a d'autres très bonnes réponses, plus portables, ici.

0 votes

Avez-vous besoin d'une conversion entre big-endian et little-endian, ou entre l'un de ces formats et votre format natif, pour d'autres traitements ?

0 votes

Il serait utile d'indiquer la plate-forme dont vous parlez.

25 votes

Ntoh hton fonctionnera très bien, même s'il n'a rien à voir avec le réseau.

203voto

Nils Pipenbrinck Points 41006

Si vous utilisez Visual C++ faites ce qui suit : Vous incluez intrin.h et appelez les fonctions suivantes :

Pour les nombres de 16 bits :

unsigned short _byteswap_ushort(unsigned short value);

Pour les nombres de 32 bits :

unsigned long _byteswap_ulong(unsigned long value);

Pour les nombres de 64 bits :

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

Les nombres de 8 bits (caractères) n'ont pas besoin d'être convertis.

De plus, ils ne sont définis que pour les valeurs non signées ; ils fonctionnent également pour les entiers signés.

Pour les flottants et les doubles, c'est plus difficile qu'avec les entiers ordinaires, car ils peuvent ou non être dans l'ordre des octets de la machine hôte. Vous pouvez obtenir des flottants little-endian sur des machines big-endian et vice versa.

D'autres compilateurs ont également des intrinsèques similaires.

Sur CCG par exemple, vous pouvez appeler directement quelques builtins comme documenté ici :

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(pas besoin d'inclure quelque chose). Je crois que bits.h déclare la même fonction d'une manière non centrée sur gcc.

Un échange de 16 bits, c'est juste une rotation de bits.

En appelant les intrinsèques au lieu de développer les vôtres, vous obtenez les meilleures performances et la meilleure densité de code

0 votes

Savez-vous si les intrinsèques de gcc sont également disponibles sur d'autres plates-formes ? c'est-à-dire, sont-ils liés à l'hôte x86 ou fonctionneraient-ils sur PPC, SPARC, etc ?

0 votes

Ils devraient fonctionner sur toutes les plateformes supportées par gcc. Si le processeur cible ne supporte pas le byteswap comme une instruction unique, le compilateur va soit mettre en ligne du code optimisé, soit appeler une fonction d'exécution.

11 votes

Avec GCC, je pourrais utiliser : #include <byteswap.h> int32_t bswap_32(int32_t x) int64_t bswap_64(int64_t x)

102voto

Alexandre C. Points 31758

C'est simple :

#include <climits>

template <typename T>
T swap_endian(T u)
{
    static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");

    union
    {
        T u;
        unsigned char u8[sizeof(T)];
    } source, dest;

    source.u = u;

    for (size_t k = 0; k < sizeof(T); k++)
        dest.u8[k] = source.u8[sizeof(T) - k - 1];

    return dest.u;
}

l'usage : swap_endian<uint32_t>(42) .

4 votes

Ayez un upvote. J'ai juste utilisé des uchars, et assigné 4 à 1, 3 à 2, 2 à 3, et 1 à 4, mais c'est plus flexible si vous avez des tailles différentes. 6 horloges sur un Pentium de 1ère génération IIRC. BSWAP est de 1 horloge, mais est spécifique à la plate-forme.

2 votes

@RocketRoy : Oui, et si la vitesse s'avère être un problème, il est très simple d'écrire des surcharges avec des intrisques spécifiques à la plateforme et au type.

0 votes

C'est l'utilisation la plus créative d'un syndicat que j'aie vue. Merci d'avoir fourni ce code !

88voto

Matthieu M. Points 101624

Desde L'erreur de l'ordre des octets par Rob Pike :

Disons que votre flux de données contient un entier de 32 bits codé en little-endian. Voici comment l'extraire (en supposant des octets non signés) :

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

Si c'est un big-endian, voici comment l'extraire :

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

TL;DR : ne vous préoccupez pas de l'ordre natif de votre plateforme, tout ce qui compte c'est l'ordre des octets du flux que vous lisez, et vous avez intérêt à ce qu'il soit bien défini.

_Note : il a été remarqué dans le commentaire qu'en l'absence de conversion explicite de type, il était important que data être un tableau de unsigned char o uint8_t . Utilisation de signed char o char (s'il est signé) aura pour résultat data[x] étant promu à un nombre entier et data[x] << 24 potentiellement en décalant un 1 dans le bit de signe qui est UB._

9 votes

C'est cool, mais il me semble que cela ne s'applique qu'aux entiers et aux variantes. Que faire avec les flottants/doubles ?

0 votes

@Brett : strictement aucune idée :)

0 votes

C'est vrai seulement parce que votre i a été créée par le compilateur sur la pile. Lorsque vous utilisez des pointeurs qui font référence à des données mappées (ou à un fichier que vous avez entièrement tiré en mémoire), vous devez également vous préoccuper de l'endianness de l'hôte, ET de l'alignement, ce qui rend les choses doublement difficiles.

60voto

Frosty Points 1624

Si vous faites cela pour des raisons de compatibilité réseau/hôte, vous devez utiliser :

ntohl() //Network to Host byte order (Long)
htonl() //Host to Network byte order (Long)

ntohs() //Network to Host byte order (Short)
htons() //Host to Network byte order (Short)

Si vous faites cela pour une autre raison, l'une des solutions byte_swap présentées ici fonctionnera très bien.

5 votes

L'ordre des octets du réseau est big endian, je crois. Ces fonctions peuvent être utilisées en gardant cela à l'esprit, même si vous n'utilisez pas de code réseau. Cependant, il n'existe pas de versions flottantes de ntohf ou htonf.

3 votes

Matt H., ce n'est que partiellement correct. Tous les systèmes informatiques n'ont pas un ordre d'octet little-endian. Si vous travaillez sur un Motorola 68k, un PowerPC ou une autre architecture big-endian, ces fonctions n'échangeront pas du tout d'octets car elles sont déjà dans l'ordre des octets du réseau.

4 votes

Malheureusement, htonl y ntohl ne peut pas passer en little endian sur une plateforme big-endian.

28voto

steve.lorimer Points 141

J'ai pris quelques suggestions de ce post et les ai rassemblées pour former ceci :

#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>
#include <cstdint>

enum endianness
{
    little_endian,
    big_endian,
    network_endian = big_endian,

    #if defined(BOOST_LITTLE_ENDIAN)
        host_endian = little_endian
    #elif defined(BOOST_BIG_ENDIAN)
        host_endian = big_endian
    #else
        #error "unable to determine system endianness"
    #endif
};

namespace detail {

template<typename T, size_t sz>
struct swap_bytes
{
    inline T operator()(T val)
    {
        throw std::out_of_range("data size");
    }
};

template<typename T>
struct swap_bytes<T, 1>
{
    inline T operator()(T val)
    {
        return val;
    }
};

template<typename T>
struct swap_bytes<T, 2>
{
    inline T operator()(T val)
    {
        return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
    }
};

template<typename T>
struct swap_bytes<T, 4>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff000000) >> 24) |
                (((val) & 0x00ff0000) >>  8) |
                (((val) & 0x0000ff00) <<  8) |
                (((val) & 0x000000ff) << 24));
    }
};

template<>
struct swap_bytes<float, 4>
{
    inline float operator()(float val)
    {
        uint32_t mem =swap_bytes<uint32_t, sizeof(uint32_t)>()(*(uint32_t*)&val);
        return *(float*)&mem;
    }
};

template<typename T>
struct swap_bytes<T, 8>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff00000000000000ull) >> 56) |
                (((val) & 0x00ff000000000000ull) >> 40) |
                (((val) & 0x0000ff0000000000ull) >> 24) |
                (((val) & 0x000000ff00000000ull) >> 8 ) |
                (((val) & 0x00000000ff000000ull) << 8 ) |
                (((val) & 0x0000000000ff0000ull) << 24) |
                (((val) & 0x000000000000ff00ull) << 40) |
                (((val) & 0x00000000000000ffull) << 56));
    }
};

template<>
struct swap_bytes<double, 8>
{
    inline double operator()(double val)
    {
        uint64_t mem =swap_bytes<uint64_t, sizeof(uint64_t)>()(*(uint64_t*)&val);
        return *(double*)&mem;
    }
};

template<endianness from, endianness to, class T>
struct do_byte_swap
{
    inline T operator()(T value)
    {
        return swap_bytes<T, sizeof(T)>()(value);
    }
};
// specialisations when attempting to swap to the same endianess
template<class T> struct do_byte_swap<little_endian, little_endian, T> { inline T operator()(T value) { return value; } };
template<class T> struct do_byte_swap<big_endian,    big_endian,    T> { inline T operator()(T value) { return value; } };

} // namespace detail

template<endianness from, endianness to, class T>
inline T byte_swap(T value)
{
    // ensure the data is only 1, 2, 4 or 8 bytes
    BOOST_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
    // ensure we're only swapping arithmetic types
    BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

    return detail::do_byte_swap<from, to, T>()(value);
}

Vous l'utiliserez alors comme suit :

// swaps val from host-byte-order to network-byte-order
auto swapped = byte_swap<host_endian, network_endian>(val);

et vice-versa

// swap a value received from the network into host-byte-order
auto val = byte_swap<network_endian, host_endian>(val_from_network);

1 votes

Vous devez également inclure <cstdint> ou <stdint.h>, par exemple, pour uint32_t

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