Je veux juste demander si ma méthode est correcte pour convertir de little endian en big endian, juste pour m'assurer que je comprends la différence.
J'ai un numéro qui est stocké dans little-endian, voici les représentations binaires et hexagonales du nombre:
0001 0010 0011 0100 0101 0110 0111 1000
12345678
Au format big-endian, je pense que les octets doivent être permutés, comme ceci:
1000 0111 0110 0101 0100 0011 0010 0001
87654321
Est-ce correct?
En outre, le code ci-dessous tente de le faire mais échoue. Y a-t-il quelque chose qui cloche ou puis-je optimiser quelque chose? Si le code est mauvais pour cette conversion, pouvez-vous expliquer pourquoi et indiquer une meilleure méthode pour effectuer la même conversion?
uint32_t num = 0x12345678;
uint32_t b0,b1,b2,b3,b4,b5,b6,b7;
uint32_t res = 0;
b0 = (num & 0xf) << 28;
b1 = (num & 0xf0) << 24;
b2 = (num & 0xf00) << 20;
b3 = (num & 0xf000) << 16;
b4 = (num & 0xf0000) << 12;
b5 = (num & 0xf00000) << 8;
b6 = (num & 0xf000000) << 4;
b7 = (num & 0xf0000000) << 4;
res = b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7;
printf("%d\n", res);
Le code exemple d'OP est incorrect.
La conversion Endian fonctionne au niveau des bits et des octets 8 bits. La plupart des problèmes endian concernent le niveau d'octet. Le code OP effectue un changement final au niveau du quartet 4 bits. Recommander à la place:
// Swap endian (big to little) or (little to big)
uint32_t num = 9;
uint32_t b0,b1,b2,b3;
uint32_t res;
b0 = (num & 0x000000ff) << 24u;
b1 = (num & 0x0000ff00) << 8u;
b2 = (num & 0x00ff0000) >> 8u;
b3 = (num & 0xff000000) >> 24u;
res = b0 | b1 | b2 | b3;
printf("%" PRIX32 "\n", res);
Si les performances sont vraiment importantes, le processeur en question devra être connu. Sinon, laissez le compilateur.
[Modifier] OP a ajouté un commentaire qui change les choses.
"La valeur numérique 32 bits représentée par la représentation hexadécimale (st uv wx yz) doit être enregistrée dans un champ de quatre octets sous la forme (st uv wx yz)."
Il semble que dans ce cas, l’endian du nombre 32 bits est unknown et le résultat doit être stocké en mémoire dans peu ordre endian.
uint32_t num = 9;
uint8_t b[4];
b[0] = (uint8_t) (num >> 0u);
b[1] = (uint8_t) (num >> 8u);
b[2] = (uint8_t) (num >> 16u);
b[3] = (uint8_t) (num >> 24u);
[2016 Éditer] Simplification
... Le type du résultat est celui de l'opérande gauche promu .... Opérateurs de décalage binaire C11 §6.5.7 3
L'utilisation de u
après le shift constantes (opérandes de droite) donne le même résultat que sans.
b3 = (num & 0xff000000) >> 24u;
b[3] = (uint8_t) (num >> 24u);
// same as
b3 = (num & 0xff000000) >> 24;
b[3] = (uint8_t) (num >> 24);
Je pense que vous pouvez utiliser la fonction htonl()
. L'ordre des octets du réseau est big endian.
"J'échange chaque octet non?" -> oui, pour convertir entre le petit et le gros endian, il suffit de donner aux octets l’ordre inverse . Mais au début, réalisez quelques petites choses:
uint32_t
est de 32 bits, soit 4 octets, soit 8 chiffres HEX.0xf
récupère les 4 bits les moins significatifs, pour récupérer 8 bits, vous avez besoin de 0xff
ainsi, si vous voulez échanger l'ordre de 4 octets avec ce type de masque, vous pouvez:
uint32_t res = 0;
b0 = (num & 0xff) << 24; ; least significant to most significant
b1 = (num & 0xff00) << 8; ; 2nd least sig. to 2nd most sig.
b2 = (num & 0xff0000) >> 8; ; 2nd most sig. to 2nd least sig.
b3 = (num & 0xff000000) >> 24; ; most sig. to least sig.
res = b0 | b1 | b2 | b3 ;
Vous pourriez faire ceci:
int x = 0x12345678;
x = ( x >> 24 ) | (( x << 8) & 0x00ff0000 )| ((x >> 8) & 0x0000ff00) | ( x << 24) ;
printf("value = %x", x); // x will be printed as 0x78563412
Je suppose que vous êtes sur Linux
Inclure "byteswap.h"
& Utiliser int32_t bswap_32(int32_t argument);
C'est une vue logique, voir, /usr/include/byteswap.h
<<
des quatre derniers swaps sont incorrectes. Elles doivent être des opérations shift-right >>
et leurs valeurs de décalage doivent également être corrigées.// Swap endian (big to little) or (little to big)
uint32_t num = 0x12345678;
uint32_t res =
((num & 0x000000FF) << 16) |
((num & 0x0000FF00) << 8) |
((num & 0x00FF0000) >> 8) |
((num & 0xFF000000) >> 16);
printf("%0x\n", res);
Le résultat est représenté ici en binaire et en hexadécimal, remarquez comment les octets ont été échangés:
0111 1000 0101 0110 0011 0100 0001 0010
78563412
En termes de performances, laissez au compilateur le soin d’optimiser votre code lorsque cela est possible. Évitez les structures de données inutiles telles que les tableaux pour les algorithmes simples comme celui-ci. Cela entraînerait généralement un comportement d'instruction différent, tel que l'accès à RAM au lieu d'utiliser des registres de la CPU.
Une manière légèrement différente de résoudre ce problème qui peut parfois être utile consiste à avoir une union de la valeur de seize ou trente-deux bits et un tableau de caractères. Je viens de faire cela lorsque je reçois des messages série entrants avec un ordre big endian, mais je travaille sur un petit micro endian.
union MessageLengthUnion {
uint16_t asInt;
uint8_t asChars[2];
};
Puis, lorsque je reçois les messages, je mets le premier reçu uint8 dans .asChars [1], le second dans .asChars [0], puis je l’accède en tant que partie .asInt du syndicat dans le reste de mon programme. Si vous avez une valeur de trente-deux bits à stocker, vous pouvez avoir un tableau long de quatre.
Un programme simple C pour convertir de petit à grand
#include <stdio.h>
int main() {
unsigned int little=0x1234ABCD,big=0;
unsigned char tmp=0,l;
printf(" Little endian little=%x\n",little);
for(l=0;l < 4;l++)
{
tmp=0;
tmp = little | tmp;
big = tmp | (big << 8);
little = little >> 8;
}
printf(" Big endian big=%x\n",big);
return 0;
}
une autre suggestion:
unsigned int a = 0xABCDEF23;
a = ((a&(0x0000FFFF)) << 16) | ((a&(0xFFFF0000)) >> 16);
a = ((a&(0x00FF00FF)) << 8) | ((a&(0xFF00FF00)) >>8);
printf("%0x\n",a);
Vous pouvez utiliser les fonctions lib. Ils se résument à Assembly, mais si vous êtes ouvert à d'autres implémentations en C, les voici (en supposant que int est 32 bits):
void byte_swap16(unsigned short int *pVal16) {
//#define method_one 1
// #define method_two 1
#define method_three 1
#ifdef method_one
unsigned char *pByte;
pByte = (unsigned char *) pVal16;
*pVal16 = (pByte[0] << 8) | pByte[1];
#endif
#ifdef method_two
unsigned char *pByte0;
unsigned char *pByte1;
pByte0 = (unsigned char *) pVal16;
pByte1 = pByte0 + 1;
*pByte0 = *pByte0 ^ *pByte1;
*pByte1 = *pByte0 ^ *pByte1;
*pByte0 = *pByte0 ^ *pByte1;
#endif
#ifdef method_three
unsigned char *pByte;
pByte = (unsigned char *) pVal16;
pByte[0] = pByte[0] ^ pByte[1];
pByte[1] = pByte[0] ^ pByte[1];
pByte[0] = pByte[0] ^ pByte[1];
#endif
}
void byte_swap32(unsigned int *pVal32) {
#ifdef method_one
unsigned char *pByte;
// 0x1234 5678 --> 0x7856 3412
pByte = (unsigned char *) pVal32;
*pVal32 = ( pByte[0] << 24 ) | (pByte[1] << 16) | (pByte[2] << 8) | ( pByte[3] );
#endif
#if defined(method_two) || defined (method_three)
unsigned char *pByte;
pByte = (unsigned char *) pVal32;
// move lsb to msb
pByte[0] = pByte[0] ^ pByte[3];
pByte[3] = pByte[0] ^ pByte[3];
pByte[0] = pByte[0] ^ pByte[3];
// move lsb to msb
pByte[1] = pByte[1] ^ pByte[2];
pByte[2] = pByte[1] ^ pByte[2];
pByte[1] = pByte[1] ^ pByte[2];
#endif
}
Et l'utilisation est effectuée comme suit:
unsigned short int u16Val = 0x1234;
byte_swap16(&u16Val);
unsigned int u32Val = 0x12345678;
byte_swap32(&u32Val);