一、为什么会出现大小端模式?
不同的cpu采用的大小端模式不一致。X86是小端模式。而KEIL
C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
二、大小端模式的不同带来的问题是什么?如何解决?
如果存在数据网络传输,如果大小端模式不一致,如果不经过转换,必然会导致数据不致,出现错误。
解决方法:统一将网络上传输的字节序采用同一种模式(大家都知道的),这样收发数据时,就会根据主机对应的模式是否和网络字节对应的模式一致,来判断是否需要进行转换。这样即使不同的设备使用不同的模式,网络传输不会出现问题。
三、如何判断cpu的大小端模式?
有多种方法,列举三种。
(1)、采用联合
// 返回真,就是小端
bool LittleEndian()
{
union
{
int i;
char c;
}udata;
udata.i = 1;
return (udata.c == 1);
}
(2)、通过指针判断
bool LittleEndian()
{
int d = 0x12345678;
char* p = *(char*)&d;
return (*p == 0x78);
}
(3)、在linux环境下,通过htonl等函数直接判断。
#include <arpa/inet.h>
bool LittleEndian()
{
return (1!=htonl(1));
}
四、网络字节序
由于不同的系统会有不同的模式,为了统一,规定在网络传输中使用大端模式,这就是网络字节序。现在看看下面这四个函数的作用
uint32_t htonl(uint32_t hostlong);//32位的主机字节序转换到网络字节序 uint16_t htons(uint16_t hostshort);//16位的主机字节序转换到网络字节序 uint32_t ntohl(uint32_t netlong);//32位的网络字节序转换到主机字节序 uint16_t ntohs(uint16_t netshort);//16位的网络字节序转换到主机字节序
拿htonl和ntohl来分析,htonl函数的内部实现原理是这样,先判断主机是什么模式存储,如果是大端模式,就跟网络字节序一致,直接返回参数即可,如果是小端模式,则把形参转换成大端模式存储在一个临时参数内,再把临时参数返回;而ntohl函数的实现原理也是一样的过程,但是要注意它的参数,参数是网络字节序,就是大端模式存储,而不管你传入实参的过程是如果存储的,因此当判断主机是大端模式的时候,会直接返回,因为该函数默认会认为形参是网络字节序,把它当大端模式来看,如果判断主机是小端模式,就会将实参做转换,转换的过程并不复杂,就是逆序存储各个字节的数据,所以结果就被转换。
说到这里,可以看出一个规律来,就是如果主机与网络字节序不一致(也就是小端模式),这四个函数的返回值与传递进去的实参值的字节排序肯定是逆序的,所以返回值绝对不等于实参值,例如htonl(1)的结果肯定不是1,而如果主机与网络字节序一致(也就是大端模式),则这四个函数根本就没有做转换操作,而是直接返回实参值,这样他们的返回结果就肯定与实参值相同,即htonl(1)的结果是1。
这样,我们就得到了一个非常简便的判断系统是什么模式的方法,就是直接利用这四个函数来判断,如
if (1 != htonl(1)) { //小端模式,作相应处理 } else { //大端模式,作相应处理 }
或者直接用一个判断if(1 != htonl(1)),只有主机字节序与网络字节序不一致时,才调用那些函数去转换,否则不需要处理,这样可以减少多余的函数调用。