Little Endian & Big Endian
什么是字节序
字节序是指在多字节数据类型(如整数、浮点数等)的字节在内存中的存储顺序。
主要有两种字节序:大端字节序(Big-endian)和小端字节序(Little-endian)。
-
Little-Endian 低字节在内存低地址端,高字节在内存高地址端
-
Big-Endian 高字节在内存低地址端,低字节在内存高地址端
address | Little Endian | Big Endian |
---|---|---|
0x7000 | 0x34 | 0x12 |
0x7001 | 0x12 | 0x34 |
为什么会有大小端的分别
大小端问题主要涉及的是非单字节非字符串外的其余数据的表示和传递,如short型、int型等。大端和小端有其各自的优势。
我们知道计算机正常的内存增长方式是从低到高(当然栈不是),取数据方式是从基址根据偏移找到他们的位置,从他们的存储方式可以看出,大端存储因为第一个字节就是高位,从而很容易知道它是正数还是负数,对于一些数值判断会很迅速。
而小端存储 第一个字节是它的低位,符号位在最后一个字节,这样在做数值四则运算时从低位每次取出相应字节运算,最后直到高位,并且最终把符号位刷新,这样的运算方式会更高效。
如何判断主机大小端
//
// Created by zhipeng shu on 2024/4/26.
//
//Little-Endian:低字节在内存低地址端,高字节在内存高地址端,如
//0x7000 is 0x34
//0x7001 is 0x12
//Big-Endian:高字节在内存低地址端,低字节在内存高地址端,如
//0x7000 is 0x12
//0x7001 is 0x34
bool isLittleEndian_1(){
union Word{
short value;
char ch[sizeof(short)];
};
Word word = {4660};//0x1234=4660=pow(16,3)+2*pow(16,2)+3*pow(16,1)+4*pow(16,0)
return word.ch[0]==0x34;
}
bool isLittleEndian_2(){
short value = 0x1234;//4660
char ch = * (char *)&value;//short转char*,取首元素判断
return ch==0x34;
}
网络字节序
UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待。也就是说,发送端发送的第一个字节是需要是高位字节。
而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节。
所以网络字节序就是大端字节序。
有些系统的本机字节序是小端字节序, 有些则是大端字节序, 为了保证传送顺序的一致性, 所以网际协议使用大端字节序来传送数据。
如何进行大小端转换
使用宏定义转换大小端字节序
/* Macros for swapping constant values in the preprocessing stage. */
//16位数据转换大小端
#define __DARWIN_OSSwapConstInt16(x) \
((__uint16_t)((((__uint16_t)(x) & 0xff00U) >> 8) | \
(((__uint16_t)(x) & 0x00ffU) << 8)))
//32位数据转换大小端
#define __DARWIN_OSSwapConstInt32(x) \
((__uint32_t)((((__uint32_t)(x) & 0xff000000U) >> 24) | \
(((__uint32_t)(x) & 0x00ff0000U) >> 8) | \
(((__uint32_t)(x) & 0x0000ff00U) << 8) | \
(((__uint32_t)(x) & 0x000000ffU) << 24)))
//64位数据转换大小端
#define __DARWIN_OSSwapConstInt64(x) \
((__uint64_t)((((__uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \
(((__uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \
(((__uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \
(((__uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \
(((__uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \
(((__uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \
(((__uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \
(((__uint64_t)(x) & 0x00000000000000ffULL) << 56)))
使用函数转换大小端
/**
******************************************************************************
* https://blog.csdn.net/weixin_46672094/article/details/122524919
* @brief 大小端转换 函数
* @param *p 数据块指针
* @param size 字节数
* @return None
******************************************************************************
*/
void BigLittleEndianSwap(unsigned char *p, unsigned char size)
{
unsigned char i;
unsigned char tmp;
unsigned char num = size/2;
size--;
for(i=0; i<num; i++)
{
tmp = p[i];
p[i] = p[size-i];
p[size-i] = tmp;
}
}
本地字节序和网络字节序的转换
htons() //host to network short
htonl() //host to network long
ntohs() //network to host short
ntohl() //network to host long
在unix系统中,可以使用man htons
指令查看手册
使用示例
short value = 0x1234;
std::cout<<std::dec<<"value="<<value<<",hex="<<std::hex<<value<<std::endl;
//value=4660,bits=0001001000110100,hex=1234
value = htons(value); //将value从主机端转换为网络字节序
std::cout<<std::dec<<"value="<<value<<",hex="<<std::hex<<value<<std::endl;
//value=4660,bits=0001001000110100,hex=1234