4 回答

TA貢獻1946條經驗 獲得超4個贊
讓我們首先解決術語問題。Go語言規范并不像您所使用的那樣使用引用一詞。然而 Go 確實有指針,而指針是引用的一種形式。此外,切片和映射有點特殊,因為存在一些底層數據(切片下的數組或映射的存儲),這些數據可能已經存在,也可能不存在,或者通過聲明或定義類型為 或 的變量來創建某些類型T或類型對T1和T2。1slice of T
map[T1]T2
在談論時,我們可以將引用一詞的用法理解為顯式指針,例如:
func f1(p *int) {
? ? // code ...
}
以及談論時隱含的指針:
func f2(m map[T1]T2) { ... }
func f3(s []T) { ... }
在 中f1,preally 是一個指針:因此它指的是某個實際的int或 is nil。在 中f2,m指的是某些底層地圖,或者是nil。在 中f3,s指的是某個底層數組,或者是nil。
但如果你寫:
l := Line{
? ? Points: []Point{
? ? ? ? Point{3, 4},
? ? },
}
那么你一定寫過:
type Line struct {
? ? // ... maybe some fields here ...
? ? Points []Point
? ? // ... maybe more fields here ...
}
這Line是一個結構類型。它不是切片類型;它不是地圖類型。它包含一種切片類型,但它本身不是一種切片類型。
您現在談論傳遞這些切片。如果您傳遞,則您將按值l傳遞整個內容。struct區分它和傳遞 的值非常重要l.Points。接收這些參數之一的函數必須使用正確的類型來聲明它。
因此,在大多數情況下,談論參考文獻只是轉移注意力——分散人們對實際情況的注意力。我們需要知道的是:您使用什么源代碼為哪些變量分配什么值?
完成所有這些后,讓我們討論一下實際的代碼示例:
l.Points = append(l.Points, Point{99, 100})
這正是它所說的:
傳遞
l.Points
to?append
,它是一個內置函數,因為它具有神奇的類型靈活性(與 Go 的其余部分相比,類型相當嚴格)。它采用任何類型的值(?T的切片,對于任何有效類型T)加上一個或多個類型的值,并生成相同類型的新值。[]T
T
[]T
將結果賦給
l.Points
.
它什么時候append
起作用,它可能:
接收
nil
(給定類型):在這種情況下,它創建底層數組,或者接收非零切片:在這種情況下,它會寫入底層數組或根據需要丟棄該數組,轉而使用新的更大容量的數組。2
因此,在所有情況下,底層數組實際上可能剛剛被創建或替換。因此,對同一底層數組的任何其他使用進行適當更新非常重要。將結果分配回更新(可能是唯一的)引用底層數組的切片變量。l.Points
然而,我們可以打破這些假設:
s2?:=?l.Points
現在l.Points
和s2
都引用(單個)底層數組。修改底層數組的操作至少可能會影響s2
?和?l.Points
。
你的第二個例子本身就可以:
*slice?=?append(*slice,?Point{99,?100})
但您尚未顯示其自身是如何?slice
聲明和/或分配給的。
你的第三個例子也很好:
slice?:=?&l.Points *slice?=?append(l.Points,?Point{99,?100})
這些行中的第一行聲明并初始化slice
為指向l.Points
。slice
因此該變量的類型為*[]Point
。它的值(slice
即 中的值,而不是 中的值)*slice
是 的地址l.Points
,其類型為[]Point
。
中的值*slice
就是 中的值l.Points
。所以你可以寫:
*slice?=?append(*slice,?Point{99,?100})
這里。由于*slice
是 的另一個名稱l.Points
,您還可以編寫:
l.Points?=?append(*slice,?Point{99,?100})
僅當由于某種原因無法使用時才需要使用,?3但如果更方便的話也可以使用。讀讀讀,更新更新。*slice
l.Points
*slice
*slice
l.Points
*slice
l.Points
1要了解我所說的可能或可能不會在此處創建的意思,請考慮:
var?s?[]int
對比:
var?s?=?[]int{42}
第一個離開,s == nil
而第二個創建一個底層數組,該數組能夠保存一個int
值42
,保存一個int
值 42,因此s != nil
。
2我不清楚是否有承諾永遠不會在容量大于其當前長度但不足以保存最終結果的現有切片數組上寫入。也就是說,可以append
先將10個對象追加到現有的底層數組中,然后發現需要更大的數組并擴展底層數組嗎?如果有其他切片值引用現有的底層數組,則可以觀察到差異。
3如果您有理由將l.Points
or傳遞&l.Points
給某些現有(預先編寫的)函數,則會出現一個經典示例:
如果您需要將
l.Points
切片值傳遞給某個現有函數,則該現有函數無法更改切片值,但可以更改底層數組。這可能是一個糟糕的計劃,所以如果確實這樣做,請確保這是可以的!如果它只讀取切片和底層數組,那就安全得多。如果您需要將
&l.Points
指向切片值的值傳遞給某個現有函數,則該現有函數可以更改切片和底層數組。
如果您正在編寫一個新函數,則由您決定以最合適的方式編寫它。如果您只想讀取切片和底層數組,則可以采用 類型的值[]Point
。如果您打算就地更新切片,則應該采用類型為“*[]Point
指向切片的指針”的值Point
。

