[备注:本文主要是将最新box项目中,遇到的问题做以总结梳理,便于加深印象和后续查阅。本文相关资料主要来源于网络,包括转载的一些资料]
遇到问题:
1、何为大、小端存储。
2、若协议中提及“消息传输采用大端模式(Big-Endian)。在一个 Byte 中, bit7 为 MSB, bit0 为 LSB。 ”,那么该协议数据传输模式是?
3、如何区分大小端存储,那种存储类型比较常用?
4、X86体系CPU、Keil编译软件,分别是那种存储方式?
5、如何用代码测试某系统的存储模式?
1、大小端是不同的对于数据在内存地址中的存放方式,不同的处理器(平台)的数据存储方式是不同的。如果实现跨平台通信则大小端是不能忽视的问题。
大端模式:数据的高位存储在内存的低字节。ARM/PowerPC等处理器采用大端模式
小端模式:数据的地位存储在内存的低字节。Intel架构处理器采用小端模式。
如一个数据:0x12345678;对应内纯地址是0x00~0x03。
大端模式: 小端模式:
地址:0x00 0x01 0x02 0x03 地址:0x00 0x01 0x02 0x03
数据:0x12 0x34 0x56 0x78 数据:0x78 0x56 0x34 0x12
2、字节序对应着内存的存储(组织)模式,如网络字节序采用大端模式。
3、字节序只是对内置的数据类型而言(int、short、double、long....char)而对于char而言由于其本身只有一个字节则字节序和存储模式对其不影响。(因此字符串在跨平台传输时不用考虑字节序)
4、单字节内纯中的比特为不受字节序的影响,大于1个字节的数据类型,字节序才对其有影响。
引申:网络通讯中字节序的影响。
在网络通讯中对端口和IP地址进行了字节序的转换而未对发送的数据进行字节序的转换的一些思考:
对于同平台的网络通信而言发送的数据是不需要进行字节类型转换的,字节始终是以二进制的形式存放不管是在何种平台下都是1个字节8个BIT,而字节序所影响的只是对我们的读取数据的影响,如果在同一平台下通讯则字节序不会对我们的读取有影响。如在进行网络通信是网络只是数据的传输介质而不需要对数据进行解读,对数据进行解读的是我们数据的接收方。所以在同平台中的网络通信中不必要对传输的数据进字节序的转换。当然跨平台中进行通信时是需要对数据的字节序进行转换的(可以在接收方也可以在发送方)。
在基于TCP/UDP进行网络通信时对端口和IP地址进行的字节转换只是在网络通信中这两个数据是给网络通信的数据只有经过字节序转换之后网络中的协议等才能够识别我们的数据。(个人认为如果在网络通信中通信两端都不进行端口或IP)的字节序转换时通信也是能够完成的只是此时网络中识别的端口和IP和我们实际想设定的端口和IP是不一致的)。
5、如何测试某系统的数据存储模式?
MSB:Most Significant Bit. “最高有效位”
LSB:Least Significant Bit. “最低有效位”
大端模式:Big Endian.
小端模式:Little Endian.
关于MSB和LSB:MSB指一个数据的二进制表示的最高有效位,LSB指数据的最低有效位,举个栗子看:
1(MSB)001 1011(LSB)
因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于 8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在如何将多个字节安排的问题。
因此就导致了大端存储模式和小端存储模式。
例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。
对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。
我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以随时在程序中(在ARM Cortex 系列使用REV、REV16、REVSH指令[1] )进行大小端的切换。
下面详细介绍:
大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
下面画图表示:
详解二:
大端模式:一个多字节数据的高字节在前,低字节在后,以数据 0x1234ABCD 看例子:
低地址 ---------------------> 高地址
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| 12 | 34 | AB | CD |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
小端模式:一个多字节数据的低字节在前,高字节在后,仍以 0x1234ABCD 看:
低地址 ---------------------> 高地址
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| CD | AB | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
大端模式会感觉大端模式比较符合人类的习惯啊,存储的顺序和直观理解的顺序是一样的。X86架构的CPU都是小端模式的....
在项目编程中,我个人接触的都是小端模式,在传输数据的时候,组帧时都是把低字节数据放在数组的前边的。
协议中如果写 “LSB first” ,这就是指数据传输时采用小端模式。
可以通过1的高字节和低字节分别与0比较来判断。
通过程序,有两种方式可以测出当前系统的大小端存储模式
[cpp] view plain copy
1. #include
2. typedef union Un
3. {
4. char ch;
5. short a;
6. }Un;
7.
8. int main()
9. {
10.
11. Un un;
12. un.a = 1;
13.
14. if (1 == (un.ch&1))
15. printf("Little Endian\n");
16. else
17. printf("Big Endian\n");
18.
19. return 0;
20. }
联合体的数据元素共用内存,所以你给int元素赋个1,如果是小端模式,它的存储就是 “ 01 00 00 00 ” 这样的;如果是大端模式,就是 “ 00 00 00 01 ” 这样的,所以通过判断char元素的值(共用内存,所以这个值占用的是第一个字节的地址)就可以得到了。
1. BOOL IsBigEndian()
2. {
3. int a = 0x1234;
4. char b = *(char *)&a; //通过将int强制类型转换成char单字节,通过判断起始存储位置。即等于 取b等于a的低地址部分
5. if( b == 0x12)
6. {
7. return TRUE;
8. }
9. return FALSE;
10. }
例如,大端数据 00112233 44556677 8899AABB CCDDEEFF,内部格式就是 CCDDEEFF 8899AABB 44556677 00112233。这完全是为了加速硬件操作而设计的。
实际上,在芯片外部的数据传递过程中,大多数的标准还是规定为大端格式。即最高比特最先发送。看一下各种接口标准就知道了。