首頁  >  文章  >  後端開發  >  go語言預設大端還是小端

go語言預設大端還是小端

青灯夜游
青灯夜游原創
2023-01-05 10:28:504022瀏覽

go語言預設是大端。一般來說網路傳輸的位元組序,可能是大端序或小端序,取決於軟體開始時通訊雙方的協定規定。 TCP/IP協定RFC1700規定使用「大端」字節序為網路字節序,開發的時候需要遵守此規則;而預設golang是使用大端序的。

go語言預設大端還是小端

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

一、概述​​

字節序:位元組在電腦中存放時的序列與輸入/輸出時的序列;也指的是存放多位元組資料的位元組(byte)的順序,典型的情況是整數在記憶體中的存放方式和網路傳輸的傳輸順序。

先看下基本概念:

  • 1、大端模式(Big endian):將高序位元組儲存在起始位址(依照從低位址到高位址的順序存放資料的高位元組到低位元組)

  • 2、小端模式(Little endian):將低序位元組儲存在起始位址(按照從低位址到高位址的順序存放據的低位元組到高位元組)

#在電腦領域中,大小端序是跟硬體的體系結構有關的。

舉個栗子:如一個 var a = 0x11223344,對於這個變數的最高位元組為0x11,最低位元組為0x44。假設在記憶體中分配位址如下(位址都是連續的)

...0x00010x0002#0x00030x0004...

當分別處於大小端模式下的內容存放如下

(1)大端模式儲存(儲存位址為16位元)

##位址資料

0x0004(高位址) 0x44

0x0003 0x33

0x0002 0x22

0x0001(低位址) 0x11

(2)小端模式儲存(儲存位址為16位元)

位址資料

0x0004(高位址) 0x11

0x0003 0x22

0x0002 0x33

0x0001(低位址) 0x44

二、大端序與小端序

前面也簡單闡述了大小端序的定義並且結合簡單實例來說明,接下來會給予詳細實例來說明:

1、大端序(Big-Endian):或稱為大尾序

#一個型別: int32 的數0X0A0B0C0D的記憶體存放情況

go語言預設大端還是小端

#資料是以8bits為單位

go語言預設大端還是小端

go語言預設大端還是小端


##範例中,最高有效位元是將0x0A儲存在最低的記憶體位址處,接著是0x0B存在後面的位址,類似十六進位位元組從左往右的順序。

資料以16bits為單位

go語言預設大端還是小端最高的16bit單元0x0A0B儲存在低位

#2、小端序(little-endian):或稱小尾序go語言預設大端還是小端

資料以8bits為單位

go語言預設大端還是小端

範例中最低有效位元則是0x0D儲存的記憶體位址處,後面依序存放在後面的位址處。

資料以16bits為單位

最低的16bit單元0x0C0D儲存在低位元。

3、總結

採用大端序的CPU和採用小端序的CPU不僅在位元組上是相反的,在位元位上也是相反的。

例如0x01在記憶體中的儲存

大端序列:記憶體低位元組00000001 記憶體高位元組

小端序列:記憶體低位元位元10000000 記憶體高位元位元例如0x00000001

大端序:記憶體低位元組00000000 00000000 00000000 00000001 記憶體高位元組

小端序:低位元記憶體低位元記憶體低位記憶體高位元位元

應用程式

其實在前面羅列出那麼東西,最終是為了接下來講述的在golang中涉及到網路傳輸、檔案儲存時的選擇。一般來說網路傳輸的位元組序,可能是大端序或小端序,取決於軟體開始時通訊雙方的協定規定。 TCP/IP協定RFC1700規定使用「大端」字節序為網路字節序,開發的時候需要遵守這項規則。預設golang是使用大端序。詳情請見golang中套件encoding/binary已提供了大、小端序的使用

import (   
   "encoding/binary"
   "fmt"
)
func BigEndian() {    // 大端序
   // 二进制形式:0000 0000 0000 0000 0001 0002 0003 0004  
   var testInt int32 = 0x01020304  // 十六进制表示
   fmt.Printf("%d use big endian: \n", testInt)   
   
   var testBytes []byte = make([]byte, 4)  
   binary.BigEndian.PutUint32(testBytes, uint32(testInt))   //大端序模式
   fmt.Println("int32 to bytes:", testBytes)  

   convInt := binary.BigEndian.Uint32(testBytes)  //大端序模式的字节转为int32
   fmt.Printf("bytes to int32: %d\n\n", convInt)
}

