将Little Endian转换为Big Endian

时间:2013-10-09 15:13:42

标签: c endianness

我只是想问一下我的方法是否正确,从小端到大端转换,只是为了确定我是否理解差异。

我有一个存储在little-endian中的数字,这里是数字的二进制和十六进制表示:

‭0001 0010 0011 0100 0101 0110 0111 1000‬

‭12345678‬

在big-endian格式中,我认为应该交换字节,如下所示:

1000 0111 0110 0101 0100 0011 0010 0001

‭87654321

这是对的吗?

此外,下面的代码尝试执行此操作但失败。有什么明显的错误或者我可以优化一些东西吗?如果代码对于此转换有害,请解释原因并显示执行相同转换的更好方法吗?

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);

14 个答案:

答案 0 :(得分:32)

OP的示例代码不正确。

字节序转换在位和8位字节级工作。大多数字节序问题涉及字节级别。 OP代码在4位半字节级别进行字节序更改。建议改为:

// 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);

如果性能真的很重要,则需要知道特定的处理器。否则,将它留给编译器。

[编辑] OP添加了一条更改内容的评论 “由十六进制表示(st uv wx yz)表示的32位数值应记录在四字节字段中(st uv wx yz)。”

在这种情况下,32位数的字节序是 unknown ,结果需要以 little 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编辑]简化

  

...结果的类型是提升左操作数的类型....按位移位运算符C11§6.5.73

shift 常量(右操作数)之后使用u会导致与没有它的情况相同。

b3 = (num & 0xff000000) >> 24u;
b[3] = (uint8_t) (num >> 24u);
// same as 
b3 = (num & 0xff000000) >> 24;
b[3] = (uint8_t) (num >> 24);

答案 1 :(得分:23)

我认为你可以使用函数htonl()。网络字节顺序是大端。

答案 2 :(得分:10)

对不起,我的答案有点太晚了,但似乎没有人提到内置函数来反转字节顺序,这在非常重要的性能方面

大多数现代处理器都是little-endian,而所有网络协议都是big-endian。这是历史,更多的是你可以find on Wikipedia.但这意味着我们的处理器在浏览互联网时会在小端和大端之间转换数百万次。

这就是为什么大多数架构都有专门的处理器指令来完成这项任务。对于x86体系结构,存在BSWAP指令,对于ARM,存在REV。这是反转字节顺序的最有效方法

为了避免在我们的C代码中进行汇编,我们可以使用内置函数。对于GCC,有__builtin_bswap32()函数,对于Visual C ++,有_byteswap_ulong()。这些函数将在大多数体系结构上生成仅一个处理器指令

以下是一个例子:

#include <stdio.h>
#include <inttypes.h>

int main()
{
    uint32_t le = 0x12345678;
    uint32_t be = __builtin_bswap32(le);

    printf("Little-endian: 0x%" PRIx32 "\n", le);
    printf("Big-endian:    0x%" PRIx32 "\n", be);

    return 0;
}

这是它产生的输出:

Little-endian: 0x12345678
Big-endian:    0x78563412

这是反汇编(没有优化,即-O0):

        uint32_t be = __builtin_bswap32(le);
   0x0000000000400535 <+15>:    mov    -0x8(%rbp),%eax
   0x0000000000400538 <+18>:    bswap  %eax
   0x000000000040053a <+20>:    mov    %eax,-0x4(%rbp)

确实只有一条BSWAP指令。

因此,如果我们关心性能,我们应该使用那些内置函数来完成任何其他字节反转方法。我的2美分。

答案 3 :(得分:8)

“我把每个字节交换正确吗?” - &gt;是的,要在little和big endian之间进行转换,你只需给出相反顺序的字节。 但首先要意识到的事情很少:

  • uint32_t的大小为32位,即4个字节,即8个十六进制数字
  • mask 0xf检索4个最低有效位,检索8位,需要0xff

所以,如果你想用这种掩码交换4个字节的顺序,你可以:

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 ;

答案 4 :(得分:4)

你可以这样做:

int x = 0x12345678;

