分类:
2011-02-11 13:31:47
Linux,C语言和多核编程 2010-09-17 15:51:08 阅读56 评论0 字号:大中小 订阅
嵌入式编程的面试或被面试的过程中,经常会有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为最低位。数字在内存中的存放,从低位起,有两种存放方式,分别为:
1) 先存放最低位(little endian)
2) 先存放最高位(big endian)
下图表明不同的处理器是如何在内存中,按照big endian和little endian存放32位十六进制数的,如0x89ABCDEF:
各自的优势:由于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;
}
分析:union e_t 由四个字节构成。其分布如下表:
Uninon |
Byte0 |
Byte1 |
Byte2 |
Byte3 |
a | ||||
b |
|
|
| |
Big Endian |
0 |
0 |
0 |
1 |
Little Endian |
1 |
0 |
0 |
0 |
int a 将占用4个字节,char b占用一个字节(Byte 0)。如果将a赋值为1,那么在Big Endian模式下,它将处于Byte 3的位置,所以此时取到的联合体中b的值为0;在Little Endian模式下,1将处于Byte 0的位置,所以b的值将为1.
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;
}
This is the layout of memory:
num |
Byte0 |
Byte1 |
Byte2 |
Byte3 |
|
char * |
|
|
|
| |
int * |
| ||||
Big Endian |
1 |
0 |
0 |
0 |
0x1000000 |
Little Endian |
1 |
0 |
0 |
0 |
0x1 |
参考文献:
http://shitou118.blog.51cto.com/715507/349895
http://blog.myspace.cn/e/401643257.htm