经调试,Windows 下为 Little_Endian,OD 中堆栈数据区的 (dword)0xAABB0102,0x02 存储在低地址,0x01 存储在高地址。

内容来自:http://blog.csdn.net/ce123_zhouwei/article/details/6971544

一、大端模式和小端模式的起源

关于大端小端名词的由来,有一个有趣的故事,来自于 Jonathan Swift 的《格利佛游记》:Lilliput 和 Blefuscu 这两个强国在过去的 36 个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端,可以那时的皇帝的祖父由于小时侯吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端,违令者重罚。然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家 Blefuscu 的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有 11000 余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。这个其实讽刺当时英国和法国之间持续的冲突。Danny Cohen 一位网络协议的开创者,第一次使用这两个术语指代字节顺序,后来就被大家广泛接受。

二、什么是大端和小端

1) Little-Endian :低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2) Big-Endian :高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

举个例子,比如数字0x12 34 56 78在内存中的表示形式为:
大端模式:

低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78

小端模式:

低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12

可见,大端模式和字符串的存储模式类似。

3) 下面是两个具体例子:
16bit 宽的数 0x1234 在内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x34 0x12
0x4001 0x12 0x34

32bit 宽的数 0x12345678 在内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78

4) 大端小端没有谁优谁劣,各自优势便是对方劣势:

小端模式 :强制转换数据不需要调整字节内容,1、2、4 字节的存储方式一样。
大端模式 :符号位的判定固定为第一个字节,容易判断正负。

判断机器的字节序

 // 获知 CPU 对内存采用 Little-endian 还是 Big-endian 读写模式
#include <stdio.h> int isBigEndian()
{
int a = 0x1234;
char b = *(char*)&a; // b 等于 a 的低地址部分
if(b == 0x12)
{
return ;
}
return ;
} // 联合体的存放顺序是所有成员都从低地址开始存放
int isLittleEndian()
{
union NUM
{
int a;
char b;
}num;
num.a = 0x1234;
if( num.b == 0x12 )
{
return ;
}
return ;
} int main()
{
printf("isBigEndian() return %d\n", isBigEndian());
printf("isLittleEndian() return %d\n", isLittleEndian());
return ;
}

四、常见的字节序

一般操作系统都是小端,而通讯协议是大端的。

4.1 常见CPU的字节序

Big Endian : PowerPC、IBM、Sun
Little Endian : x86、DEC
ARM既可以工作在大端模式,也可以工作在小端模式。

4.2 常见文件的字节序

Adobe PS : Big Endian
BMP : Little Endian
DXF(AutoCAD) : Variable
GIF : Little Endian
JPEG : Big Endian
MacPaint : Big Endian
RTF : Little Endian

另外,Java 和所有的网络通讯协议都是使用 Big-Endian 的编码。

五、大小端转换

对于字数据(16位):

#define BigtoLittle16(A) ((((uint16)(A) & 0xff00) >> 8) | \
(((uint16)(A) & 0x00ff) << 8 ) )

对于双字数据(32位):

#define BigtoLittle32(A) ((( (uint32)(A) & 0xff000000) >> 24) | \
(( (uint32)(A) & 0x00ff0000) >> ) | \
(( (uint32)(A) & 0x0000ff00) << ) | \
(( (uint32)(A) & 0x000000ff) << ))

六、从软件的角度理解端模式

从软件的角度上,不同端模式的处理器进行数据传递时必须要考虑端模式的不同。如进行网络数据传递时,必须要考虑端模式的转换。在 Socket 接口编程中,以下几个函数用于大小端字节序的转换。

#define ntohs(n) //16 位数据类型网络字节顺序到主机字节顺序的转换
#define htons(n) //16 位数据类型主机字节顺序到网络字节顺序的转换
#define ntohl(n) //32 位数据类型网络字节顺序到主机字节顺序的转换
#define htonl(n) //32 位数据类型主机字节顺序到网络字节顺序的转换

其中互联网使用的网络字节顺序采用大端模式进行编址,而主机字节顺序根据处理器的不同而不同,如PowerPC处理器使用大端模式,而Pentuim处理器使用小端模式。

大端模式处理器的字节序到网络字节序不需要转换,此时ntohs(n)=n,ntohl = n;

小端模式处理器的字节序到网络字节必须要进行转换,此时ntohs(n) = __swab16(n),ntohl = __swab32(n)。__swab16与__swab32函数定义如下所示。

#define ___swab16(x)
{
__u16 __x = (x);
((__u16)(
(((__u16)(__x) & (__u16)0x00ffU) << ) |
(((__u16)(__x) & (__u16)0xff00U) >> ) ));
} #define ___swab32(x)
{
__u32 __x = (x);
((__u32)(
(((__u32)(__x) & (__u32)0x000000ffUL) << ) |
(((__u32)(__x) & (__u32)0x0000ff00UL) << ) |
(((__u32)(__x) & (__u32)0x00ff0000UL) >> ) |
(((__u32)(__x) & (__u32)0xff000000UL) >> ) ));
}

