数据大端小端转化

本文深入解析字节存储顺序的概念,通过实例演示如何判断当前系统的字节顺序,并提供字节转换方法,确保跨平台数据通信的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当前的存储器,多以byte为访问的最小单元,当一个逻辑上的地址必须分割为物理上的若干单元时就存在了先放谁后放谁的问题,于是端(endian)的问题应运而生了,对于不同的存储方法,就有大端(big-endian)和小端(little- endian)两个描述。

字节排序按分为大端和小端,概念如下

大端(big endian):低地址存放高有效字节

小端(little endian):低字节存放地有效字节

现在主流的CPU,intel系列的是采用的little endian的格式存放数据,而motorola系列的CPU采用的是big endian,ARM则同时支持 big和little,网络编程中,TCP/IP统一采用大端方式传送数据,所以有时我们也会把大端方式称之为网络字节序

特别需要注意的是,C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而 JAVA编写的程序则唯一采用big endian方式来存储数据。这里我就只讨论C/C++语言的情况。

1.大端和小端的方式及判断

举个例子说明,我的机子是32位windows的系统,处理器是AMD的。对于一个int型数0x12345678,为方便说明,这里采用16进制表示。这个数在不同字节顺序存储的CPU中储存顺序如下:

0x12345678   16进制,两个数就是一字节

高有效字节——>低有效字节: 12 34 56 78

          低地址位     高低址位

大端:  12  34        56   78

小端: 78  56        34   12

下面验证下本机CPU属于哪种字节存储顺序。代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
 
using  namespace  std;
 
typedef  unsigned int  UINT ;
typedef  unsigned char  UCHAR ;
 
int  main()
{
     UINT  i=0x12345678;
     cout<<hex<<i<<endl;
     UCHAR  *p = ( UCHAR *)&i;          //将i的地址传给数组指针p,实际上p指向的地址是i在内存中存储的第一个字节,大端就是0x12,小端就是0x78
     if ((*p==0x78)&(*(p+1)==0x56))       
         cout<< "小端" <<endl;
     else  if ((*p==0x12)&(*(p+1)==0x34))
         cout<< "大端" <<endl;
     else
         cout<< "这是神马字节顺序呢?" ;
     return  0;
}

调试显示时小端,我用的机子字节存储为小端方式。

2.大端和小端的字节转换

当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序(即大端方式)后再进行传输。此外用C/C++在小端方式的机器上编写的程序与java程序互通时也要进行大端和小端的转换。

这里所谓转换就是改变字节的排序,使交互时数据保持一致。举一个例子,还是16进制表示的数0x12345678,在小端机器上排序为0x78563412,当内存中这样的数传输时,在大端方式下就是0x78563412这个值,与原值不同,要想与原值相同,在传输前,在大端方式下就该是0x12345678,这时原数在内存中为0x12345678,即将原数据0x12345678在内存存储序列为0x12345678,也就是要转换成大端方式。

要传输值:12 34 56 78

不转换时,小端:78 56 34 12

转换为大端:12 34 56 78

根据上面的大端和小端字节排序,可以方便的用移位运算完成转换功能。从小端转到大端代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
 
using  namespace  std;
 
typedef  unsigned int  UINT ;
typedef  unsigned char  UCHAR ;
 
int  main()
{
     UINT  i=0x12345678;
     cout<<hex<<i<<endl;
     UCHAR  *p = ( UCHAR *)&i;
     UINT  num,num1,num2,num3,num4;
     num1=( UINT )(*p)<<24;
     num2=(( UINT )*(p+1))<<16;
     num3=(( UINT )*(p+2))<<8;
     num4=(( UINT )*(p+3));
     num=num1+num2+num3+num4;
 
     cout<< "num1:" <<hex<<num1<<endl;     //看num1的16进制表示,下同
     cout<< "num2:" <<hex<<num2<<endl;
     cout<< "num3:" <<hex<<num3<<endl;
     cout<< "num4:" <<hex<<num4<<endl;
     cout<< "num:" <<hex<<num<<endl;
 
     unsigned char  *q = (unsigned char *)&num;
     if ((*q==0x78)&(*(q+1)==0x56))         
         cout<< "小端" <<endl;
     else  if ((*q==0x12)&(*(q+1)==0x34))
         cout<< "大端" <<endl;
     else
         cout<< "这是神马字节顺序呢?" ;
     return  0;
}

至于说(UINT)(*p)为什么要移24位,其实是很好理解的,将0x00000012变成0x12000000,不就是向左移24位吗。

当然,向上面这样写时为了方便理解,可以更简单的写一个函数用于完成上面的转换功能,函数如下:

