golang切片指針,go 切片作為參數

本文目錄一覽:

Golang|切片原理

在Golang語言開發過程中,我們經常會用到數組和切片數據結構,數組是固定長度的,而切片是可以擴張的數組,那麼切片底層到底有什麼不同?接下來我們來詳細分析一下內部實現。

首先我們來看一下數據結構

這裡的array其實是指向切片管理的內存塊首地址,而len就是切片的實際使用大小,cap就是切片的容量。

我們可以通過下面的代碼輸出slice:

這麼分析下來,我們可以了解如下內容:

使用一個切片通常有兩種方法:

另一種是slice = make([]int, len, cap)這種方法,稱為分配內存。

創建一個slice,實質上是在分配內存。

這裡跟一下細節,math.MulUintptr是基於底層的指針計算乘法的,這樣計算不會導致超出int大小,這個方法在後面會經常用到。

同樣,對於int64的長度,也有對應的方法

而實際分配內存的操作調用mallocgc這個分配內存的函數,這個函數以後再分析。

我們了解切片和數組最大的不同就是切片能夠自動擴容,接下來看看切片是如何擴容的

這裡可以看到,growslice是返回了一個新的slice,也就是說如果發生了擴容,會發生拷貝。

所以我們在使用過程中,如果預先知道容量,可以預先分配好容量再使用,能提高運行效率。

copy這個函數在內部實現為slicecopy

還有關於字元串的拷貝

這裡顯示了可以把string拷貝成[]byte,不能把[]byte拷貝成string。

1、切片的數據結構是 array內存地址,len長度,cap容量

2、make的時候需要注意 容量 * 長度 分配的內存大小要小於264,並且要小於可分配的內存量,同時長度不能大於容量。

3、內存增長的過程:

4、當發生內存擴容時,會發生拷貝數據的現象,影響程序運行的效率,如果可以,要先分配好指定的容量

5、關於拷貝,可以把string拷貝成[]byte,不能把[]byte拷貝成string。

golang-101-hacks(12)——切片作為函數參數傳遞

註:本文是對 golang-101-hacks 中文翻譯。

在Go語言中,函數參數是值傳遞。使用slice作為函數參數時,函數獲取到的是slice的副本:一個指針,指向底層數組的起始地址,同時帶有slice的長度和容量。既然各位熟知數據存儲的內存的地址,現在可以對切片數據進行修改。讓我們看看下面的例子:

In Go, the function parameters are passed by value. With respect to use slice as a function argument, that means the function will get the copies of the slice: a pointer which points to the starting address of the underlying array, accompanied by the length and capacity of the slice. Oh boy! Since you know the address of the memory which is used to store the data, you can tweak the slice now. Let’s see the following example:

運行結果如下

由此可見,執行modifyValue函數,切片s的元素髮生了變化。儘管modifyValue函數只是操作slice的副本,但是任然改變了切片的數據元素,看另一個例子:

You can see, after running modifyValue function, the content of slice s is changed. Although the modifyValue function just gets a copy of the memory address of slice’s underlying array, it is enough!

See another example:

The result is like this:

而這一次,addValue函數並沒有修改main函數中的切片s的元素。這是因為它只是操作切片s的副本,而不是切片s本身。所以如果真的想讓函數改變切片的內容,可以傳遞切片的地址:

This time, the addValue function doesn’t take effect on the s slice in main function. That’s because it just manipulate the copy of the s, not the “real” s.

So if you really want the function to change the content of a slice, you can pass the address of the slice:

運行結果如下

golang函數返回slice和返回 slice的指針有什麼區別

按照你的定義,slice是切片,而p是指針。切片是一個結構體頭部+數組區域,其頭部結構定義如下:

struct Slice

{ // must not move anything

byte* array; // actual data

uintgo len; // number of elements

uintgo cap; // allocated number of elements

};

因此,slice的返回其實是頭部值返回,函數內外的地址是不同的,這也導致主程序中,ss與pp不同。因為ss是新分配的,pp則是與子程序testInterface中的slice相同。簡單修改你的代碼,通過輸出對比,會非常清晰:

package main

import (

“fmt”

)

func testInterface() (slice interface{}, p interface{}) {

slice = make([]int, 10)

p = slice

fmt.Println(“debug:testInterface”)

fmt.Println(slice)//兩個地址應該相同

fmt.Println(p) //兩個地址應該相同

return slice, p

}

func main() {

fmt.Println(“debug:main”)

ss, pp := testInterface()

fmt.Println(ss)

fmt.Println(pp) //應該與子程序的輸出一致

}

另外,第一個問題就不用多解釋,依然是值和指針不同了。

Golang 中數組(Array)和切片(Slice)的區別

Go 中數組的長度是不可改變的,而 Slice 解決的就是對不定長數組的需求。他們的區別主要有兩點。

數組:

切片:

注意 1

雖然數組在初始化時也可以不指定長度,但 Go 語言會根據數組中元素個數自動設置數組長度,並且不可改變。切片通過 append 方法增加元素:

如果將 append 用在數組上,你將會收到報錯:first argument to append must be slice。

注意 2

切片不只有長度(len)的概念,同時還有容量(cap)的概念。因此切片其實還有一個指定長度和容量的初始化方式:

這就初始化了一個長度為3,容量為5的切片。

此外,切片還可以從一個數組中初始化(可應用於如何將數組轉換成切片):

上述例子通過數組 a 初始化了一個切片 s。

當切片和數組作為參數在函數(func)中傳遞時,數組傳遞的是值,而切片傳遞的是指針。因此當傳入的切片在函數中被改變時,函數外的切片也會同時改變。相同的情況,函數外的數組則不會發生任何變化。