PowerPC 处理器提供了 lwbrx,lhbrx,stwbrx,sthbrx 四条指令用于处理字节序的转换以优化 __swab16 和 __swap32 这类函数。

此外PowerPC处理器中的 rlwimi 指令也可以用来实现 __swab16 和 __swap32 这类函数。

在对普通文件进行处理也需要考虑端模式问题。在大端模式的处理器下对文件的 32,16 位读写操作所得到的结果与小端模式的处理器不同。单纯从软件的角度理解上远远不能真正理解大小端模式的区别。事实上,真正的理解大小端模式的区别,必须要从系统的角度,从指令集,寄存器和数据总线上深入理解,大小端模式的区别。

七、从系统的角度理解端模式

先补充两个关键词,MSB和LSB:
MSB : Most Significant Bit ------- 最高有效位
LSB  : Least Significant Bit ------- 最低有效位

处理器在硬件上由于端模式问题在设计中有所不同。从系统的角度上看,端模式问题对软件和硬件的设计带来了不同的影响,当一个处理器系统中大小端模式同时存在时,必须要对这些不同端模式的访问进行特殊的处理。

PowerPC 处理器主导网络市场,可以说绝大多数的通信设备都使用 PowerPC 处理器进行协议处理和其他控制信息的处理,这也可能也是在网络上的绝大多数协议都采用大端编址方式的原因。因此在有关网络协议的软件设计中,使用小端方式的处理器需要在软件中处理端模式的转变。而 Pentium 主导个人机市场,因此多数用于个人机的外设都采用小端模式,包括一些在网络设备中使用的 PCI 总线、Flash 等设备,这也要求在硬件设计中注意端模式的转换。

这里提到的小端外设是指这种外设中的寄存器以小端方式进行存储,如 PCI 设备的配置空间,NOR FLASH 中的寄存器等等。对于有些设备,如 DDR 颗粒,没有以小端方式存储的寄存器,因此从逻辑上讲并不需要对端模式进行转换。在设计中,只需要将双方数据总线进行一一对应的互连,而不需要进行数据总线的转换。

如果从实际应用的角度说,采用小端模式的处理器需要在软件中处理端模式的转换,因为采用小端模式的处理器在与小端外设互连时,不需要任何转换。而采用大端模式的处理器需要在硬件设计时处理端模式的转换。大端模式处理器需要在寄存器,指令集,数据总线及数据总线与小端外设的连接等等多个方面进行处理,以解决与小端外设连接时的端模式转换问题。在寄存器和数据总线的位序定义上,基于大小端模式的处理器有所不同。

一个采用大端模式的 32 位处理器,如基于 E500 内核的 MPC8541,将其寄存器的最高位 msb(most significant bit)定义为 0,最低位 lsb(lease significant bit)定义为31;而小端模式的 32 位处理器,将其寄存器的最高位定义为 31,低位地址定义为 0。与此向对应,采用大端模式的 32 位处理器数据总线的最高位为 0,最高位为 31;采用小端模式的 32 位处理器的数据总线的最高位为 31,最低位为 0。

大小端模式处理器外部总线的位序也遵循着同样的规律,根据所采用的数据总线是 32 位,16 位和 8 位,大小端处理器外部总线的位序有所不同。大端模式下 32 位数据总线的 msb 是第 0 位,MSB 是数据总线的第 0~7 的字段;而 lsb 是第 31 位,LSB 是第 24~31 字段。小端模式下 32 位总线的 msb 是第 31 位,MSB 是数据总线的第 31~24 位,lsb 是第 0 位,LSB 是 7~0 字段。大端模式下 16 位数据总线的 msb 是第 0 位,MSB 是数据总线的第 0~7 的字段;而 lsb 是第 15 位,LSB 是第 8~15 字段。小端模式下 16 位总线的 msb 是第 15 位,MSB 是数据总线的第 15~7 位,lsb 是第 0 位,LSB 是 7~0 字段。大端模式下 8 位数据总线的 msb 是第 0 位,MSB 是数据总线的第 0~7 的字段;而 lsb 是第 7 位,LSB 是第 0~7 字段。小端模式下 8 位总线的 msb 是第 7 位,MSB 是数据总线的第 7~0 位,lsb 是第 0 位,LSB 是 7~0 字段。

由上分析,我们可以得知对于 8 位,16 位和 32 位宽度的数据总线,采用大端模式时数据总线的 msb 和 MSB 的位置都不会发生变化,而采用小端模式时数据总线的 lsb 和 LSB 位置也不会发生变化。
为此,大端模式的处理器对 8 位,16 位和 32 位的内存访问(包括外设的访问)一般都包含第 0~7 字段,即 MSB。小端模式的处理器对 8 位,16 位和 32 位的内存访问都包含第 7~0 位,小端方式的第 7~0 字段,即 LSB。由于大小端处理器的数据总线其 8 位,16 位和 32 位宽度的数据总线的定义不同,因此需要分别进行讨论在系统级别上如何处理端模式转换。在一个大端处理器系统中,需要处理大端处理器对小端外设的访问。

