我只是想问一下我的方法是否正确,从小端到大端转换,只是为了确保我理解差异 .
我有一个存储在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 回答
解决此问题的一种稍微不同的方法有时可能是有用的是具有十六或三十二位值和一组字符的并集 . 我刚刚收到带有大端序的串行消息时,我正在做这个,但我正在研究一个小端的微型 .
union MessageLengthUnion {
};
然后,当我收到消息时,我将第一个接收到的uint8放在.asChars [1]中,第二个放在.asChars [0]中,然后我在程序的其余部分中将其作为联合的.asInt部分访问 . 如果你有一个32位的值存储,你可以让数组长四 .
还有一个建议:
OP的示例代码不正确 .
字节序转换在位和8位字节级工作 . 大多数字节序问题涉及字节级别 . OP代码在4位半字节级别进行字节序更改 . 推荐:
如果性能真的很重要,则需要知道特定的处理器 . 否则,将其留给编译器 .
[编辑] 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位数字的字节序是未知的,结果需要以小端顺序存储在内存中 .
[2016年编辑]简化
在移位常数(右操作数)之后使用
u
导致与没有它的情况相同 .我想你可以使用函数
htonl()
. 网络字节顺序是大端 ."I swap each bytes right?" - >是的,要在little和big endian之间进行转换,你只需给出相反顺序的字节 . 但起初意识到很少:
uint32_t
的大小是32位,即4个字节,即8个十六进制数字mask
0xf
检索4个最低有效位,检索8位,需要0xff
所以如果你想用这种掩码交换4个字节的顺序,你可以:
对不起,我的回答有点太晚,但似乎没有人提到内置函数来反转字节顺序,这在 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 .这是一个例子:
这是它产生的输出:
这里是反汇编(没有优化,即
-O0
):确实只有一条
BSWAP
指令 .所以,如果我们关心 performance ,我们应该 use those built-in functions instead 任何其他字节反转方法 . 只需2美分 .
你可以这样做:
我假设你在linux上
包含
"byteswap.h"
并使用int32_t bswap_32(int32_t argument);
这是逻辑观点,实际上看,
/usr/include/byteswap.h
OP的代码不正确,原因如下:
交换正在半字节(4位)边界上执行,而不是字节(8位)边界 .
最后四次交换的左移
<<
操作不正确,它们应该是右移>>
操作,并且还需要纠正它们的移位值 .不需要使用中间存储,因此可以重写代码以使其更简洁/可识别 . 这样做,一些编译器将能够通过识别经常使用的模式来更好地优化代码 .
考虑以下代码,它有效地转换无符号值:
这里以二进制和十六进制表示结果,注意字节是如何交换的:
优化
在性能方面,请尽可能将其留给编译器来优化代码 . 对于像这样的简单算法,你应该避免不必要的数据结构,比如数组,这样做通常会导致不同的指令行为,例如访问RAM而不是使用CPU寄存器 .
您可以使用lib函数 . 它们归结为汇编,但如果您对C中的替代实现持开放态度,那么它们(假设int是32位):
使用方式如下:
一个简单的C程序,从小到大转换