今天在坐车的时候,对大小端字节序做了一些思考,有了更深的体会,记录下心得。
首先描述字节序的两种定义及意义,然后从网络编程、编译器、CPU三个角度来探讨字节序相关知识,最后给出测字节序的两段C代码。
当然,以下内容很多是个人猜想,即把自己摆在设计者的角度思考怎么实现的,并没有特别地查证,在此记录,以后实践中发现错误再修正吧。
第一节
(一)两种定义:
1.MSB在低地址即为大端。——存储的角度
2.MSB先到达即为大端。——传输的角度
(二)意义:
很多人认为大小端的形成纯粹是体系生态沿用或历史原因。笔者认为,大小端的划分,在某些特殊算法上,是有其意义的。比如在芯片加密算法中做大数运算时,大端方式,可以利用先到达的数据先进行运算,从而提高运算速度。
存在即合理,劳动人民的智慧总是无法想象的无穷。
第二节
(一)网络编程角度
大家都知道在网络编程中要尤其注意大小端问题。因为通常大部分主机本身是little endian,而且网络采用的是big endian。所以发包前要用htonl/htons转换,然后在收包时再用ntohl/ntohs变回到小端字节序。
其实之前,一直有个疑问,既然网络数据是以二进制封装在TCP/UDP包->IP数据包中的,那为什么还会有字节序问题,所谓的网络采用big endian到底是啥意思?
一、网络传输中的字节序:
TCP/IP网络协议,完成传输的算法那中必然要用到一些数据,比如IP地址,端口号等信息,这些东西必须统一规定字节序,才能让所有的路由器、网卡、主机、网络协议栈、操作系统等硬件软件实现有统一的规矩可循。这里规定为大端字节序。所以C程序中构建socket时,通常要用inet_addr(IP_ADDRESS)(这个函数将ip地址点分进制字符串转变为二进制形式,并按照大端字节序排列),htons(PORT_NUM)来完成大小端的转换
二、网络编程中应用程序的字节序:
如果通信两端能确保字节序一致,除了网络协议中用到的少量协议信息,真正要传输的数据无需再进行字节序转换。
在服务器端编程中,因为通常服务器无法确定访问它的机器是不是与它字节序相等。所以应该要想办法告诉客户端,它的大小端编码方式。这可能有多种方式,比如高层直接交互,比如在传输的信息中用某些标志位表明大小端等等。
(二)编译器角度
编译器将程序编译为可执行文件时,这里以ELF文件为例,其.data段存放初始化完成的全局静态变量与局部静态变量;.rodata存放只读数据(比如const型数据或者字符串常量)。这些数据在内存中的存放本身有大小端之分,编译器编译时指定的大小端(比如gcc的-EL),我想应该就是设定编译过程中对这些数据的存储采用EL还是EB。
(三)CPU角度
大小端与数据存储有关,那么CPU的存储,首先想到的就是寄存器、数据总线与内存。在寄存器通过数据总线与内存交换数据时,可以按照不同的字节序来完成操作。
在大端模式下,MSB在寄存器的第0位,LSB在第31位;数据总线的第一条总线为MSB,最后一条为LSB。
小端模式相反。
这样,应该有的结果如下所预料(以后发现错误再纠正吧,懒得验证了):
比如假设内存中地址为0x1000到0x1003的四个字节,存储的数据依次为0x12,0x34,0x56,0x78。LOAD,STORE指令将这些数据存到寄存器时,可能以不一样的映射方式来完成。
以MIPS指令集的一条LW指令为例,如果按照大端编码从地址为0x1000到0x0x1003之间取连续的四个字节,存放到32位的寄存器R15中,那么R15中的数据为0x12345678。如果按照小端编码,则R15中的数据为0x78563412。
第三节 测字节序的C代码
(一)union方法:
union{
short a;
char b[sizeof(short)];
} u;
u.a=0x1234;
if (u.b[0]==0x12&&u.b[1]==0x34)
printf("Endian Big!\n");
else if(u.b[0]==0x34&&u.b[1]==0x12)
printf("Endian Litlle\n");
else
print("error");
(二)强制类型转换方法;
unsigned short a=0x1234;
char * b = (char*) &a;
if (*b==0x12)
printf("Endian Big!\n"):
else
printf("Endian Litltle\n");
else
printf("error");
}