x = ( x >> 24 ) | (( x << 8) & 0x00ff0000 )| ((x >> 8) & 0x0000ff00) | ( x << 24)  ; 

printf("value = %x", x);  // x will be printed as 0x78563412

答案 5 :(得分:2)

我假设你在linux上

包含"byteswap.h"&amp;使用int32_t bswap_32(int32_t argument);

这是逻辑视图,实际上是看/usr/include/byteswap.h

答案 6 :(得分:1)

解决这个问题的一种稍微不同的方法,有时可能是有用的是具有十六或三十二位值和一组字符的并集。我刚刚收到带有大端序的串行消息时,我正在做这个,但我正在研究一个小端的微型。

union MessageLengthUnion {

uint16_t asInt;
uint8_t asChars[2];

};

然后,当我收到消息时,我将第一个收到的uint8放在.asChars [1]中,第二个放在.asChars [0]中,然后我在我程序的其余部分中将其作为联合的.asInt部分访问。如果你有一个32位的值存储,你可以让数组长四。

答案 7 :(得分:1)

还有一个建议:

unsigned int a = 0xABCDEF23;
a = ((a&(0x0000FFFF)) << 16) | ((a&(0xFFFF0000)) >> 16);
a = ((a&(0x00FF00FF)) << 8) | ((a&(0xFF00FF00)) >>8);
printf("%0x\n",a);

答案 8 :(得分:1)

OP的代码不正确,原因如下:

  • 交换正在半字节(4位)边界上执行,而不是字节(8位)边界。
  • 最后四次互换的左移<<操作不正确,它们应该是右移>>操作,并且还需要更正它们的移位值。
  • 不需要使用中间存储,因此可以重写代码以使其更简洁/可识别。通过这样做,一些编译器将能够通过识别经常使用的模式来更好地优化代码。

考虑以下代码,它有效地转换无符号值:

// 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);

这里以二进制和十六进制表示结果,注意字节的交换方式:

‭0111 1000 0101 0110 0011 0100 0001 0010‬

78563412

优化

就性能而言,请尽可能优化代码。对于像这样的简单算法,你应该避免不必要的数据结构,比如数组,这样做通常会导致不同的指令行为,例如访问RAM而不是使用CPU寄存器。

答案 9 :(得分:1)

一个简单的C程序,可以从小到大转换

#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;
}

答案 10 :(得分:0)

您可以使用lib功能。它们归结为汇编,但如果您对C中的替代实现持开放态度,那么它们(假设int是32位):

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
}

使用方式如下:

unsigned short int u16Val = 0x1234;
byte_swap16(&u16Val);
unsigned int u32Val = 0x12345678;
byte_swap32(&u32Val);

答案 11 :(得分:0)

下面是对我有用的另一种方法

setHttpProxy

答案 12 :(得分:0)

下面的程序根据需要产生结果:

#include <stdio.h>
 
unsigned int Little_To_Big_Endian(unsigned int num);
 
int main( )
{
    int num = 0x11223344 ;
    
    printf("\n Little_Endian = 0x%X\n",num);
    
    printf("\n Big_Endian    = 0x%X\n",Little_To_Big_Endian(num));
 
}
 
unsigned int Little_To_Big_Endian(unsigned int num)
{
    return (((num >> 24) & 0x000000ff) | ((num >> 8) & 0x0000ff00) | ((num << 8) & 0x00ff0000) | ((num << 24) & 0xff000000));
}

还可以使用以下功能:

    unsigned int Little_To_Big_Endian(unsigned int num)
    {
        return (((num & 0x000000ff) << 24) | ((num & 0x0000ff00) << 8 ) | ((num & 0x00ff0000) >> 8) | ((num & 0xff000000) >> 24 ));
    }

答案 13 :(得分:0)

#include<stdio.h>
int main(){
        int var = 0X12345678;
        var =  ((0X000000FF & var)<<24)|
               ((0X0000FF00 & var)<<8) |
               ((0X00FF0000 & var)>>8) |
               ((0XFF000000 & var)>>24);
        printf("%x",var);

}