主頁 > 後端開發 > 關于切片引數傳遞的問題

關于切片引數傳遞的問題

2023-05-29 11:12:45 後端開發

前言:在 Golang 中函式之間傳遞變數時總是以值的方式傳遞的,無論是 int,string,bool,array 這樣的內置型別(或者說原始的型別),還是 slice,channel,map 這樣的參考型別,在函式間傳遞變數時,都是以值的方式傳遞,也就是說傳遞的都是值的副本,

在使用ioutil的ReadAll方法時查看了其內部實作如下,這讓我很痛苦,不明白為什么要這樣寫,下面我們就來一探究竟,

func ReadAll(r Reader) ([]byte, error) {
  b := make([]byte, 0, 512)
  for {
    if len(b) == cap(b) {
      // Add more capacity (let append pick how much).
      b = append(b, 0)[:len(b)]
    }
    n, err := r.Read(b[len(b):cap(b)])
    b = b[:len(b)+n]
    if err != nil {
      if err == EOF {
        err = nil
      }
      return b, err
    }
  }
}

討論這個問題之前先看一下標準庫中切片的內部結構

type SliceHeader struct {
    Data uintptr
    Len int
    Cap int
}

由切片的結構定義可知,切片的結構由三個資訊組成:

  1. 指標Data,指向底層陣列中切片指定的開始位置
  2. 長度Len,即切片的長度
  3. 容量Cap,也就是最大長度,即切片開始位置到陣列的最后位置的長度

最開始我想將一個檔案內容讀取到記憶體,我想到的操作是這樣的

func f1() {
  f, _ := os.Open("F:\\hello.txt")
  b := make([]byte, 0, 512)

  read, err := f.Read(b)
  if err != nil {
    return
  }
  fmt.Println(read)//列印0
  fmt.Println(b)//列印[]
}

為什么會出現這種情況呢?我們點開f.Read方法看到 Read reads up to len(b) bytes from the File. 讀取len(b) 長度byte的資料到b,那現在len(b)=0就一個位元組都不會讀取了,這時候你就會明白為什么上面標準庫中ReadAll引數為什么要用b[len(b):cap(b)] (對切片的任何操作都會復制一個切片b[len(b):cap(b)] 操作對b切片結構體進行了復制,產生了新的切片并且新切片的len=cap=512,這也就解釋了為什么資料能讀入b[len(b):cap(b)] 了),觀察下面代碼:

func f2() {
  f, _ := os.Open("F:\\hello.txt")
  b := make([]byte, 0, 512)
  //
  c := b[len(b):cap(b)]
  
  fmt.Println(len(c))//512
  fmt.Println(cap(c))//512
  
  read, err := f.Read(c)

  if err != nil {
    return
  }
  
  fmt.Println(read)//512
  fmt.Println(b)//[]
  fmt.Println(b[:cap(b)])//[...] 列印出了資料
  fmt.Println(c)//[...]列印出了和上面相同的資料
}

這就奇怪了不是說是參考傳遞嗎,為什么現在c作為引數傳進Read方法后值被改變了,這就需要看切片的內部結構了,切片本身并不承載資料,它只是一個有三個屬性的結構體,傳遞時,就會把這個結構體的三個屬性復制一份進行傳遞,而且復制后頭指標指向相同的地址,另外還有一個重要的概念:對切片的任何操作都會復制一個切片(并不是復制切片資料,二十切片的結構體,他們指向的記憶體區域還是一樣的),也就是復制上面說的三個屬性,讀取切片型別資料的另一個重要屬性就是len,len是多少那就會讀多少資料,雖然由b衍生出的其他結構體他們的頭指標的地址是一樣的,后面的資料也是一樣的,但是如果你的len是0那頭指標后面的資料一個byte也不屬于你,也就讀不出來,你有多少的len那么頭指標后就有多少資料屬于你,

這也就解釋了為什么b始終是空的了,雖然你的頭指標后面有資料被填充了,但是你的len始終是0那么資料都與你無關也是就是空了,c切片的頭指標與b相同但是len和cap不同都是512,所以就能讀取出頭指標后512bytes的資料了,

另外還要討論切片的擴容機制,當切片的len=cap時使用append方法會觸發內置的擴容機制cap會擴大,我就有些疑問為什么是b = append(b, 0)[:len(b)] ,因為使用append函式僅僅是為了觸發擴容,添加進去的0是無意義的,原來len=512現在就變成了513,再往后填充資料就會導致與原資料不一致的問題,因此要把添加的byte去除,

func f3() {
  f, _ := os.Open("F:\\hello.txt")
  b := make([]byte, 0, 512)
  for {
    if len(b) == cap(b) {
      // Add more capacity (let append pick how much).
      //b = append(b, 0)[:len(b)]
      b = append(b, 0)
    }
    n, err := f.Read(b[len(b):cap(b)])

    b = b[:len(b)+n]
    if err != nil {
      if err == io.EOF {
        err = nil
      }
      break
    }
  }
  fmt.Println(string(b))
}

可以看到使用b = append(b, 0) 會導致部分資料失真,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553685.html

標籤:Go

上一篇:驅動開發:內核決議記憶體四級頁表

