24

我只是想问一下我的方法从小端转换到大端是否正确,只是为了确保我是否理解其中的区别。

我有一个以小端序存储的数字,这里是数字的二进制和十六进制表示:

‭0001 0010 0011 0100 0101 0110 0111 1000‬

‭12345678‬

在大端格式中,我认为应该交换字节,如下所示:

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

15 回答 15

43

OP 的示例代码不正确。

Endian 转换工作在位和 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位数字的字节序是未知的,结果需要以字节序存储在内存中。

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.7 3

在移位u常量(右操作数)之后使用 a与不使用它的结果相同。

b3 = (num & 0xff000000) >> 24u;
b[3] = (uint8_t) (num >> 24u);
// same as 
b3 = (num & 0xff000000) >> 24;
b[3] = (uint8_t) (num >> 24);
于 2013-10-09T15:25:04.300 回答
33

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

大多数现代处理器都是小端的,而所有的网络协议都是大端的。这就是历史,您可以在 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美分。

于 2018-02-14T18:10:21.277 回答
30

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

于 2013-10-09T15:24:53.300 回答
10

“我交换每个字节对吗?” -> 是的,要在小端和大端之间进行转换,你只需给字节相反的顺序。但起初意识到几件事:

  • size ofuint32_t是 32bits,也就是 4 个字节,也就是 8 个 HEX 数字
  • 掩码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 ;
于 2013-10-09T15:28:15.957 回答
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
于 2014-10-17T03:18:49.383 回答
3

解决这个问题的一种稍微不同的方法有时很有用,就是将 16 位或 32 位值与一个字符数组结合起来。当我收到带有大端序的串行消息时,我一直在这样做,但我正在研究一个小端微序。

union MessageLengthUnion
{

    uint16_t asInt;
    uint8_t asChars[2];

};

然后,当我收到消息时,我将收到的第一个 uint8 放入 .asChars[1] 中,第二个放入 .asChars[0] 中,然后我将其作为联合程序的其余部分中的 .asInt 部分进行访问。

如果要存储一个 32 位的值,则可以将数组设为 4 长。

于 2013-10-09T15:37:38.293 回答
2

我假设你在linux上

包括"byteswap.h"和使用int32_t bswap_32(int32_t argument);

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

于 2016-06-17T03:57:58.980 回答
1

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

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

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

// Swap endian (big to little) or (little to big)
uint32_t num = 0x12345678;
uint32_t res =
    ((num & 0x000000FF) << 24) |
    ((num & 0x0000FF00) << 8) |
    ((num & 0x00FF0000) >> 8) |
    ((num & 0xFF000000) >> 24);

printf("%0x\n", res);

结果在这里以二进制和十六进制表示,注意字节是如何交换的:

‭0111 1000 0101 0110 0011 0100 0001 0010‬

78563412

优化

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

于 2017-07-29T07:57:46.110 回答
1

还有一个建议:

unsigned int a = 0xABCDEF23;
a = ((a&(0x0000FFFF)) << 16) | ((a&(0xFFFF0000)) >> 16);
a = ((a&(0x00FF00FF)) << 8) | ((a&(0xFF00FF00)) >>8);
printf("%0x\n",a);
于 2015-12-30T14:56:25.307 回答
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;
}
于 2017-11-06T17:35:16.083 回答
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);
于 2017-08-10T13:19:00.267 回答
0

这是我编写的一个小功能,效果很好,它可能无法移植到每台机器或单个 cpu 指令的速度,但应该适用于大多数人。它可以处理高达 32 字节(256 位)的数字,并且适用于大端和小端交换。关于这个函数的最好的部分是你可以将它指向一个字节数组,在转换前将它插入或连接到网络中并交换字节。

#include <stdio.h>
#include <string.h>

void byteSwap(char**,int);

int main() {

    //32 bit
    int test32 = 0x12345678;
    printf("\n BigEndian = 0x%X\n",test32);

    char* pTest32 = (char*) &test32;

    //convert to little endian
    byteSwap((char**)&pTest32, 4);
    printf("\n LittleEndian = 0x%X\n", test32);

    //64 bit
    long int test64 = 0x1234567891234567LL;
    printf("\n BigEndian = 0x%lx\n",test64);

    char* pTest64 = (char*) &test64;

    //convert to little endian
    byteSwap((char**)&pTest64,8);
    printf("\n LittleEndian = 0x%lx\n",test64);

    //back to big endian
    byteSwap((char**)&pTest64,8);
    printf("\n BigEndian = 0x%lx\n",test64);

    return 0;
}


void byteSwap(char** src,int size) {
    int x = 0;
    char b[32];
    while(size-- >= 0) { b[x++] = (*src)[size]; };
    memcpy(*src,&b,x);
}

输出:

$gcc -o main *.c -lm
$main

 BigEndian = 0x12345678

 LittleEndian = 0x78563412

 BigEndian = 0x1234567891234567

 LittleEndian = 0x6745239178563412

 BigEndian = 0x1234567891234567
于 2021-09-18T12:36:03.340 回答
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 ));
    }
于 2020-09-10T16:28:54.537 回答
0

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

convertLittleEndianByteArrayToBigEndianByteArray (byte littlendianByte[], byte bigEndianByte[], int ArraySize){
    int i =0;

    for(i =0;i<ArraySize;i++){
      bigEndianByte[i] = (littlendianByte[ArraySize-i-1] << 7 & 0x80) | (littlendianByte[ArraySize-i-1] << 5 & 0x40) |
                            (littlendianByte[ArraySize-i-1] << 3 & 0x20) | (littlendianByte[ArraySize-i-1] << 1 & 0x10) |
                            (littlendianByte[ArraySize-i-1] >>1 & 0x08) | (littlendianByte[ArraySize-i-1] >> 3 & 0x04) |
                            (littlendianByte[ArraySize-i-1] >>5 & 0x02) | (littlendianByte[ArraySize-i-1] >> 7 & 0x01) ;
    }
}
于 2019-11-13T17:20:57.943 回答
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);

}
于 2021-03-24T19:36:27.233 回答