八、实际中的例子

虽然很多时候,字节序的工作已由编译器完成了,但是在一些小的细节上,仍然需要去仔细揣摩考虑,尤其是在以太网通讯、MODBUS 通讯、软件移植性方面。

OD: Big_Endian vs Little_Endian的更多相关文章

  1. Linux-Big-Endian和Little-Endian转换

    转自:http://blog.csdn.net/aklixiaoyao/article/details/7548860 在各种计算机体系结构中,对于字节.字等的存储机制有所不同,因而引发了计算机通信领 ...

  2. 移位操作<<和>>,是逻辑数字上的移动(和大端小端无关)

    问题描述 这几天帮同事调试DSP TMS320F28335,这鬼东西蛋疼死了.char是16bit的,16位就是他的最小内存单元.但是PC机串口发过来的有8bit的数据,然后转换就出问题. 一开始不知 ...

  3. ByteArray

    ByteArray:属性endian:String == Endian.BIG_ENDIAN/Endian.LITTLE_ENDIAN.length:uint ByteArray的字节数positio ...

  4. 大端小端(Big- Endian和Little-Endian)[转]

    原文出处: 字节序(Endian),大端(Big-Endian),小端(Little-Endian)  http://www.cppblog.com/tx7do/archive/2009/01/06/ ...

  5. Analysis about different methods for reading and writing file in Java language

    referee:Java Programming Tutorial Advanced Input & Output (I/O) JDK 1.4+ introduced the so-calle ...

  6. ES6 二进制数组

    二进制数组(ArrayBuffer对象.TypedArray视图和DataView视图)是JavaScript操作二进制数据的一个接口.这些对象早就存在,属于独立的规格(2011年2月发布),ES6将 ...

  7. 24.ArrayBuffer

    ArrayBuffer ArrayBuffer ArrayBuffer对象.TypedArray视图和DataView视图是 JavaScript 操作二进制数据的一个接口.这些对象早就存在,属于独立 ...

  8. grads,fortran,ncl二进制文件

    #转自论坛日志# grad用fwrite生成的二进制文件大小和ncl用fbinwrite生成的不一样,相差有8个字节,参考了以下网页,问题出在顺序存取和直接存取的差异. 以下是我的理解,欢迎指正:   ...

  9. ES6的新特性(23)——ArrayBuffer

    ArrayBuffer ArrayBuffer对象.TypedArray视图和DataView视图是 JavaScript 操作二进制数据的一个接口.这些对象早就存在,属于独立的规格(2011 年 2 ...

随机推荐

  1. Swift - 35 - 使用闭包简化语法

    //: Playground - noun: a place where people can play import UIKit // 初始化一个整数数组 var arr = [1, 3, 5, 7 ...

  2. 使用C#代码追加和提交文件到SVN服务器

    windows系统下使用svn的命令需要安装一个插件,下载地址:http://sourceforge.net/projects/win32svn/?source=typ_redirect 安装后程序会 ...

  3. osg for android (一) 简单几何物体的加载与显示

    1. 首先需要一个OSG for android的环境. (1).NDK 现在Eclipse 对NDK已经相当友好了,已经不需要另外cygwin的参与,具体可以参考 Android NDK开发篇(一) ...

  4. phpcms v9版本二次开发四步曲

    今晚看了一下PHPCMS V9版本,做一个实例抛砖引玉,其实很简单,以下是二次开发的一个实例以旅游模块为例1.   在phpcms\modules目录下建立一个文件夹tour2.  在phpcms\m ...

  5. How To Learn English Very Fast

    How do you learn English very fast? Every week, I get emails about this topic.   Typically, someone ...

  6. IOS编程教程(八):在你的应用程序添加启动画面

    IOS编程教程(八):在你的应用程序添加启动画面   虽然你可能认为你需要编写闪屏的代码,苹果已经可以非常轻松地把它做在Xcode中.不需要任何编码.你只需要做的是设置一些配置. 什么是闪屏 对于那些 ...

  7. NUMBER BASE CONVERSION(进制转换)

    Description Write a program to convert numbers in one base to numbers in a second base. There are 62 ...

  8. build.prop各种优化代码

    1.首先要准备RE管理器以及root权限.2.在根目录里的system文件夹找到build.prop文件.3.打开build.prop文件后找到代码ro.sf.lcd_density=240(这里的2 ...

  9. word 生成目录

    生成目录: (1)Ctrl+End,到达文档的最后一页: (2)"插入"菜单--引用--索引和目录(此时出现索引和目录对话框): (3)单击"目录"选项卡 a. ...

  10. 【HDOJ】1403 Longest Common Substring

    后缀数组2倍增可解. #include <cstdio> #include <cstring> #include <cstdlib> #define MAXM 28 ...