下一篇:返回列表

標籤雲
其他(159902) Python(38178) JavaScript(25460) Java(18141) C(15232) 區塊鏈(8268) C#(7972) AI(7469) 爪哇(7425) MySQL(7214) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5344) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4578) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1977) 功能(1967) Web開發(1951) HtmlCss(1949) C++(1925) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1878) .NETCore(1862) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 關于切片引數傳遞的問題

    前言:在 Golang 中函式之間傳遞變數時總是以值的方式傳遞的,無論是 int,string,bool,array 這樣的內置型別(或者說原始的型別),還是 slice,channel,map 這樣的參考型別,在函式間傳遞變數時,都是以值的方式傳遞,也就是說傳遞的都是值的副本。 在使用ioutil ......

    uj5u.com 2023-05-29 11:12:45 more
  • 驅動開發:內核決議記憶體四級頁表

    當今作業系統普遍采用64位架構,CPU最大尋址能力雖然達到了64位,但其實僅僅只是用到了48位進行尋址,其記憶體管理采用了`9-9-9-9-12`的分頁模式,`9-9-9-9-12`分頁表示物理地址擁有四級頁表,微軟將這四級依次命名為PXE、PPE、PDE、PTE這四項。關于記憶體管理和分頁模式,不同的... ......

    uj5u.com 2023-05-29 10:57:11 more
  • 計算機網路面試八股文

    ## 網路分層結構 計算機網路體系大致分為三種,OSI七層模型、TCP/IP四層模型和五層模型。一般面試的時候考察比較多的是五層模型。最全面的Java面試網站:[最全面的Java面試網站](https://topjavaer.cn) ![](http://img.topjavaer.cn/img/t ......

    uj5u.com 2023-05-29 07:47:54 more
  • Java的Object類的方法

    Java的Object類是所有類的根類,它提供了一些通用的方法。下面是一些常用的Object類方法: 1. equals(Object obj):判斷當前物件是否與給定物件相等。默認情況下,equals方法比較的是物件的參考,但可以通過在具體類中重寫equals方法來改變其比較行為。 2. hash ......

    uj5u.com 2023-05-29 07:47:29 more
  • Python 使用ConfigParser操作ini組態檔

    ini 組態檔格式如下 要求:ini 檔案必須是GBK編碼,如果是UTF-8編碼,python讀取組態檔會報錯。 # 這里是注釋內容 # [FY12361] #婦幼保健介面服務埠 serverIP=192.168.1.11 serverPort=8400 [SM] #國產SM加密服務埠 se ......

    uj5u.com 2023-05-29 07:47:23 more
  • Rust Web 全堆疊開發之 Actix 嘗鮮并構建REST API

    # Rust Web 全堆疊開發之 Actix 嘗鮮并構建REST API ## 一、Actix 嘗鮮 ### 需要使用的crate - actix-web v4.3.1 - actix-rt v2.8.0 ```bash ~ via 🅒 base ? cd rust ~/rust via 🅒 b ......

    uj5u.com 2023-05-29 07:47:16 more
  • QT 繪制波形圖、頻譜圖、瀑布圖、星座圖、眼圖、語圖

    最近在學中頻信號處理的一些東西,順便用 QT 寫了一個小工具,可以顯示信號的時域波形圖、幅度譜、功率譜、二次方譜、四次方譜、八次方譜、瞬時包絡、瞬時頻率、瞬時相位、非線性瞬時相位、瞬時幅度直方圖、瞬時頻率直方圖、瞬時相位直方圖、眼圖、星座圖、語譜圖、瀑布圖。 ......

    uj5u.com 2023-05-29 07:46:15 more
  • 【VS Code+Qt6】拖放操作

    由于老周的示例代碼都是用 VS Code + CMake + Qt 寫的,為了不誤匯入,在標題中還是加上“VS Code”好一些。 上次咱們研究了剪貼板的基本用法,也了解了叫 QMimeData 的重要類。為啥要強調這個類?因為接下來扯到的拖放操作也是和它有關系。哦,對了,咱們先避開一下主題,關于剪 ......

    uj5u.com 2023-05-29 07:45:36 more
  • Python asyncio之協程學習總結

    ## 實踐環境 Python 3.6.2 ## 什么是協程 **協程**(Coroutine)一種電腦程式組件,該程式組件通過允許暫停和恢復任務,為非搶占式多任務生成子程式。**協程**也可以簡單理解為協作的程式,通過協同多任務處理實作并發的函式的變種(一種可以支持中斷的函式)。 下面,我們通過日常 ......

    uj5u.com 2023-05-29 07:45:19 more
  • Java 網路編程 —— 創建非阻塞的 HTTP 服務器

    ## HTTP 概述 HTTP 客戶程式必須先發出一個 HTTP 請求,然后才能接收到來自 HTTP 服器的回應,瀏覽器就是最常見的 HTTP 客戶程式。HTTP 客戶程式和 HTTP 服務器分別由不同的軟體開發商提供,它們都可以用任意的編程語言撰寫。HTTP 嚴格規定了 HTTP 請求和 HTTP ......

    uj5u.com 2023-05-29 07:45:07 more