• golang大小端字节序


    为什么要有字节序?

    字节序,即字节的排列顺序。在计算机领域中,计算机内存中的字(word)由多个字节(bytes)组成,这些字节的排列顺序叫做字节序。

    计算机中电路优先处理低位字节,效率比较高,因为计算机都是从低位开始的,所以计算机内部处理都是小端字节序。但是我们平常读写数值的方法,习惯用大端字节序,所以除了计算机的内部,其他场景大都是大端字节序,比如:网络传输和文件储存时都是用的大端字节序。

    小端序(Little Endian):低位字节在低地址,高位字节在高地址。

    大端序(Big Endian):高位字节在低地址,低位字节在高地址。

    字节序取决于什么?

    其实大小端主要由CPU决定,与编程语言、编译器、操作系统这些没有直接关系。

    大小端是用于存储的顺序,与存储器(硬件)关系比较大,编译器和操作系统仅仅是配合CPU编译好相应的代码,而不是决定大小端的因素。

    一般来说,x86系列CPU都是Little-endian字节序,PowerPC通常是Big-endian字节序。

    查看golang整型是大端还是小端?

    在 Intel i5 CPU 上执行:

    1. package main
    2. import (
    3. "fmt"
    4. "unsafe"
    5. )
    6. func main() {
    7. isLit := IsLittleEndian()
    8. fmt.Printf("%t\n", isLit)
    9. }
    10. func IsLittleEndian() bool {
    11. var value int32 = 1
    12. pointer := unsafe.Pointer(&value)
    13. pb := (*byte)(pointer)
    14. if *pb == 1 {
    15. return true
    16. }
    17. return false
    18. }

    输出:

    true

    以go1.18为例,从go官方目前发布的各系统编译好的包来看,常见的架构都是小端,包括:386、amd64、arm(默认小端)、ppc64le都是小端架构。

    整数移位运算结果和字节序有关系吗?

    不算大端还是小端,移位运算的结果是一样的,都是按照直观的字面视觉效果移动也就是按照大端字节序理解即可。

    移位运算可以看作是一系列的乘2或除2操作。

    例如golang的官方包 encoding/binary 里有段代码:

    1. func (littleEndian) PutUint32(b []byte, v uint32) {
    2. _ = b[3] // early bounds check to guarantee safety of writes below
    3. b[0] = byte(v)
    4. b[1] = byte(v >> 8)
    5. b[2] = byte(v >> 16)
    6. b[3] = byte(v >> 24)
    7. }

    这段代码用了两个和架构无关的运算来实现大小端转换:

    1、通过移位运算进行大小端转换,实现架构的兼容。

    2、通过整数类型缩小来取最低位byte值,实现架构的兼容。

    如果是小端机器这样的操作其实可以用取巧的指针运算,无需进行多次的移位运算:

    1. package main
    2. import (
    3. "encoding/binary"
    4. "fmt"
    5. "unsafe"
    6. )
    7. func main() {
    8. var i uint32 = 0x01020304
    9. //官方包
    10. var ibyte = make([]byte, 4)
    11. binary.LittleEndian.PutUint32(ibyte, i)
    12. fmt.Printf("%v\n", ibyte)
    13. //指针访问
    14. var ibyte2 = make([]byte, 4)
    15. pointer := unsafe.Pointer(&i)
    16. pb := (*byte)(pointer)
    17. ibyte2[0] = *pb
    18. pb = (*byte)(unsafe.Pointer((uintptr)(pointer) + 1))
    19. ibyte2[1] = *pb
    20. pb = (*byte)(unsafe.Pointer((uintptr)(pointer) + 2))
    21. ibyte2[2] = *pb
    22. pb = (*byte)(unsafe.Pointer((uintptr)(pointer) + 3))
    23. ibyte2[3] = *pb
    24. fmt.Printf("%v\n", ibyte2)
    25. }

    显示:
    [4 3 2 1]
    [4 3 2 1]
    结果和官方包是一致的。

    大类型向小类型的强制转换结果和字节序有关吗?

    不算大端还是小端,大类型向小类型的强制转换的结果是一样的,都是按照直观的字面视觉效果取最低有效位(最右字节)的值,也就是按照小端字节序理解即可。

    在不同架构上运行以下代码,如在ppc和x86机器上运行。

    1. #include
    2. #include
    3. void main(){
    4. //H的16进制是0x48,10进制是72
    5. //I的16进制是0x49,10进制是73
    6. uint32_t a = 0x48020349;
    7. printf("%c\n", *(char*)&a);
    8. uint8_t b = (uint8_t)a;
    9. printf("%d\n", b);
    10. }

    在x86上执行输出:

    I
    73

    在ppc上执行输出:

    H
    73

    可以看到:

    按字符处理时,在x86上是小端序,在ppc上是大端序,

    在向小类型转换时,都是输出73,也就是都是按的小端序,截取的最低有效位。

    --end--

  • 相关阅读:
    ICDE‘22推荐系统论文梳理之Industry篇
    Python 全栈系列195 Neo4j 4.4 Docker安装
    机器学习算法 —— 1. K近邻算法
    Java中的异常
    C++中queue的用法(超详细,入门必看)
    arcgis 网络分析 生成可达范围/等时线
    ZCMU--1488: 过家家。。。(C语言)
    L298N双路驱动直流电机方案(支持PWM+正反转)
    hot100-课程表
    Prompt工程师指南[应用篇]:Prompt应用、ChatGPT|Midjouney Prompt Engineering
  • 原文地址:https://blog.csdn.net/flynetcn/article/details/133903555