首页 文章

将Little Endian转换为Big Endian

提问于
浏览
19

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

我有一个存储在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);

11 回答

  • 26

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

    union MessageLengthUnion {

    uint16_t asInt;
    uint8_t asChars[2];
    

    };

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

  • 1

    还有一个建议:

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

    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添加了一条改变事情的评论 .
    "32bit numerical value represented by the hexadecimal representation (st uv wx yz) shall be recorded in a four-byte field as (st uv wx yz)."

    在这种情况下,32位数字的字节序是未知的,结果需要以小端顺序存储在内存中 .

    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

    在移位常数(右操作数)之后使用 u 导致与没有它的情况相同 .

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

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

  • 1

    "I swap each bytes right?" - >是的,要在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 ;
    
  • 0

    对不起,我的回答有点太晚,但似乎没有人提到内置函数来反转字节顺序,这在 very important in terms of performance 中 .

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

    这就是为什么大多数架构都有专门的处理器指令来促进这项任务 . 对于x86体系结构,有 BSWAP 指令,对于ARM,有 REV . 这是 the most efficient way to reverse byte order .

    为了避免在我们的C代码中进行汇编,我们可以使用内置函数 . 对于GCC,有 __builtin_bswap32() 函数,对于Visual C,有 _byteswap_ulong() . 这些函数将在大多数体系结构上生成 just one processor instruction .

    这是一个例子:

    #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 指令 .

    所以,如果我们关心 performance ,我们应该 use those built-in functions instead 任何其他字节反转方法 . 只需2美分 .

  • 21

    你可以这样做:

    int x = 0x12345678;
    
    x = ( x >> 24 ) | (( x << 8) & 0x00ff0000 )| ((x >> 8) & 0x0000ff00) | ( x << 24)  ; 
    
    printf("value = %x", x);  // x will be printed as 0x78563412
    
  • 1

    我假设你在linux上

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

    这是逻辑观点,实际上看, /usr/include/byteswap.h

  • 4

    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寄存器 .

  • 6

    您可以使用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);
    
  • 0

    一个简单的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;
    }
    

相关问题