func LittleEndian() {  // 小端序
   //二进制形式: 0000 0000 0000 0000 0001 0002 0003 0004
   var testInt int32 = 0x01020304  // 16进制
   fmt.Printf("%d use little endian: \n", testInt)   

   var testBytes []byte = make([]byte, 4)
   binary.LittleEndian.PutUint32(testBytes, uint32(testInt)) //小端序模式
   fmt.Println("int32 to bytes:", testBytes)

   convInt := binary.LittleEndian.Uint32(testBytes) //小端序模式的字节转换
   fmt.Printf("bytes to int32: %d\n\n", convInt)
}

func main() {
   BigEndian()
   LittleEndian()
}

輸出結果:

16909060 use big endian:
int32 to bytes: [1 2 3 4] ### [0001 0002 0003 0004]
bytes to int32: 16909060

16909060 use little endian:
int32 to bytes: [4 3 2 1] ### [0004 0003 0002 0001]
bytes to int32: 16909060
RPCX

### #在RPCX框架中關於RPC呼叫過程涉及的傳遞訊息進行編碼的,採用的就是大端序模式###
func (m Message) Encode() []byte {  // 编码消息
       // 编码metadata将key-value转为key=value&key=value形式
    meta := encodeMetadata(m.Metadata)  

    spL := len(m.ServicePath)   // 服务长度
    smL := len(m.ServiceMethod)  // 服务函数

    var err error
    payload := m.Payload   // 消息体
    if m.CompressType() != None {  // 压缩
        compressor := Compressors[m.CompressType()]
        if compressor == nil { // 默认使用None压缩类型
            m.SetCompressType(None)
        } else {
            payload, err = compressor.Zip(m.Payload)  // GZIP压缩
            if err != nil {   // 压缩失败 不对传输消息进行压缩
                m.SetCompressType(None)
                payload = m.Payload            }
        }
    }

    // RPCX数据包 = header + ID + total size +
    // 服务名及内容: servicePath(size(servicePath) 、len(servicePath)) +
    // 服务函数及内容:serviceMethod(size(serviceMethod) 、 len(serviceMethod)) +
    // 元数据及内容:   metadata(size(metadata) 、len(metadata)) +  
    // 消息体及内容:payload(size(payload) 、 len(payload)) 

        // 消息长度 = size(servicePath) + len(servicePath) + size(serviceMethod) 
       //        + len(serviceMethod) + size(metadata) + len(metadata) 
       //        + size(payload) + len(payload)
    totalL := (4 + spL) + (4 + smL) + (4 + len(meta)) + (4 + len(payload))  


    // header + dataLen + spLen + sp + smLen + sm 
       //              + metaL + meta + payloadLen + payload
    metaStart := 12 + 4 + (4 + spL) + (4 + smL) // meata开始位置

    payLoadStart := metaStart + (4 + len(meta)) // payLoad开始位置
    l := 12 + 4 + totalL

    data := make([]byte, l)
    copy(data, m.Header[:])  // 拷贝header内容

        // 将数据包以大端序模式进行编码 
    //totalLen
    binary.BigEndian.PutUint32(data[12:16], uint32(totalL))  // 

    binary.BigEndian.PutUint32(data[16:20], uint32(spL))
    copy(data[20:20+spL], util.StringToSliceByte(m.ServicePath))

    binary.BigEndian.PutUint32(data[20+spL:24+spL], uint32(smL))
    copy(data[24+spL:metaStart], util.StringToSliceByte(m.ServiceMethod))

    binary.BigEndian.PutUint32(data[metaStart:metaStart+4], uint32(len(meta)))
    copy(data[metaStart+4:], meta)

    binary.BigEndian.PutUint32(data[payLoadStart:payLoadStart+4],
                                       uint32(len(payload)))
    copy(data[payLoadStart+4:], payload)

    return data}
###【相關推薦:###Go影片教學###、###程式教學###】###

以上是go語言預設大端還是小端的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn