Comment convertir entre les valeurs big-endian et little-endian en C ++?
EDIT: pour plus de clarté, je dois traduire des données binaires (valeurs à virgule flottante double précision et entiers 32 bits et 64 bits) d'une architecture CPU à une autre. Cela n'implique pas la mise en réseau, donc ntoh () et les fonctions similaires ne fonctionneront pas ici.
EDIT # 2: La réponse que j'ai acceptée s'applique directement aux compilateurs que je cible (c'est pourquoi je l'ai choisi). Cependant, il existe d'autres très bonnes réponses, plus portables ici.
c++
endianness
Uhall
la source
la source
short swap(short x)
code, car il se cassera si vous passez à une plate-forme avec une endianité différente. Matthieu M a la seule bonne réponse ci-dessous.Réponses:
Si vous utilisez Visual C ++, procédez comme suit: Vous incluez intrin.h et appelez les fonctions suivantes:
Pour les nombres à 16 bits:
Pour les nombres 32 bits:
Pour les nombres 64 bits:
Les nombres à 8 bits (caractères) n'ont pas besoin d'être convertis.
De plus, ceux-ci 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 que pour les entiers simples, car ceux-ci peuvent ou non être dans l'ordre des octets des machines hôtes. Vous pouvez obtenir des flotteurs little-endian sur des machines big-endian et vice versa.
D'autres compilateurs ont également des caractéristiques intrinsèques similaires.
Dans GCC, par exemple, vous pouvez appeler directement certains programmes intégrés comme indiqué ici :
(pas besoin d'inclure quelque chose). Afaik bits.h déclare également la même fonction de manière non gcc-centrique.
Swap 16 bits c'est juste une rotation de bits.
Appeler les intrinsèques au lieu de lancer les vôtres vous offre les meilleures performances et densité de code entre les deux.
la source
__builtin_bswapX
est uniquement disponible à partir de GCC-4.3htonl
,htons
, etc. Vous devez savoir à partir du contexte de votre situation quand échanger réellement les octets.htonl
etntohl
sans se soucier du contexte fonctionnerait lors de l'écriture de code portable puisque la plate-forme définissant ces fonctions le permuterait si c'est petit / moyen-endian et sur gros-boutien ce serait un no-op. Cependant, lors du décodage d'un type de fichier standard qui est défini comme petit-boutien (par exemple BMP), il faut toujours connaître le contexte et ne peut pas simplement compter surhtonl
etntohl
.Tout simplement:
utilisation:
swap_endian<uint32_t>(42)
.la source
Extrait de The Byte Order Fallacy de Rob Pike:
TL; DR: ne vous inquiétez pas de l'ordre natif de votre plate-forme, tout ce qui compte est l'ordre des octets du flux à partir duquel vous lisez, et vous feriez mieux d'espérer qu'il soit bien défini.
Remarque: il a été remarqué dans le commentaire qu'en l'absence de conversion de type explicite, il était important que ce
data
soit un tableau deunsigned char
ouuint8_t
. L'utilisation designed char
ouchar
(si signé) entraînera ladata[x]
promotion en un entier etdata[x] << 24
éventuellement le décalage d'un 1 dans le bit de signe qui est UB.la source
Si vous faites cela à des fins de compatibilité réseau / hôte, vous devez utiliser:
Si vous faites cela pour une autre raison, l'une des solutions byte_swap présentées ici fonctionnerait très bien.
la source
htonl
etntohl
ne peut pas aller au petit endian sur une plate-forme big-endian.J'ai pris quelques suggestions de ce post et les ai rassemblées pour former ceci:
la source
La procédure pour passer du big-endian au little-endian est la même que pour passer du petit-endian au big-endian.
Voici un exemple de code:
la source
Il existe une instruction d'assemblage appelée BSWAP qui fera l'échange pour vous, extrêmement rapidement . Vous pouvez en lire plus ici .
Visual Studio, ou plus précisément la bibliothèque d'exécution Visual C ++, a pour cela des intrinsèques de plate-forme, appelés
_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
. Des similaires devraient exister pour d'autres plateformes, mais je ne sais pas comment elles s'appelleraient.la source
Nous l'avons fait avec des modèles. Vous pouvez faire quelque chose comme ça:
la source
Si vous faites cela pour transférer des données entre différentes plates-formes, regardez les fonctions ntoh et hton.
la source
De la même manière que vous le faites en C:
Vous pouvez également déclarer un vecteur de caractères non signés, y mémoriser la valeur d'entrée, inverser les octets dans un autre vecteur et mémoriser les octets, mais cela prendra des ordres de grandeur plus longtemps que le twiddling de bits, en particulier avec les valeurs 64 bits.
la source
Sur la plupart des systèmes POSIX (ce n'est pas dans la norme POSIX), il y a le fichier endian.h, qui peut être utilisé pour déterminer le codage utilisé par votre système. De là, c'est quelque chose comme ça:
Cela permute l'ordre (du gros endian au petit endian):
Si vous avez le numéro 0xDEADBEEF (sur un petit système endian stocké en tant que 0xEFBEADDE), ptr [0] sera 0xEF, ptr [1] est 0xBE, etc.
Mais si vous souhaitez l'utiliser pour la mise en réseau, alors htons, htonl et htonll (et leurs inverses ntohs, ntohl et ntohll) seront utiles pour la conversion de l'ordre de l'hôte à l'ordre du réseau.
la source
htonl
et amis, que le cas d'utilisation ait quelque chose à voir avec la mise en réseau. L'ordre des octets réseau est big-endian, il suffit donc de traiter ces fonctions comme host_to_be et be_to_host. (Cela n'aide pas si vous avez besoin de host_to_le, cependant.)Notez que, au moins pour Windows, htonl () est beaucoup plus lent que son homologue intrinsèque _byteswap_ulong (). Le premier est un appel de bibliothèque DLL dans ws2_32.dll, le second est une instruction d'assemblage BSWAP. Par conséquent, si vous écrivez du code dépendant de la plate-forme, préférez utiliser les intrinsèques pour la vitesse:
Cela peut être particulièrement important pour le traitement d'image .PNG où tous les entiers sont enregistrés dans Big Endian avec l'explication "On peut utiliser htonl () ..." {pour ralentir les programmes Windows typiques, si vous n'êtes pas préparé}.
la source
La plupart des plates-formes ont un fichier d'en-tête système qui fournit des fonctions d'octetswap efficaces. Sous Linux, c'est dans
<endian.h>
. Vous pouvez bien l'envelopper en C ++:Production:
la source
j'aime celui-ci, juste pour le style :-)
la source
char[]
disant "Erreur: le type incomplet n'est pas autorisé"Sérieusement ... je ne comprends pas pourquoi toutes les solutions sont si compliquées ! Que diriez-vous de la fonction de modèle la plus simple et la plus générale qui permute tout type de n'importe quelle taille en toutes circonstances dans n'importe quel système d'exploitation ????
C'est le pouvoir magique du C et du C ++ ensemble! Échangez simplement la variable d'origine caractère par caractère.
Point 1 : Aucun opérateur: N'oubliez pas que je n'ai pas utilisé l'opérateur d'affectation simple "=" car certains objets seront gâchés lorsque l'endianisme sera inversé et le constructeur de copie (ou l'opérateur d'affectation) ne fonctionnera pas. Par conséquent, il est plus fiable de les copier caractère par caractère.
Point 2 : Soyez conscient des problèmes d'alignement: notez que nous copions vers et depuis un tableau, ce qui est la bonne chose à faire car le compilateur C ++ ne garantit pas que nous pouvons accéder à la mémoire non alignée (cette réponse a été mise à jour à partir de son original formulaire pour cela). Par exemple, si vous allouez
uint64_t
, votre compilateur ne peut pas garantir que vous pouvez accéder au 3e octet de cela en tant queuint8_t
. Par conséquent, la bonne chose à faire est de le copier dans un tableau de caractères, de l'échanger, puis de le recopier (donc nonreinterpret_cast
). Notez que les compilateurs sont généralement assez intelligents pour convertir ce que vous avez fait enreinterpret_cast
s'ils sont capables d'accéder à des octets individuels indépendamment de l'alignement.Pour utiliser cette fonction :
et
x
est maintenant différent dans l'endianisme.la source
new
/delete
pour allouer un tampon pour cela?!?sizeof(var)
est une constante de compilation, vous pouvez donc le fairechar varSwapped[sizeof(var)]
. Ou vous pourriez fairechar *p = reinterpret_cast<char*>(&var)
et échanger sur place.for(size_t i = 0 ; i < sizeof(var) ; i++)
au lieu d'unstatic_cast<long>
. (Ou en fait, le swap sur place utilisera un ordre croissant et décroissantchar*
pour que cela disparaisse quand même).J'ai ce code qui me permet de convertir de HOST_ENDIAN_ORDER (quel qu'il soit) en LITTLE_ENDIAN_ORDER ou BIG_ENDIAN_ORDER. J'utilise un modèle, donc si j'essaie de convertir de HOST_ENDIAN_ORDER en LITTLE_ENDIAN_ORDER et qu'ils se trouvent être les mêmes pour la machine pour laquelle je compile, aucun code ne sera généré.
Voici le code avec quelques commentaires:
la source
Si un entier non signé big-endian 32 bits ressemble à 0xAABBCCDD qui est égal à 2864434397, alors ce même entier non signé 32 bits ressemble à 0xDDCCBBAA sur un processeur little-endian qui est également égal à 2864434397.
Si un court-circuit non signé 16 bits big-endian ressemble à 0xAABB qui est égal à 43707, alors ce même short non-signé 16 bits ressemble à 0xBBAA sur un processeur little-endian qui est également égal à 43707.
Voici quelques fonctions #define pratiques pour échanger des octets de little-endian à big-endian et vice-versa ->
la source
Voici une version généralisée que j'ai inventée du haut de ma tête, pour échanger une valeur en place. Les autres suggestions seraient meilleures si les performances sont un problème.
Avertissement: je n'ai pas encore essayé de le compiler ou de le tester.
la source
Si vous prenez le modèle commun pour inverser l'ordre des bits dans un mot et supprimez la partie qui inverse les bits dans chaque octet, vous vous retrouvez avec quelque chose qui inverse uniquement les octets dans un mot. Pour 64 bits:
Le compilateur devrait nettoyer les opérations de masquage de bits superflues (je les ai laissées pour mettre en surbrillance le modèle), mais si ce n'est pas le cas, vous pouvez réécrire la première ligne de cette façon:
Cela devrait normalement se réduire à une seule instruction de rotation sur la plupart des architectures (sans tenir compte du fait que toute l'opération est probablement une instruction).
Sur un processeur RISC, les grandes constantes compliquées peuvent entraîner des difficultés de compilation. Cependant, vous pouvez calculer trivialement chacune des constantes de la précédente. Ainsi:
Si vous le souhaitez, vous pouvez l'écrire en boucle. Ce ne sera pas efficace, mais juste pour le plaisir:
Et pour être complet, voici la version 32 bits simplifiée du premier formulaire:
la source
Je pensais juste avoir ajouté ma propre solution ici car je ne l'ai vue nulle part. C'est une petite fonction portable et basée sur un modèle C ++ et portable qui utilise uniquement des opérations de bits.
la source
Je suis vraiment surpris que personne n'ait mentionné les fonctions htobeXX et betohXX. Ils sont définis dans endian.h et sont très similaires aux fonctions réseau htonXX.
la source
En utilisant les codes ci-dessous, vous pouvez facilement basculer entre BigEndian et LittleEndian
la source
J'ai récemment écrit une macro pour le faire en C, mais elle est également valable en C ++:
Il accepte n'importe quel type et inverse les octets dans l'argument passé. Exemples d'utilisations:
Qui imprime:
Ce qui précède est parfaitement copiable / collable, mais il se passe beaucoup de choses ici, donc je vais vous expliquer comment cela fonctionne pièce par pièce:
La première chose notable est que la macro entière est enfermée dans un
do while(0)
bloc. Ceci est un idiome commun permettant l'utilisation normale des points-virgules après la macro.Ensuite, l'utilisation d'une variable nommée
REVERSE_BYTES
commefor
compteur de boucle. Le nom de la macro lui-même est utilisé comme nom de variable pour garantir qu'il n'entre pas en conflit avec d'autres symboles qui peuvent être dans le champ d'application partout où la macro est utilisée. Étant donné que le nom est utilisé dans l'expansion de la macro, il ne sera pas développé à nouveau lorsqu'il est utilisé comme nom de variable ici.Dans la
for
boucle, deux octets sont référencés et XOR échangés (un nom de variable temporaire n'est donc pas requis):__VA_ARGS__
représente tout ce qui a été donné à la macro et est utilisé pour augmenter la flexibilité de ce qui peut être transmis (quoique pas beaucoup). L'adresse de cet argument est ensuite prise et transtypée en ununsigned char
pointeur pour permettre l'échange de ses octets via un[]
indice de tableau .Le dernier point particulier est le manque d'
{}
appareils orthopédiques. Ils ne sont pas nécessaires car toutes les étapes de chaque échange sont jointes à l' opérateur virgule , ce qui en fait une instruction.Enfin, il convient de noter que ce n'est pas l'approche idéale si la vitesse est une priorité absolue. S'il s'agit d'un facteur important, certaines des macros spécifiques au type ou des directives spécifiques à la plate-forme référencées dans d'autres réponses sont probablement une meilleure option. Cette approche, cependant, est portable pour tous les types, toutes les principales plates-formes et les langages C et C ++.
la source
__VA_ARGS__
?Wow, je ne pouvais pas croire certaines des réponses que j'ai lues ici. Il y a en fait une instruction d'assemblage qui le fait plus rapidement qu'autre chose. bswap. Vous pouvez simplement écrire une fonction comme celle-ci ...
C'est BEAUCOUP plus rapide que les intrinsèques qui ont été suggérées. Je les ai démontés et j'ai regardé. La fonction ci-dessus n'a pas de prologue / épilogue et n'a donc pratiquement aucun frais généraux.
Faire 16 bits est tout aussi simple, à l'exception que vous utiliseriez xchg al, ah. bswap ne fonctionne que sur les registres 32 bits.
64 bits est un peu plus délicat, mais pas trop. Beaucoup mieux que tous les exemples ci-dessus avec des boucles et des modèles, etc.
Il y a quelques mises en garde ici ... Tout d'abord, bswap n'est disponible que sur les processeurs 80x486 et supérieurs. Est-ce que quelqu'un envisage de le faire fonctionner sur un 386?!? Si c'est le cas, vous pouvez toujours remplacer bswap par ...
L'assemblage en ligne n'est également disponible qu'en code x86 dans Visual Studio. Une fonction nue ne peut pas être doublée et n'est pas non plus disponible dans les versions x64. Dans ce cas, vous allez devoir utiliser les caractéristiques intrinsèques du compilateur.
la source
_byteswap_ulong
et_uint64
(par exemple dans la réponse acceptée) les deux compilent pour utiliser l'bswap
instruction. Je serais surpris mais intéressé de savoir si cet asm est beaucoup plus rapide car il n'omet que le prologue / l'épilogue - l'avez-vous évalué?Technique portable pour implémenter des accesseurs endiens non alignés non alignés et optimisés. Ils fonctionnent sur chaque compilateur, chaque alignement de frontière et chaque ordre d'octets. Ces routines non alignées sont complétées ou évoquées, selon l'endian natif et l'alignement. Liste partielle mais vous avez l'idée. BO * sont des valeurs constantes basées sur l'ordre des octets natifs.
Ces typedefs ont l'avantage de générer des erreurs de compilation s'ils ne sont pas utilisés avec des accesseurs, atténuant ainsi les bogues oubliés des accesseurs.
la source
Voici comment lire un double stocké au format IEEE 754 64 bits, même si votre ordinateur hôte utilise un système différent.
Pour le reste de la suite de fonctions, y compris les routines d'écriture et d'entier, voir mon projet github
https://github.com/MalcolmMcLean/ieee754
la source
L'échange d'octets avec une astuce en trois étapes autour d'un pivot dans une fonction de modèle donne une solution O (ln2) flexible et rapide qui ne nécessite pas de bibliothèque, le style ici rejette également les types à 1 octet:
la source
Il semble que le moyen le plus sûr serait d'utiliser des htons sur chaque mot. Donc, si vous avez ...
Ce qui précède serait un no-op si vous étiez sur un système big-endian, donc je chercherais tout ce que votre plate-forme utilise comme condition de compilation pour décider si htons est un no-op. C'est O (n) après tout. Sur un Mac, ce serait quelque chose comme ...
la source
Si vous avez C ++ 17, ajoutez cet en-tête
Utilisez cette fonction de modèle pour échanger les octets:
appelez-le comme:
la source
Recherchez le décalage de bits, car c'est essentiellement tout ce que vous devez faire pour échanger de petit -> gros endian. Ensuite, en fonction de la taille du bit, vous modifiez la façon dont vous effectuez le décalage des bits.
la source