GoLang中的切片擴容機制

[5]int 是數組,而 []int 是切片。二者看起來相似,實則是根本上不同的數據結構。

切片的數據結構中,包含一個指向數組的指針 array ,當前長度 len ,以及最大容量 cap 。在使用 make([]int, len) 創建切片時,實際上還有第三個可選參數 cap ,也即 make([]int, len, cap) 。在不聲明 cap 的情況下,默認 cap=len 。當切片長度沒有超過容量時,對切片新增數據,不會改變 array 指針的值。

當對切片進行 append 操作,導致長度超出容量時,就會創建新的數組,這會導致和原有切片的分離。在下例中

由於 a 的長度超出了容量,所以切片 a 指向了一個增長後的新數組,而 b 仍然指向原來的老數組。所以之後對 a 進行的操作,對 b 不會產生影響。

試比較

本例中, a 的容量為6,因此在 append 後並未超出容量,所以 array 指針沒有改變。因此,對 a 進行的操作,對 b 同樣產生了影響。

下面看看用 a := []int{} 這種方式來創建切片會是什麼情況。

可以看到,空切片的容量為0,但後面向切片中添加元素時,並不是每次切片的容量都發生了變化。這是因為,如果增大容量,也即需要創建新數組,這時還需要將原數組中的所有元素複製到新數組中,開銷很大,所以GoLang設計了一套擴容機制,以減少需要創建新數組的次數。但這導致無法很直接地判斷 append 時是否創建了新數組。

如果一次添加多個元素,容量又會怎樣變化呢?試比較下面兩個例子:

那麼,是不是說,當向一個空切片中插入 2n-1 個元素時,容量就會被設置為 2n 呢?我們來試試其他的數據類型。

可以看到,根據切片對應數據類型的不同,容量增長的方式也有很大的區別。相關的源碼包括: src/runtime/msize.go , src/runtime/mksizeclasses.go 等。

我們再看看切片初始非空的情形。

可以看到,與剛剛向空切片添加5個int的情況一致,向有3個int的切片中添加2個int,容量增長為6。

需要注意的是, append 對切片擴容時,如果容量超過了一定範圍,處理策略又會有所不同。可以看看下面這個例子。

具體為什麼會是這樣的變化過程,還需要從 源碼 中尋找答案。下面是 src/runtime/slice.go 中的 growslice 函數中的核心部分。

GoLang中的切片擴容機制,與切片的數據類型、原本切片的容量、所需要的容量都有關係,比較複雜。對於常見數據類型,在元素數量較少時,大致可以認為擴容是按照翻倍進行的。但具體情況需要具體分析。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/304683.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2025-01-01 11:05
下一篇 2025-01-01 11:05

相關推薦

  • 三星內存條參數用法介紹

    本文將詳細解釋三星內存條上面的各種參數,讓你更好地了解內存條並選擇適合自己的一款。 一、容量大小 容量大小是內存條最基本的參數,一般以GB為單位表示,常見的有2GB、4GB、8GB…

    編程 2025-04-29
  • Python3定義函數參數類型

    Python是一門動態類型語言,不需要在定義變數時顯示的指定變數類型,但是Python3中提供了函數參數類型的聲明功能,在函數定義時明確定義參數類型。在函數的形參後面加上冒號(:)…

    編程 2025-04-29
  • 使用Golang調用Python

    在現代軟體開發中,多種編程語言的協作是相當普遍的。其中一種使用場景是Golang調用Python,這使得在使用Python庫的同時,可以利用Golang的高性能和強大並發能力。這篇…

    編程 2025-04-29
  • Python input參數變數用法介紹

    本文將從多個方面對Python input括弧里參數變數進行闡述與詳解,並提供相應的代碼示例。 一、基本介紹 Python input()函數用於獲取用戶輸入。當程序運行到inpu…

    編程 2025-04-29
  • Spring Boot中發GET請求參數的處理

    本文將詳細介紹如何在Spring Boot中處理GET請求參數,並給出完整的代碼示例。 一、Spring Boot的GET請求參數基礎 在Spring Boot中,處理GET請求參…

    編程 2025-04-29
  • 指針Python:為什麼Python中不需要使用指針?

    在Python中,指針的使用不像其他語言一樣那麼常見。這是因為Python有自己的內存管理方式,所以在大多數情況下,不需要顯式地使用指針。那麼,為什麼Python中不需要使用指針呢…

    編程 2025-04-29
  • Python Class括弧中的參數用法介紹

    本文將對Python中類的括弧中的參數進行詳細解析,以幫助初學者熟悉和掌握類的創建以及參數設置。 一、Class的基本定義 在Python中,通過使用關鍵字class來定義類。類包…

    編程 2025-04-29
  • Hibernate日誌列印sql參數

    本文將從多個方面介紹如何在Hibernate中列印SQL參數。Hibernate作為一種ORM框架,可以通過列印SQL參數方便開發者調試和優化Hibernate應用。 一、通過配置…

    編程 2025-04-29
  • Python函數名稱相同參數不同:多態

    Python是一門面向對象的編程語言,它強烈支持多態性 一、什麼是多態多態是面向對象三大特性中的一種,它指的是:相同的函數名稱可以有不同的實現方式。也就是說,不同的對象調用同名方法…

    編程 2025-04-29
  • 全能編程開發工程師必知——DTD、XML、XSD以及DTD參數實體

    本文將從大體介紹DTD、XML以及XSD三大知識點,同時深入探究DTD參數實體的作用及實際應用場景。 一、DTD介紹 DTD是文檔類型定義(Document Type Defini…

    編程 2025-04-29

發表回復

登錄後才能評論