119 votes

convertir big endian en little endian en C [sans utiliser les fonctions fournies].

J'ai besoin d'écrire une fonction pour convertir big endian en little endian en C. Je ne peux pas utiliser de fonction de bibliothèque.

5 votes

Une valeur de 16 bits ? une valeur de 32 bits ? un flottant ? un tableau ?

25 votes

Le temps de choisir une réponse peut-être ?

9 votes

Vote pour la réouverture. Identique à stackoverflow.com/questions/105252/ pour C++. Nous pourrions juste éditer pour rendre cela plus clair.

206voto

Sam Post Points 1812

En supposant que ce dont vous avez besoin est un simple échange d'octets, essayez quelque chose comme

Conversion 16 bits non signée :

swapped = (num>>8) | (num<<8);

Conversion de 32 bits non signés :

swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
                    ((num<<8)&0xff0000) | // move byte 1 to byte 2
                    ((num>>8)&0xff00) | // move byte 2 to byte 1
                    ((num<<24)&0xff000000); // byte 0 to byte 3

Cela permute les ordres d'octets des positions 1234 à 4321. Si votre entrée était 0xdeadbeef un swap 32 bits endian pourrait avoir une sortie de 0xefbeadde .

Le code ci-dessus devrait être nettoyé avec des macros ou au moins des constantes à la place des nombres magiques, mais j'espère qu'il sera utile tel quel.

EDIT : comme l'a souligné une autre réponse, il existe des alternatives spécifiques à la plate-forme, au système d'exploitation et au jeu d'instructions qui peuvent être BEAUCOUP plus rapides que les solutions ci-dessus. Dans le noyau Linux, il y a des macros (cpu_to_be32 par exemple) qui gèrent très bien l'endiannité. Mais ces alternatives sont spécifiques à leurs environnements. En pratique, la meilleure façon de gérer l'endiveté est d'utiliser un mélange des approches disponibles.

5 votes

+1 pour la mention des méthodes spécifiques à la plate-forme ou au matériel. Les programmes sont toujours exécutés sur un certain matériel, et les caractéristiques du matériel sont toujours plus rapides.

25 votes

Si la conversion 16 bits est effectuée comme ((num & 0xff) >> 8) | (num << 8) gcc 4.8.3 génère un simple rol l'instruction. Et si la conversion 32 bits est écrite comme ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24) Le même compilateur génère un seul bswap l'instruction.

0 votes

Je ne sais pas si c'est efficace mais j'ai échangé l'ordre des octets avec des champs de bits comme ceci : struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;} où il s'agit d'un champ de bits avec 8 champs de 1 bit chacun. Mais je ne suis pas sûr que ce soit aussi rapide que les autres suggestions. Pour les ints, utilisez la fonction union { int i; byte_t[sizeof(int)]; } pour inverser octet par octet dans l'entier.

129voto

Amir Mgh Points 329

En incluant :

#include <byteswap.h>

vous pouvez obtenir une version optimisée des fonctions d'échange d'octets dépendantes de la machine. Ensuite, vous pouvez facilement utiliser les fonctions suivantes :

__bswap_32 (uint32_t input)

ou

__bswap_16 (uint16_t input)

4 votes

Merci pour votre réponse, mais je ne peux utiliser aucune fonction de la bibliothèque.

4 votes

Devrait être lu #include <byteswap.h> Voir le commentaire dans le fichier .h lui-même. Cet article contient des informations utiles, c'est pourquoi j'ai voté en faveur de l'article malgré le fait que l'auteur ait ignoré l'exigence de l'OP de ne pas utiliser une fonction lib.

34 votes

En fait, les fonctions __bswap_32/__bswap_16 sont en fait des macros et non des fonctions de bibliothèque, une autre raison de voter plus haut.

78voto

chmike Points 2514
#include <stdint.h>

//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val ) 
{
    return (val << 8) | (val >> 8 );
}

//! Byte swap short
int16_t swap_int16( int16_t val ) 
{
    return (val << 8) | ((val >> 8) & 0xFF);
}

//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
    val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | (val >> 16);
}

//! Byte swap int
int32_t swap_int32( int32_t val )
{
    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | ((val >> 16) & 0xFFFF);
}

Mise à jour : Ajout de l'échange d'octets en 64 bits

int64_t swap_int64( int64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}

uint64_t swap_uint64( uint64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | (val >> 32);
}

0 votes

Pour le int32_t y int64_t variantes, quel est le raisonnement derrière le masquage des ... & 0xFFFF y ... & 0xFFFFFFFFULL ? Y a-t-il quelque chose qui se passe avec l'extension de signe ici que je ne vois pas ? De même, pourquoi swap_int64 en retournant sur uint64_t ? Cela ne devrait-il pas être int64_t ?

1 votes

Le swap_int64 retournant un uint64 est en effet une erreur. Le masquage avec des valeurs int signées est en effet pour enlever le signe. Le décalage vers la droite injecte le bit de signe à gauche. Nous pourrions éviter cela en appelant simplement l'opération de swap des int non signés.

0 votes

Merci. Vous pourriez vouloir changer le type de la valeur de retour pour swap_int64 dans votre réponse. +1 pour la réponse utile, BTW !

11voto

kol Points 10683

Si vous avez besoin de macros (par exemple, système embarqué) :

#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))

0 votes

Ces macros fonctionnent bien, mais ((x) >> 24) échoue lorsqu'un entier signé est compris entre 0x80000000 et 0xffffffff. C'est une bonne idée d'utiliser le AND par bit ici. Remarque : ((x) << 24) est parfaitement sûr. (x) >> 8) échouera également si les 16 bits supérieurs sont non nuls (ou si une valeur 16 bits signée est fournie).

2 votes

@PacMan-- Ces macros sont destinées à être utilisées pour échanger non signé les entiers uniquement. C'est pourquoi il existe le UINT en leur nom.

1 votes

Oui, c'est vrai, désolé pour le bruit. Ne serait-il pas mieux d'intégrer un typecast ?

4voto

Chris H Points 3534

Pour tous vos besoins de manipulation de bits
http://graphics.stanford.edu/~seander/bithacks.html

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