转自:http://hi.baidu.com/qzfukwzlvgqsvzr/item/97aba6304e9d10f82784f4d9
嵌入式编程的面试或被面试的过程中,经常会有Little Endian与Big Endian相关的面试问题,分析区别或者写程序来判断一个CPU是Little Endian还是Big Endian。 这里做一下总结。
一、Big Endian和Little Endian的概念
计算机的所有内存以字节数组的方式进行编址。
当一个逻辑上长于一个字节的整形数据放置在内存中时(比如16位,32位,和64位的整数),需要考虑这些字节的存储顺序。Little Endian: 将字节的逻辑顺序与物理顺序一致,即将逻辑上较低的字节放置在物理上较低的字节上,比如Intel x86系列;
Big Endian字节的逻辑顺序与物理顺序相反,即将逻辑上较低的字节放置在物理上较高的字节上,比如Motorola的PowerPC以及Sun Sparc。还有一些平台同时支持两种方案,由开发者决定使用哪一种。
例如数字0x89ABCDEF,有四个字节组成,分别为0x89, 0xAB, 0xCD, 0xEF, 相应的算术值为0x89000000,0xAB0000, 0xCD00和0xEF,很明显,0x89的值最大,称之为数字0x89ABCDEF的最高位,而0xEF为最低位。数字在内存中的存放,从低位起,有两种存放方式,如下图:
内存位置 | 小端 | 大端 |
0x....0 | 0xEF | 0x89 |
0x....1 | 0xCD | 0xAB |
0x....2 | 0xAB | 0xCD |
0x....3 | 0x89 | 0xEF |
各自的优势
由于Little Endian提供了逻辑顺序与物理顺序的一致性,让编程者摆脱了不一致性所带来的困扰,C语言开发者可以无所顾忌的按照自己的意愿进行强制类型转换。
但Big Endian也有其优点,尤其对于汇编程序员:他们对于任意长度的整数,总是可以通过判断Byte 0的bit-7来查看一个整数的正负(补码的首位标识正负);对于Little Endian则不得不首先知道当前整数的长度,然后查看最高byte的bit-7来判断其正负。对于这种情况,big endian的开发者可以写出非常高效的代码。
TCP/IP协议指定其数据格式为Big endian,因此Big Endian的CPU处理起来更方面;对于Little-Endian的CPU, 如x86,应用中需要首先转换TCP/IP的数据为little endian格式。实际上,POSIX提供相应的操作函数,这些函数是htonl(), ntohl(), htons()和ntohs()。
需要特别指出的是,通常所提到的Little Endian和Big Endian仅仅指字节顺序。
二、如何通过程序来判断CPU为Big Endian还是Little Endian
1. 利用union来判断
联合中的数据成员是共享存储空间的,所分配的空间为数据成员中最大所需的内存数。union 的成员本身就被存放在相同的内存空间(共享内存,正是union 发挥作用的地方),因此,可以将一个字节型数据和一个整型数据同时作为一个union 的成员,通过如下的代码来测试endian的模式。
#include "stdio.h"
typedef union endian_test{
int a;
char b;
} e_t;
int main()
{
e_t c;
c.a = 1;
if (c.b)
printf("little endian\n");
else
printf("big endian\n");
return (int)c.b;
}
2. 通过强制类型转换来判断
将一个字节(CHAR/BYTE 类型)的数据和一个整型数据存放于同样的内存开始地址,通过读取整型数据,分析CHAR/BYTE 数据在整型数据的高位还是低位来判断CPU 工作于Littleendian 还是Big endian 模式。
#include "stdio.h"
int main(int argc, char* argv[])
{
unsigned int num = 0;
unsigned int *p;
p = #
*(unsigned char *)p = 0x1; // Set BYTE0 to 0X1
if (num == 1)
{
printf("little endian\n");
}
else // num == 0x1000000
{
printf("big endian\n");
}
return 0;
}