计算机字节序(Endianness)

高低顺序约定

  • 内存地址: 通常约定从左到右内存地址从小到大
  • 字节位: 通常约定左高位,右低位

基本概念

字节序描述多字节数据在内存中的存储顺序。

类型存储方式示例:0x12345678
大端序最高有效字节存低地址,即低位对高地址[12] [34] [56] [78]
小端序最低有效字节存低地址,即低位对低地址[78] [56] [34] [12]

常见架构

  • 小端:x86/x64
  • 大端:网络协议、PowerPC
  • 可配置:ARM

多字节数据 vs 字节序列

核心区别

类型示例是否有字节序
多字节数据int32, float32
字节序列UTF-8, string

判断标准

“交换两个字节的位置,还是同一个东西吗?”

  • → 有字节序(int32交换后还是同一个数,只是表示不同)
  • → 无字节序(UTF-8交换后解码失败)

字节存储

比如常量:

const x = 0x12345678  // 这是字节位

作为 int32(有字节序)

var x int32 = 0x12345678
  • 小端内存:[78 56 34 12]
  • 大端内存:[12 34 56 78]

作为字节序列(无字节序)

b := []byte{0x12, 0x34, 0x56, 0x78}
  • 所有系统:[12 34 56 78](按索引固定顺序)

寄存器与内存映射

寄存器特点

  • 寄存器无字节序概念,只有高位(bit31)和低位(bit0),假设32位系统。
  • 多字节读取指令(如MOV EAX)会按系统字节序映射
B3 高位 B2 B1 B0 低位

小端系统映射

规则:低地址 → 寄存器低位

内存(小端) 寄存器EAX 0x78 低地址 0x56 0x34 0x12 高地址 B3 0x12 高位 B2 0x34 B1 0x56 B0 0x78 低位 低地址→低位 结果: EAX = 0x12345678

大端系统映射

规则:低地址 → 寄存器高位

内存(大端) 寄存器R0 0x12 低地址 0x34 0x56 0x78 高地址 B3 0x12 高位 B2 0x34 B1 0x56 B0 0x78 低位 低地址→高位 结果: R0 = 0x12345678

整块复制逻辑

核心:硬件按字节序读写,不关心数据类型

不管是 int32 还是 string,整块复制时都走相同的字节序映射,即根据大小端来映射。

网络传输与转换

网络字节序标准

  • 网络字节序 = 大端序(TCP/IP规定)
  • 所有多字节整数必须按大端传输

转换责任

flowchart LR
    subgraph Sender["发送方(小端)"]
        S1["主机数据<br/>[90 1F 00 00]<br/>port=8080"]
    end

    subgraph Network["网络传输"]
        N1["[00 00 1F 90]"]
    end

    subgraph Receiver["接收方(小端)"]
        R1["主机数据<br/>[90 1F 00 00]<br/>port=8080"]
    end

    S1 -->|"htonl<br/>小端↔大端"| N1
    N1 -->|"ntohl<br/>大端↔小端"| R1
函数作用说明
htonl()主机 → 网络小端转大端,大端无操作
ntohl()网络 → 主机大端转小端,大端无操作

各层责任

flowchart TB
    subgraph AppLayer["应用层"]
        AppData["应用层数据<br/>(自定义协议字段)"]
        AppHandle["用户程序处理<br/>htonl/ntohl"]
    end

    subgraph TransportLayer["传输层"]
        TcpHeader["TCP/UDP头部<br/>(端口号、序列号等)"]
        Kernel1["内核自动处理"]
    end

    subgraph NetworkLayer["网络层"]
        IpHeader["IP头部<br/>(IP地址、包长度等)"]
        Kernel2["内核自动处理"]
    end

    subgraph LinkLayer["数据链路层"]
        Frame["以太网帧<br/>(MAC地址、CRC等)"]
        Hardware["驱动/硬件处理"]
    end

    AppData --> AppHandle
    AppHandle --> TcpHeader
    TcpHeader --> Kernel1
    Kernel1 --> IpHeader
    IpHeader --> Kernel2
    Kernel2 --> Frame
    Frame --> Hardware

关键API

// C
uint32_t htonl(uint32_t hostlong);
uint32_t ntohl(uint32_t netlong);
// Go
binary.BigEndian.PutUint32(buf, val)  // htonl
binary.BigEndian.Uint32(buf)          // ntohl

与序列化的关系

方式处理
手动二进制显式调用 htonl/ntohl
Protocol Buffers库自动处理(推荐)
JSON/XML无需处理(文本无字节序)

核心总结

  1. 字节序只针对多字节数据(int/float),字节序列(UTF-8/string)无字节序
  2. 寄存器读取按系统字节序映射
    • 小端:低地址→寄存器低位
    • 大端:低地址→寄存器高位
  3. 网络传输统一用大端:发送方htonl,接收方ntohl
  4. 整块复制无问题(读写用同一规则),跨系统传输需转换
  5. 使用序列化库(Protobuf)可自动处理