大端模式和小端模式的起源
关于大端小端名词的由来 , 有一个有趣的故事 , 来自于 Jonathan Swift的 [格利佛游记] , Lilliput 和 Blefuscu 这两个强国在过去的 36 个月中一直在苦战 , 战争的原因 : 大家都知道 , 吃鸡蛋的时候 , 原始的方法是打破鸡蛋较大的一端 , 可那时的皇帝的祖父由于小时侯吃鸡蛋 , 按这种方法把手指弄破了 , 因此他的父亲 , 就下令 , 命令所有的子民吃鸡蛋的时候 , 必须先打破鸡蛋较小的一端 , 违令者重罚 ; 然后老百姓对此法令极为反感 , 期间发生了多次叛乱 , 其中一个皇帝因此送命 , 另一个丢了王位 , 产生叛乱的原因就是另一个国家 Blefuscu 的国王大臣煽动起来的 , 叛乱平息后 , 就逃到这个帝国避难 ; 据估计 , 先后几次有 11000 余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋 ; 这个其实讽刺当时英国和法国之间持续的冲突 ; Danny Cohen 一位网络协议的开创者 , 第一次使用这两个术语指代字节顺序 , 后来就被大家广泛接受 ;
大端和小端原理
对于整型 , 长整型等数据类型 , BigEndian 认为第一个字节是最高位字节 (按照从低地址到高地址的顺序存放数据的高位字节到低位字节) ;
而 LittleEndian 则相反 , 它认为第一个字节是最低位字节 (按照从低地址到高地址的顺序存放据的低位字节到高位字节) ;
例如 , 假设从内存地址 0x0000 开始有以下数据 :
0x0000 0x0001 0x0002 0x0003
0x12 0x34 0xab 0xcd
如果我们去读取一个地址为 0x0000 的四个字节变量 , 若字节序为 BigEndian , 则读出结果为 0x1234abcd , 若字节序为 LittleEndian , 则读出结果为 0xcdab3412 ;
如果我们将 0x1234abcd 写入到以 0x0000 开始的内存中 , 则 LittleEndian 和 BigEndian 模式的存放结果如下 :
Addr 0x0000 0x0001 0x0002 0x0003
BigEndian 0x12 0x34 0xab 0xcd
LittleEndian 0xcd 0xab 0x34 0x12
一般来说 , x86 系列 CPU 都是 LittleEndian 的字节序 , PowerPC 通常是 BigEndian , 网络字节顺序也是 BigEndian 还有的CPU 能通过跳线来设置 CPU 工作于 LittleEndian 还是 BigEndian 模式 ;
对于0x12345678的存储:
小端模式:(从低字节到高字节)
低位地址 0x78 0x56 0x34 0x12 高位地址
大端模式:(从高字节到低字节)
低位地址 0x12 0x34 0x56 0x78 高位地址
大端小端检测方法
int i = 1;
char *p = (char *)&i;
if(*p == 1)
{
printf("Little Endian");
}
else
{
printf("Big Endian");
}
实现同样的功能 , 来看看 Linux 操作系统中相关的源代码是怎么做的 :
static union { char c[4]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } };
#define ENDIANNESS ((char)endian_test.mylong)
Linux 的内核作者们仅仅用一个 union 变量和一个简单的宏定义就实现了一大段代码同样的功能 (如果 ENDIANNESS=’l’ 表示系统为 LittleEndian , 为 ’b’ 表示 BigEndian)
作者 Github : tojohnonly , 博客 : EnskDeCode