TA貢獻1821條經驗 獲得超5個贊
Append 返回一個新切片,該切片可能會修改初始切片的原始支持數組。原始切片仍將指向原始后備數組,而不是新數組(新切片可能位于內存中的同一位置,也可能不位于內存中的同一位置)
例如(操場)
slice := []int{1,2,3}
fmt.Println(len(slice))
// Output: 3
newSlice := append(slice, 4)
fmt.Println(len(newSlice))
// Output: 4
fmt.Println(len(slice))
// Output: 3?
雖然切片可以被描述為“指向數組的胖指針”,但它不是指針,因此您無法取消引用它,這就是您收到錯誤的原因。
通過創建一個指向切片的指針,并append
按照上面的操作使用,您可以將指針指向的切片設置為 . 返回的“新”切片append
。

TA貢獻1900條經驗 獲得超5個贊
我知道這可能是褻瀆的,但是對我來說,將切片視為結構很有用。
type Slice struct {
len int
cap int
Array *[n]T // Pointer to array of type T
}
由于在像 C 這樣的語言中,該[]運算符也是一個解引用運算符,因此我們可以認為每次訪問切片時,我們實際上都在解引用底層數組并為其分配一些值。那是:
var s []int
s[0] = 1
可能被認為等同于(在偽代碼中):
var s Slice
*s.Array[0] = 1
這就是為什么我們可以說切片是“指針”。因此,它可以像這樣修改其底層數組:
myArray := [3]int{1,1,1}
mySlice := myArray[0:1]
mySlice = append(mySlice, 2, 3) // myArray == mySlice
修改mySlice也會修改myArray,因為切片存儲了指向數組的指針,并且在追加時,我們取消引用該指針。
然而,這種行為并不總是這樣。如果超出原始數組的容量,則會創建一個新數組,并且原始數組保持不變。
myArray := [3]int{1,1,1}
mySlice := myArray[0:1]
mySlice = append(mySlice, 2, 3, 4, 5) // myArray != mySlice
當我們嘗試將切片本身視為實際指針時,就會出現混亂。由于我們可以通過附加到底層數組來修改它,因此我們相信在這種情況下:
sliceCopy := mySlice
sliceCopy = append(sliceCopy, 6)
兩個切片slice和sliceCopy是相同的,但它們不是。我們必須顯式傳遞對切片內存地址的引用(使用&運算符)才能修改它。那是:
sliceAddress := &mySlice
*sliceAddress = append(mySlice, 6) // or append(*sliceAddress, 6)

TA貢獻1993條經驗 獲得超6個贊
您的第一次嘗試沒有成功,因為切片不是指針,它們可以被視為引用類型。如果底層數組有足夠的容量,Append 將修改它,否則返回一個新切片。
通過結合這兩種嘗試,您可以實現您想要的目標。
l := Line{
Points: []Point{
Point{3, 4},
},
}
slice := &l.Points
for i := 0; i < 100; i++ {
*slice = append(*slice, Point{99 + i, 100 + i})
}
fmt.Println(l.Points)
- 4 回答
- 0 關注
- 190 瀏覽
添加回答
舉報