[数据传输/网络传输/序列化/计算机组成原理] 字节序/大小端
缘起CAN / DBC 字节序与大端
概述: 字节序(Byte Order) := 大小端(Endian) := { Little-Endian(小端字节序) , Big-Endian(大端字节序/网络序) }
大小端的由来
[*]关于大端小端名词的由来,有一个有趣的故事,来自于Jonathan Swift的《格利佛游记》:
Lilliput和Blefuscu这两个强国在过去的36个月中一直在苦战。
战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端,可以那时的皇帝的祖父由于小时侯吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端,违令者重罚。
然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家Blefuscu的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。
据估计,先后几次有11000余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。
这个故事其实在讽刺当时英国和法国之间持续的冲突。
[*]Danny Cohen,一位网络协议的开创者,第1次使用这两个术语指代字节顺序,后来就被大家广泛接受。
字节序的定义
[*]Little-Endian(小端字节序) / Big-Endian(大端字节序),这两个术语与 CPU 体系结构中单词中字节的方向有关。
[*]endian := byte order := 字节序
[*]计算机内存由正整数地址引用。
在计算机内存中,存储最低有效字节位于最高有效字节之前的数字是“自然”的。
有时,计算机设计人员更喜欢使用这种表示的倒序版本。
[*]“自然”顺序,其中不太重要的字节在内存地址中较高的有效字节之前,称为 little-endian。
[*]许多供应商(如 Motorola、IBM、CRAY 和 Sun)更喜欢相反的顺序,当然,这称为 big-endian。
[*]Big-Endian(大端字节序) := 数据的高字节在内存的低地址存放,数据的低字节在内存的高地址存放
:= Motorola 模式 := 网络字节序(Network Order, 即网络序,默认网络传输字节为大端)
[*]典型代表(CPU): Motorola / IBM 390 / PowerPC CPU /
[*]典型代表(程序): Sun / Java /
[*]典型代表(文件): Adobe PS / JPEG / MacPaint /
[*]其他代表: 绝大部分的【网络通讯协议】 / CRAY /
即: 地址由小向大增加,而数据从高位往低位放
这和我们的阅读习惯一致。
[*]Little-Endian(小端字节序) := 数据的高字节在内存的高地址存放,数据的低字节在内存的低地址存放
:= Intel 模式
[*]典型代表(CPU): Intel / X86 CPU / DEC CPU
[*]典型代表(程序): C 语言程序
[*]典型代表(文件): RTF / GIF / BMP
[*]其他代表:大部分的【操作系统】
这种存储模式将地址的高低和数据位权有效地结合起来: 高地址部分权值高,低地址部分权值低
[*]特殊代表
[*]DXF文件(AutoCAD) – Variable(可灵活配置大小端)
[*]ARM CPU既可以工作在大端模式,也可以工作在小端模式
双端处理器可以在小端和大端两种模式下运行。当前一代的ARM处理器是双端处理器。
[*]示例: 4个字节的16进制值(0x12345678)将按不同的字节序方式存储在内存中
Address 00010203
------------------------------
Little-endian 78563412
Big-endian 12345678为什么会有大小端之分?
[*]在计算机系统中,我们是以字节(Byte)为单位的,每个地址单元都对应着一个字节,一个字节为8bit。
[*]但是在C语言中,除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器)。
另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器的宽度大于1个字节,那么必然存在着:多个字节的存放安排的问题。
因此,就导致了大端存储模式和小端存储模式。
[*]例如,一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。
对于大端模式,就将0x11放在低地址中,即 0x0010 中,0x22放在高地址中,即0x0011中。
对于小端模式,刚好相反。
bytes: 0x11(高字节) / 0x22(低字节)
Address 00(低地址)10(高地址)
------------------------------------
Little-endian 22 11
Big-endian 11 22
[*]我们常用的X86CPU架构是小端模式,而KEIL C51则为大端模式。
很多的ARM,DSP都为小端模式。
有些ARM处理器,还可以由硬件来选择是大端模式还是小端模式。
字节顺序是否影响文件格式?
[*]以1个字节为基本单位的文件格式,独立于字节顺序,例如: ASCII文件。
[*]其他文件格式,使用一些固定的端顺序格式
例如: JPEG文件,以大端顺序格式存储。
[*]java程序文件及二进制文件,全部为大端(与平台无关): Java二进制文件中的所有内容都以大端顺序存储。
这意味着如果您只使用Java,那么所有文件在所有平台(Mac、PC、UNIX等)上的处理方式都是相同的。
[*]C语言程序及二进制文件,默认是小端模式:用C语言编写的程序通常使用小端顺序
如何判断机器的字节序?
C 语言版
[*]可以编写一段测试程序来判别机器的字节序
BOOL IsBigEndian()
{
int a = 0x1234;
char b =*(char *)&a;//通过将 int 强制类型转换成 char 单字节,通过判断起始存储位置。即 等于 取b等于a的低地址部分
if( b == 0x12) //低地址的内容为 高字节序的值时
{
return TRUE; //大端
}
return FALSE; //小端
}
[*]联合体/union的存放顺序是所有成员都从低地址开始存放
利用该特性可以轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写:
BOOL IsBigEndian()
{
union NUM
{
int a;
char b;
}num;
num.a = 0x1234;
if( num.b == 0x12 )
{
return TRUE;
}
return FALSE;
}Shell 版
[*]方法1
echo -n I | od -o | head -n1 | cut -f2 -d" " | cut -c6解释: od命令的作用为将指定内容以八进制、十进制、十六进制、浮点格式或ASCII编码字符方式显示
[*]0: 大端字节序
[*]1: 小端字节序
Intel(R) Xeon(R) Platinum 8378C CPU @ 2.80GHz
[*]方法2
echo -n I | od -o | head -n1 | awk '{print $2}'| cut -c6
[*]输出:1为小端模式,0为大端模式;
[*]解析:awk命令为文本处理。
[*]方法3
lscpu | grep -i byte
[*]输出: Byte Order / Little Endian;
[*]解析: grep -i 为不区分大小写匹配;
[*]注意: 在低版本的Linux 或 Windows Git Bash 可能不支持lscpu命令。
[*]方法4
dpkg-architecture | grep -i end
[*]输出:
DEB_BUILD_ARCH_ENDIAN=little
DEB_HOST_ARCH_ENDIAN=little
DEB_TARGET_ARCH_ENDIAN=little
[*]解析: dpkg-architecture 命令是列出dpkg打包的一些环境参数;
Java 版
[*]在Java中,我们可以使用ByteOrder.nativeOrder()方法来获取CPU使用的字节顺序。
在使用Intel CPU 或 AMD CPU时,输出结果都是小端顺序
import java.nio.ByteOrder;
/**
* 获取机器的字节序
* @return
*/
public static ByteOrder getByteOrder(){
ByteOrder byteOrder = ByteOrder.nativeOrder();
return byteOrder;
}
[*]基础工具: ByteUtils(字节操作) - 博客园/千千寰宇
含大小端的判断与处理
Python 版
[*]关键: sys.byteorder
[*]https://docs.python.org/zh-cn/3/library/sys.html#sys.byteorder
import sys;
sys.byteorderout
'little'大小端之间的转换?
[*]对于字数据(16位):
#define BigtoLittle16(A) ( ( ((uint16)(A) & 0xff00) >> 8)|(( (uint16)(A) & 0x00ff) > 24) | \ (( (uint32)(A) & 0x00ff0000) >> 8) | \ (( (uint32)(A) & 0x0000ff00)
页:
[1]