将大端转换为小端的 C/C++ 代码
Posted
技术标签:
【中文标题】将大端转换为小端的 C/C++ 代码【英文标题】:C/C++ code to convert big endian to little endian 【发布时间】:2017-12-31 15:05:39 【问题描述】:我见过几个不同的代码示例,它们可以将大端转换为小端,反之亦然,但我遇到过有人编写的一段代码,似乎可以工作,但我'我不知道为什么会这样。
基本上,有一个 char 缓冲区,在某个位置,包含一个 4 字节的 int 存储为 big-endian。该代码将提取整数并将其存储为本机小端。这是一个简短的例子:
char test[8] = 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07;
char *ptr = test;
int32_t value = 0;
value = ((*ptr) & 0xFF) << 24;
value |= ((*(ptr + 1)) & 0xFF) << 16;
value |= ((*(ptr + 2)) & 0xFF) << 8;
value |= (*(ptr + 3)) & 0xFF;
printf("value: %d\n", value);
值:66051
上面的代码获取前四个字节,将其存储为小端,并打印结果。谁能逐步解释这是如何工作的?我很困惑为什么 ((*ptr) & 0xFF) = 8 都不会评估为 0。
【问题讨论】:
因为在算术完成之前char
值被提升为int
。注意:应该使用unsigned char *
和uint32_t
。
您的代码独立于字节序,它将在大小端机器上打印66051
。 value
存储在机器的字节序中,并不总是存储在小字节序中。
& 0xFF
仅对有符号值是必需的,当负的 char
值符号扩展为 int
时,去除多余的位。使用unsigned
的原因之一,以及可疑的符号位转换。
在 x86 上可以使用 ntohl。
@WeatherVane:使用有符号字符和有符号整数确实不美观,但不会改变这个交换过程的任何功能。
【参考方案1】:
这段代码正在构造值,一次一个字节。
首先它捕获最低字节
(*ptr) & 0xFF
然后移到最高字节
((*ptr) & 0xFF) << 24
然后将它赋给之前初始化的0值。
value =((*ptr) & 0xFF) << 24
现在“魔法”开始发挥作用。由于ptr
值被声明为char*
,因此在其上加一会使指针前进一个字符。
(ptr + 1) /* the next character address */
*(ptr + 1) /* the next character */
在您看到他们使用指针数学来更新相对起始地址之后,其余操作与已经描述的操作相同,只是为了保留部分移位的值,他们将or
的值放入现有的value
变量
value |= ((*(ptr + 1)) & 0xFF) << 16
请注意,指针数学是您可以执行类似操作的原因
char* ptr = ... some value ...
while (*ptr != 0)
... do something ...
ptr++;
但它的代价是可能真的弄乱了您的指针地址,大大增加了违反 SEGFAULT 的风险。一些语言认为这是一个问题,他们删除了进行指针数学的能力。您无法对其进行指针数学运算的几乎指针通常称为引用。
【讨论】:
【参考方案2】:如果你想将小端表示转换为大端,你可以使用 htonl、htons、ntohl、ntohs。这些函数在主机和网络字节顺序之间转换值。 Big endian 也用于基于 arm 的平台。看这里:https://linux.die.net/man/3/endian
【讨论】:
据我所知,ARM 的默认字节序是小字节序。大多数都用于小端,但您可以切换到大端(在任何项目中从未见过)。 ARM 和 Intel 自 x486 以来提供本机支持交换指令。见这里:infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0210c/… 此答案仅对在 little-endian 主机上运行的代码正确。对于在大端主机上运行的代码(想到 HP-UX),htonl()
和朋友都是无操作的。
为什么没有整数相等?【参考方案3】:
您可能使用的代码基于网络上的号码应以 BIG ENDIAN 模式发送的想法。
htonl()
和 htons()
函数在 BIG ENDIAN 中转换 32 位整数和 16 位整数,其中您的系统使用 LITTLE ENDIAN,否则它们会将数字保留在 BIG ENDIAN 中。
代码如下:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <arpa/inet.h>
int main(void)
uint32_t x,y;
uint16_t s,z;
x=0xFF567890;
y=htonl(x);
printf("LE=%08X BE=%08X\n",x,y);
s=0x7891;
z=htons(s);
printf("LE=%04X BE=%04X\n",s,z);
return 0;
编写此代码是为了在 LE 机器上从 LE 转换为 BE。
您可以使用相反的函数 ntohl()
和 ntohs()
将 BE 转换为 LE,这些函数在 LE 机器上将整数从 BE 转换为 LE,而不是在 BE 机器上转换。
【讨论】:
【参考方案4】:我很困惑为什么 ((*ptr) & 0xFF) = 8 都不会计算为 0。
我认为您误解了换档功能。
value = ((*ptr) & 0xFF) << 24;
表示用 0xff(字节)屏蔽 ptr 处的值,然后移位 24 位(不是字节)。即移动 24/8 个字节(3 个字节)到最高字节。
【讨论】:
【参考方案5】:理解((*ptr) & 0xFF) << X
评价的关键之一
是Integer Promotion。值 (*ptr) & 0xff
在移动之前提升为 Integer
。
【讨论】:
【参考方案6】:我已经编写了下面的代码。此代码包含两个函数swapmem()
和swap64()
。
swapmem()
交换任意维度内存区域的字节。
swap64()
交换 64 位整数的字节。
在这个回复的最后,我告诉你一个解决字节缓冲区问题的想法。
代码如下:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <malloc.h>
void * swapmem(void *x, size_t len, int retnew);
uint64_t swap64(uint64_t k);
/**
brief swapmem
This function swaps the byte into a memory buffer.
param x
pointer to the buffer to be swapped
param len
lenght to the buffer to be swapped
param retnew
If this parameter is 1 the buffer is swapped in a new
buffer. The new buffer shall be deallocated by using
free() when it's no longer useful.
If this parameter is 0 the buffer is swapped in its
memory area.
return
The pointer to the memory area where the bytes has been
swapped or NULL if an error occurs.
*/
void * swapmem(void *x, size_t len, int retnew)
char *b = NULL, app;
size_t i;
if (x != NULL)
if (retnew)
b = malloc(len);
if (b!=NULL)
for(i=0;i<len;i++)
b[i]=*((char *)x+len-1-i);
else
b=(char *)x;
for(i=0;i<len/2;i++)
app=b[i];
b[i]=b[len-1-i];
b[len-1-i]=app;
return b;
uint64_t swap64(uint64_t k)
return ((k << 56) |
((k & 0x000000000000FF00) << 40) |
((k & 0x0000000000FF0000) << 24) |
((k & 0x00000000FF000000) << 8) |
((k & 0x000000FF00000000) >> 8) |
((k & 0x0000FF0000000000) >> 24)|
((k & 0x00FF000000000000) >> 40)|
(k >> 56)
);
int main(void)
uint32_t x,*y;
uint16_t s,z;
uint64_t k,t;
x=0xFF567890;
/* Dynamic allocation is used to avoid to change the contents of x */
y=(uint32_t *)swapmem(&x,sizeof(x),1);
if (y!=NULL)
printf("LE=%08X BE=%08X\n",x,*y);
free(y);
/* Dynamic allocation is not used. The contents of z and k will change */
z=s=0x7891;
swapmem(&z,sizeof(z),0);
printf("LE=%04X BE=%04X\n",s,z);
k=t=0x1120324351657389;
swapmem(&k,sizeof(k),0);
printf("LE=%16"PRIX64" BE=%16"PRIX64"\n",t,k);
/* LE64 to BE64 (or viceversa) using shift */
k=swap64(t);
printf("LE=%16"PRIX64" BE=%16"PRIX64"\n",t,k);
return 0;
程序编译完成后,我好奇地看到了 gcc 生成的汇编代码。我发现函数swap64的生成如下所示。
00000000004007a0 <swap64>:
4007a0: 48 89 f8 mov %rdi,%rax
4007a3: 48 0f c8 bswap %rax
4007a6: c3 retq
此结果是在具有 Intel I3 CPU 的 PC 上使用 gcc 选项编译代码获得的:-Ofast,或 -O3,或 -O2,或 -Os。
您可以使用类似swap64()
的函数来解决您的问题。我命名为swap32()
:
uint32_t swap32(uint32_t k)
return ((k << 24) |
((k & 0x0000FF00) << 8) |
((k & 0x00FF0000) >> 8) |
(k >> 24)
);
您可以将其用作:
uint32_t j=swap32(*(uint32_t *)ptr);
【讨论】:
以上是关于将大端转换为小端的 C/C++ 代码的主要内容,如果未能解决你的问题,请参考以下文章