[数据传输/网络传输/序列化/计算机组成原理] 字节序/大小端

缘起

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)将按不同的字节序方式存储在内存中
txt 复制代码
Address         00  01  02  03
------------------------------
Little-endian   78  56  34  12
Big-endian      12  34  56  78

为什么会有大小端之分?

  • 在计算机系统中,我们是以字节(Byte) 为单位的,每个地址单元 都对应着一个字节,一个字节为8bit。
  • 但是在C语言中,除了8bitchar之外,还有16bitshort型,32bitlong型(要看具体的编译器)。

另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器的宽度 大于1个字节,那么必然存在着:多个字节的存放安排的问题。

因此,就导致了大端存储模式小端存储模式

  • 例如,一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。

对于大端模式 ,就将0x11放在低地址中,即 0x0010 中,0x22放在高地址中,即0x0011中。

对于小端模式,刚好相反。

txt 复制代码
bytes: 0x11(高字节) / 0x22(低字节)

Address         00(低地址)  10(高地址)
------------------------------------
Little-endian   22         11
Big-endian      11         22
  • 我们常用的X86CPU架构是小端模式 ,而KEIL C51则为大端模式

很多的ARMDSP都为小端模式

有些ARM处理器 ,还可以由硬件 来选择是大端模式 还是小端模式

字节顺序是否影响文件格式?

  • 1个字节为基本单位的文件格式,独立于字节顺序,例如: ASCII文件。

  • 其他文件格式 ,使用一些固定的端顺序格式

例如: JPEG文件,以大端顺序格式存储。

  • java程序文件及二进制文件,全部为大端(与平台无关): Java二进制文件中的所有内容都以大端顺序存储。

这意味着如果您只使用Java,那么所有文件在所有平台(Mac、PC、UNIX等)上的处理方式都是相同的。

  • C语言程序及二进制文件,默认是小端模式 :用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模式读写:

c 复制代码
BOOL IsBigEndian()
{
    union NUM
    {
        int a;
        char b;
    }num;
    num.a = 0x1234;
    if( num.b == 0x12 )
    {
        return TRUE;
    }
    return FALSE;
}

Shell 版

  • 方法1
shell 复制代码
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
shell 复制代码
echo -n I | od -o | head -n1 | awk '{print $2}'| cut -c6
  • 输出:1为小端模式,0为大端模式;
  • 解析:awk命令为文本处理。
  • 方法3
shell 复制代码
lscpu | grep -i byte
  • 输出: Byte Order / Little Endian;
  • 解析: grep -i 为不区分大小写匹配;
  • 注意: 在低版本的Linux 或 Windows Git Bash 可能不支持lscpu命令。
  • 方法4
shell 复制代码
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 CPUAMD CPU时,输出结果都是小端顺序

java 复制代码
import java.nio.ByteOrder;

    /**
     * 获取机器的字节序
     * @return
     */
    public static ByteOrder getByteOrder(){
        ByteOrder byteOrder = ByteOrder.nativeOrder();
        return byteOrder;
    }

含大小端的判断与处理

大小端之间的转换?

  • 对于字数据(16位):
c 复制代码
#define BigtoLittle16(A)   ( ( ((uint16)(A) & 0xff00) >> 8)  |  (( (uint16)(A) & 0x00ff) << 8) )
  • 对于双字数据(32位)
c 复制代码
#define BigtoLittle32(A)   ((( (uint32)(A) & 0xff000000) >> 24) | \
                                       (( (uint32)(A) & 0x00ff0000) >> 8)   | \
                                       (( (uint32)(A) & 0x0000ff00) << 8)   | \
                                       (( (uint32)(A) & 0x000000ff) << 24))

Java 中的字节序 := Big-Endian

  • 在两台计算机之间传输数据时,字节序的差异可能是一个问题。
  • Java 二进制文件 中的所有内容都以 big-endian 顺序存储。

这有时称为网络顺序

这意味着,如果您只使用 Java,则:所有文件所有平台 上都以相同的方式完成:MacPCUNIX 等。

您可以自由地以电子方式交换二进制数据,而无需担心字节序

  • 当您必须与一些不是用 Java 编写的、使用小端顺序程序交换数据时,问题就出现了。

一些平台在内部使用 big-endian order (Mac, IBM 390);有些使用 little-endian order (Intel)。

最常见的是用 C 编写的程序。(C语言程序的默认字节序: 小端字节序)

从字节数组中按照指定的字节序提取bit序列

  • 参考文献
java 复制代码
    /**
     * 从16进制序列中按照指定的大小端(字节序),提取指定长度的、指定偏移位的二进制串
     * @note 本示例中的样例序列对应的结果,均为正确结果
     */
    @Test
    public void extractTargetBitsTest(){
        String bits = null;

        //CAN帧 Payload
        bits = BytesUtils.extractTargetBits("0050D4408102050A", ByteOrder.LITTLE_ENDIAN, 17, 9);//'001101010'
        log.info("bits:{}", bits);

        bits = BytesUtils.extractTargetBits("7f7f010110110021", ByteOrder.BIG_ENDIAN, 22, 7);// '0000001'
        log.info("bits:{}", bits);

        bits = BytesUtils.extractTargetBits("7f7f010110110021", ByteOrder.BIG_ENDIAN, 22, 7);// '0000001'
        log.info("bits:{}", bits);

        //SOME/IP 报文 Payload
        bits = BytesUtils.extractTargetBits("ff", ByteOrder.BIG_ENDIAN, 1*8-1, 8);// '11111111' (1个字节,从起始位 8-1 开始,从右往左数 8 个 bit)
        log.info("bits:{}", bits);
    }

Y 推荐文献

含大小端的判断与处理

案例2:(小端模式/Intel/1) 【必读】

案例3:(大端模式/Motorola/0) 【必读】

X 参考文献

相关推荐
dualven_in_csdn1 小时前
搞了两天的win7批处理脚本问题
java·linux·前端
晨曦backend2 小时前
Vim 匹配跳转与搜索命令完整学习笔记
linux·编辑器·vim
爬呀爬的水滴4 小时前
解决Ubuntu24.04版本,右键没有共享选项的问题
linux·服务器·ubuntu·samba·共享文件夹
IT coke4 小时前
centos7部署AWStats日志分析系统
linux·运维·centos
雾岛心情5 小时前
【黑客与安全】Linux的常用命令之系统架构信息获取系列命令
linux·运维·服务器
杯莫停丶5 小时前
Linux基础指令大全
linux·运维·chrome
卫生纸不够用6 小时前
(三)Linux性能优化-CPU-CPU 使用率
linux·运维·服务器
Clownseven8 小时前
Ubuntu 24.04 LTS 长期支持版发布:对服务器用户意味着什么?新特性、升级建议与性能影响初探
linux·服务器·ubuntu
Xam_d_LM8 小时前
【Linux】服务器反向代理自动续签免费 Let‘s Encrypt 证书报错解决方法
linux·运维·服务器·反向代理·lets encrypt·http-01·证书续签
有时有晌8 小时前
linux arm系统烧录
linux·运维·arm开发