C语言 将小端转换为大端

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19275955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:40:56  来源:igfitidea点击:

Convert Little Endian to Big Endian

cendianness

提问by JeckyPorter

I just want to ask if my method is correct to convert from little endian to big endian, just to make sure if I understand the difference.

我只是想问问我的方法是否正确将小端转换为大端,以确保我是否理解差异。

I have a number which is stored in little-endian, here are the binary and hex representations of the number:

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

?0001 0010 0011 0100 0101 0110 0111 1000?

?12345678?

In big-endian format I believe the bytes should be swapped, like this:

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

1000 0111 0110 0101 0100 0011 0010 0001

?87654321

Is this correct?

这样对吗?

Also, the code below attempts to do this but fails. Is there anything obviously wrong or can I optimize something? If the code is bad for this conversion can you please explain why and show a better method of performing the same 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);

回答by chux - Reinstate Monica

OP's sample code is incorrect.

OP 的示例代码不正确。

Endian conversion works at the bit and 8-bit byte level. Most endian issues deal with the byte level. OP code is doing a endian change at the 4-bit nibble level. Recommend instead:

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

If performance is truly important, the particular processor would need to be known. Otherwise, leave it to the compiler.

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

[Edit] OP added a comment that changes things.
"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)."

[编辑] OP 添加了改变事物的评论。
“以十六进制表示(st uv wx yz)表示的32位数值应记录在四字节字段中为(st uv wx yz)。”

It appears in this case, the endian of the 32-bit number is unknownand the result needs to be store in memory in littleendian order.

在这种情况下,32位数字的endian是未知的,结果需要以littleendian的顺序存储在内存中。

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 Edit] Simplification

[2016 编辑] 简化

... The type of the result is that of the promoted left operand.... Bitwise shift operators C11 §6.5.7 3

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

Using a uafter the shiftconstants (right operands) results in the same as without it.

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

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

回答by aoak

I think you can use function htonl(). Network byte order is big endian.

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

回答by Andriy Berestovskyy

Sorry, my answer is a bit too late, but it seems nobody mentioned built-in functions to reverse byte order, which in very important in terms of performance.

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

Most of the modern processors are little-endian, while all network protocols are big-endian. That is history and more on that you can find on Wikipedia.But that means our processors convert between little- and big-endian millions of times while we browse the Internet.

大多数现代处理器都是小端的,而所有网络协议都是大端的。这是历史,您可以在维基百科找到更多相关信息但这意味着我们的处理器在浏览 Internet 时会在小端和大端之间转换数百万次。

That is why most architectures have a dedicated processor instructions to facilitate this task. For x86 architectures there is BSWAPinstruction, and for ARMs there is REV. This is the most efficient way to reverse byte order.

这就是为什么大多数架构都有专用的处理器指令来促进这项任务。对于 x86 架构有BSWAP指令,对于 ARM 有REV. 这是反转字节顺序的最有效方法

To avoid assembly in our C code, we can use built-ins instead. For GCC there is __builtin_bswap32()function and for Visual C++ there is _byteswap_ulong(). Those function will generate just one processor instructionon most architectures.

为了避免在我们的 C 代码中进行汇编,我们可以改用内置函数。对于 GCC 有__builtin_bswap32()函数,对于 Visual C++ 有_byteswap_ulong(). 在大多数架构上,这些函数只会生成一条处理器指令

Here is an example:

下面是一个例子:

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

Here is the output it produces:

这是它产生的输出:

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

And here is the disassembly (without optimization, i.e. -O0):

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

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

There is just one BSWAPinstruction indeed.

BSWAP确实只有一个指令。

So, if we do care about the performance, we should use those built-in functions insteadof any other method of byte reversing. Just my 2 cents.

因此,如果我们确实关心性能,我们应该使用那些内置函数而不是任何其他字节反转方法。只有我的 2 美分。

回答by LihO

"I swap each bytes right?"-> yes, to convert between little and big endian, you just give the bytes the opposite order. But at first realize few things:

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

  • size of uint32_tis 32bits, which is 4 bytes, which is 8 HEX digits
  • mask 0xfretrieves the 4 least significant bits, to retrieve 8 bits, you need 0xff
  • 大小uint32_t为 32 位,即 4 个字节,即 8 个 HEX 数字
  • mask0xf检索 4 个最低有效位,要检索 8 个位,您需要0xff

so in case you want to swap the order of 4 bytes with that kind of masks, you could:

因此,如果您想用这种掩码交换 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 ;

回答by ayyappa ch

You could do this:

你可以这样做:

int x = 0x12345678;

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

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

回答by 0x47-sci-tech

I am assuming you are on linux

我假设你在 linux 上

Include "byteswap.h"& Use int32_t bswap_32(int32_t argument);

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

It is logical view, In actual see, /usr/include/byteswap.h

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

回答by DiBosco

One slightly different way of tackling this that can sometimes be useful is to have a union of the sixteen or thirty-two bit value and an array of chars. I've just been doing this when getting serial messages that come in with big endian order, yet am working on a little endian micro.

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

union MessageLengthUnion
{

    uint16_t asInt;
    uint8_t asChars[2];

};

Then when I get the messages in I put the first received uint8 in .asChars[1], the second in .asChars[0] then I access it as the .asInt part of the union in the rest of my program.

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

If you have a thirty-two bit value to store you can have the array four long.

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

回答by Shaun Wilson

OP's code is incorrect for the following reasons:

由于以下原因,OP 的代码不正确:

  • The swaps are being performed on a nibble (4-bit) boundary, instead of a byte (8-bit) boundary.
  • The shift-left <<operations of the final four swaps are incorrect, they should be shift-right >>operations and their shift values would also need to be corrected.
  • The use of intermediary storage is unnecessary, and the code can therefore be rewritten to be more concise/recognizable. In doing so, some compilers will be able to better-optimize the code by recognizing the oft-used pattern.
  • 交换是在半字节(4 位)边界而不是字节(8 位)边界上执行的。
  • <<最后四次swap的左移操作不正确,应该是右移>>操作,他们的移位值也需要修正。
  • 不需要使用中间存储,因此可以重写代码以使其更简洁/可识别。这样做时,一些编译器将能够通过识别经常使用的模式来更好地优化代码。

Consider the following code, which efficiently converts an unsigned value:

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

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

The result is represented here in both binary and hex, notice how the bytes have swapped:

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

?0111 1000 0101 0110 0011 0100 0001 0010?

78563412

Optimizing

优化

In terms of performance, leave it to the compiler to optimize your code when possible. You should avoid unnecessary data structures like arrays for simple algorithms like this, doing so will usually cause different instruction behavior such as accessing RAM instead of using CPU registers.

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

回答by Saurabh Sengar

one more suggestion :

还有一个建议:

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

回答by Naresh pothula

A Simple C program to convert from little to big

一个从小到大的简单 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;
}