一、大端模式和小端模式的起源 关于大端小端名词的由来, 有一个有趣的故事, 来自于 Jonathan Swift 的《格利佛游记》:Lilliput 和 Blefuscu 这两个强国在过去的 36 个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的 方法是打破鸡蛋较大的一端, 可以那时的皇帝的祖父由于小时侯吃鸡蛋, 按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃 鸡蛋的时候,必须先打破鸡蛋较小的一端,违令者重罚。然后老百姓对 此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一 个丢了王位,产生叛乱的原因就是另一个国家 Blefuscu 的国王大臣煽 动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有 11000 余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。 这个其实讽刺当 时英国和法国之间持续的冲突。 Danny Cohen 一位网络协议的开创者, 第一次使用这两个术语指代字节顺序,后来就被大家广泛接受。 二、什么是大端和小端 Big-Endian 和 Little-Endian 的定义如下: 1) Little-Endian 就是低位字节排放在内存的低地址端,高位字节排放在 内存的高地址端。 2) Big-Endian 就是高位字节排放在内存的低地址端,低位字节排放在 内存的高地址端。 举一个例子,比如数字 0x12 34 56 78 在内存中的表示形式为: 1)大端模式: 低地址 -----------------> 高地址 0x12 | 0x34 | 0x56 | 0x78 2)小端模式: 低地址 ------------------> 高地址 0x78 | 0x56 | 0x34 | 0x12 可见,大端模式和字符串的存储模式类似。 3)下面是两个具体例子: 16bit 宽的数 0x1234 在 Little-endian 模式(以及 Big-endian 模式)CPU 内存中的存放方式(假设从地址 0x4000 开始存放) 为: 内存地址 小端模式存放内容 大端模式存放内容 0x4000 0x4001 0x34 0x12 0x12 0x34 32bit 宽的数 0x12345678 在 Little-endian 模式以及 Big-endian 模式) CPU 内存中的存放方式 (假设从地址 0x4000 开始存放) 为: 内存地址 小端模式存放内容 大端模式存放内容 0x4000 0x4001 0x4002 0x4003 0x78 0x56 0x34 0x12 0x12 0x34 0x56 0x78 4)大端小端没有谁优谁劣,各自优势便是对方劣势: 小端模式 :强制转换数据不需要调整字节内容,1、2、4 字节的存储 方式一样。 大端模式 :符号位的判定固定为第一个字节,容易判断正负。 三、数组在大端小端情况下的存储: 以 unsigned int value = 0x12345678 为例, 分别看看在两种字节序 下其存储情况,我们可以用 unsigned char buf[4]来表示 value: Big-Endian: 低地址存放高位,如下: 高地址 --------------buf[3] (0x78) -- 低位 buf[2] (0x56) buf[1] (0x34) buf[0] (0x12) -- 高位 --------------低地址 Little-Endian: 低地址存放低位,如下: 高地址 --------------buf[3] (0x12) -- 高位 buf[2] (0x34) buf[1] (0x56) buf[0] (0x78) -- 低位 -------------低地址 四、为什么会有大小端模式之分呢? 这是因为在计算机系统中,我们是以字节为单位的,每个地址单 元都对应着一个字节,一个字节为 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 处理器还可以 由硬件来选择是大端模式还是小端模式。 五、如何判断机器的字节序 可以编写一个小的测试程序来判断机器的字节序: [cpp] view plaincopy 1. BOOL IsBigEndian() 2. { 3. 4. 5. 6. 7. 8. 9. } return FALSE; int a = 0x1234; char b = *(char *)&a; //通过将 int 强制类型转换成 char 单字节, 通过判断起始存 储位置。即等于 取 b 等于 a 的低地址部分 if( b == 0x12) { return TRUE; 10. } 联合体 union 的存放顺序是所有成员都从低地址开始存放,利用该特性可以轻松地获得了 CPU 对内存采用 Little-endian 还是 Big-endian 模式读写: [cpp] view plaincopy 1. BOOL IsBigEndian() 2. { 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. } return FALSE; background-color: rgb(255, 255, 255); "> union NUM { int a; char b; }num; num.a = 0x1234; if( num.b == 0x12 ) { return TRUE; 14. }