C/C++中的大端和小端
本文在chatGPT的协助下完成,如有侵权请联系删除
在涉及到通信时(以太网/串口等)往往需要面对数据的大小端转换。
下面以图片的形式对联合体、数组、结构体等数据类型在大小端模式下在内存中的组织形式进行说明。
小端模式
-
定义示例 1
typedef union InputReg_t { uint32_t DWord; uint8_t RawBytes[4]; struct { uint32_t data : 20; uint32_t addr : 4; uint32_t cmd : 4; uint32_t : 4; }; } InputReg_t;
结果如下:
-
定义示例 2
typedef union InputReg_t { uint32_t DWord; uint8_t RawBytes[4]; struct { uint32_t : 4; uint32_t cmd : 4; uint32_t addr : 4; uint32_t data : 20; }; } InputReg_t;
结果如下:
大端模式
-
定义示例 1
typedef union InputReg_t { uint32_t DWord; uint8_t RawBytes[4]; struct { uint32_t data : 20; uint32_t addr : 4; uint32_t cmd : 4; uint32_t : 4; }; } InputReg_t;
结果如下:
2.定义示例 2
typedef union InputReg_t {
uint32_t DWord;
uint8_t RawBytes[4];
struct
{
uint32_t : 4;
uint32_t cmd : 4;
uint32_t addr : 4;
uint32_t data : 20;
};
} InputReg_t;
结果如下:
大小端模式的判断
windows 平台下
- windwos 下默认为小端模式,因此没有相关的直接判断方法
linux 平台下
- 在头文件<endian.h>中,可以使用宏定义__BYTE_ORDER 来判断大小端,如果值为__LITTLE_ENDIAN,则为小端;如果值为__BIG_ENDIAN,则为大端
通用方法
-
__FLOAT_WORD_ORDER__
:用于确定浮点数在内存中的存储顺序。 -
__BYTE_ORDER__
:表示当前编译器所用的字节序。 -
__ORDER_BIG_ENDIAN__
:表示采用大端字节序,即高位字节存储在低地址,低位字节存储在高地址。 -
__ORDER_LITTLE_ENDIAN__
:表示采用小端字节序,即低位字节存储在低地址,高位字节存储在高地址。 -
union 结构体法:通过 union 结构体的特性,将同一内存空间的不同类型变量进行赋值,然后通过判断内存中存储的值是否与赋值时的值相同来判断大小端
-
异或法:通过将一个整型数的地址强制类型转换为字符型指针,然后逐个取出每个字节,再通过异或运算判断大小端
大小端模式的转换
windwos
- 包含头文件<winsock2.h>
htons()
:将 16 位主机字节序转换成网络字节序htonl()
:将 32 位主机字节序转换成网络字节序ntohs()
:将 16 位网络字节序转换成主机字节序ntohl()
:将 32 位网络字节序转换成主机字节序
linux
- 包含头文件<netinet/in.h>
htons()
:将 16 位主机字节序转换成网络字节序htonl()
:将 32 位主机字节序转换成网络字节序ntohs()
:将 16 位网络字节序转换成主机字节序ntohl()
:将 32 位网络字节序转换成主机字节序
- 包含头文件<byteswap.h>
bswap_16()
:将一个 16 位整数从大端字节序转换为小端字节序或从小端字节序转换为大端字节序。bswap_32()
:将一个 32 位整数从大端字节序转换为小端字节序或从小端字节序转换为大端字节序。bswap_64()
:将一个 64 位整数从大端字节序转换为小端字节序或从小端字节序转换为大端字节序。
- 包含头文件<endian.h>
太多了不进行详细说明
通用方法
-
包含头文件<stdlib.h>
_byteswap_ulong()
: 将一个 16 位无符号整数字节序反转_byteswap_ushort()
: 将一个 32 位无符号整数字节序反转_byteswap_uint64()
: 将一个 64 位无符号整数字节序反转
-
编译器内置宏定义
__builtin_bswap16()
: 将一个 16 位无符号整数字节序反转__builtin_bswap32()
: 将一个 32 位无符号整数字节序反转__builtin_bswap64()
: 将一个 64 位无符号整数字节序反转
-
可以使用 C11 标准库中的
fread
和fwrite
函数,将浮点数转换成字节流进行传输。 -
使用联合体来进行字节序的转换
union { float f; uint32_t i; } u; u.f = 3.14; u.i = htonl(u.i);
大小端模式的设置
windows
在 Windows 上使用 GCC 和链接器改变大小端模式需要使用特定的命令行选项。以下是一些可能有用的选项:
-
-mlittle-endian
:这个选项告诉 GCC 和链接器使用小端模式。 -
-mbig-endian
:这个选项告诉 GCC 和链接器使用大端模式。 -
-mno-ms-bitfields
:这个选项告诉 GCC 不使用 Microsoft 的位域布局。 -
-EL
:这个选项告诉链接器使用小端模式。 -
-EB
:这个选项告诉链接器使用大端模式。 -
-Wl,-EL
:这个选项告诉 GCC 将-EL
选项传递给链接器。 -
-Wl,-EB
:这个选项告诉 GCC 将-EB
选项传递给链接器。
linux
在 Linux 下,可以使用以下命令来改变大小端模式:
-
通过 LDFLAGS 修改链接器标志:
LDFLAGS=-EL export LDFLAGS
-
通过 CFLAGS 修改编译器标志:
CFLAGS=-EL export CFLAGS
其中,-EL 表示使用小端模式,-EB 表示使用大端模式。
需要注意的是,修改大小端模式可能会影响程序的二进制兼容性,因此在修改后需要进行充分的测试和验证。