?
1
2
3
4
5
UINT  EndianConvertLToB( UINT  InputNum) {
     UCHAR  *p = ( UCHAR *)&InputNum;
     return ((( UINT )*p<<24)+(( UINT )*(p+1)<<16)+
                (( UINT )*(p+2)<<8)+( UINT )*(p+3));
}

同样的原理适用于大端转小端,但是大端转小端时移位有差别,函数如下:

?
1
2
3
4
5
UINT  EndianConvertBToL( UINT  InputNum) {
     UCHAR  *p = ( UCHAR *)&InputNum;
     return ((( UINT )*p)+(( UINT )*(p+1)<<8)+
                (( UINT )*(p+2)<<16)+( UINT )*(p+3)<<24);
}
### Python 中实现大端和小字节序之间的转换 在 Python 中,可以通过多种方式实现大端 (Big-Endian) 和小 (Little-Endian) 字节序之间的转换。以下是几种常见的方法: #### 方法一:使用字符串切片反转 这种方法适用于处理十六进制字符串的情况。通过 `[::-1]` 对字符串进行逆序排列来完成大小的转换。 ```python hex_str = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000" little_endian_hex = ''.join([hex_str[i:i+2][::-1] for i in range(0, len(hex_str), 2)][::-1]) print(little_endian_hex) ``` 此代码片段展示了如何将给定的大端十六进制字符串转换为小格式[^1]。 --- #### 方法二:使用 `struct` 模块 Python 的标准库提供了 `struct` 模块,用于解析和构建 C 结构体中的数据。它可以轻松地指定字节序并执行相应的转换。 对于整数类型的数据,可以利用 `struct.pack()` 将其打包成特定字节序的二进制形式,然后再用 `struct.unpack()` 解包回原始值。 ```python import struct value = 0x12345678 packed_big_endian = struct.pack(">I", value) # 打包为大端 packed_little_endian = struct.pack("<I", value) # 打包为小 unpacked_value_from_big = struct.unpack(">I", packed_big_endian)[0] unpacked_value_from_little = struct.unpack("<I", packed_little_endian)[0] print(f"Packed Big Endian: {packed_big_endian.hex()}") # 输出 '12345678' print(f"Packed Little Endian: {packed_little_endian.hex()}") # 输出 '78563412' print(unpacked_value_from_big, unpacked_value_from_little) # 都会还原到原值 305419896 ``` 这里展示了如何使用 `struct` 来切换不同字节序下的整型表示[^2]。 --- #### 方法三:使用 `int.to_bytes()` 和 `int.from_bytes()` 从 Python 3 开始引入的新功能允许更直观地操作数字与其对应的字节数组之间关系的方法——即 `to_bytes()` 及 `from_bytes()` 函数支持显式声明所需字节顺序参数 (`byteorder='big' | 'little'`)。 ```python number = 0x12345678 bytes_big_endian = number.to_bytes(length=4, byteorder="big") bytes_little_endian = number.to_bytes(length=4, byteorder="little") converted_back_number_be = int.from_bytes(bytes_big_endian, byteorder="big") converted_back_number_le = int.from_bytes(bytes_little_endian, byteorder="little") print(f"Bytes Big Endian: {bytes_big_endian.hex()}") # 输出 '12345678' print(f"Bytes Little Endian: {bytes_little_endian.hex()}") # 输出 '78563412' print(converted_back_number_be, converted_back_number_le) # 均返回初始数值 305419896 ``` 这段程序说明了新特性下更加简洁明了的方式来进行两模式间变换[^3]。 --- #### 方法四:手动位运算实现 如果希望深入理解底层原理或者需要优化性能时,也可以考虑直接运用按位逻辑运算符达成目标效果。 假设有一个双精度浮点数存储于 IEEE754 标准定义之下,则可通过如下手段将其由一种字节序列调整至另一种状态: ```python def float_to_ieee754(value): import struct return struct.unpack('<Q', struct.pack('<d', value))[0] def ieee754_swap_endianness(bits): swapped_bits = ((bits & 0xFF) << 56 | (bits & 0xFF00) << 40 | (bits & 0xFF0000) << 24 | (bits & 0xFF000000) << 8 | (bits >> 8) & 0xFF000000 | (bits >> 24) & 0xFF0000 | (bits >> 40) & 0xFF00 | (bits >> 56)) return swapped_bits original_float = 123.456 ieee_representation = float_to_ieee754(original_float) swapped_ieee_representation = ieee754_swap_endianness(ieee_representation) new_float = struct.unpack('>d', struct.pack('>Q', swapped_ieee_representation))[0] print(new_float) ``` 以上脚本解释了一个复杂场景下的自定义解决方案[^3]。 --- ### 总结 无论采取哪种途径,在实际应用过程中都需注意输入数据长度匹配问题以及可能存在的溢出风险等因素影响最终结果准确性。此外还需明确具体需求背景以便选取最合适的工具集解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值