iT邦幫忙

0

go slice問題

  • 分享至 

  • xImage

各位前輩好:

我看到一個例子,程式碼如下:

func SliceRise(s []int) {	
	s = append(s, 0)	
	for i := range s {
		s[i]++
	}
	fmt.Println(s)
}



func main() {
	//hello.goSliceExtend()
	slice1 := []int{1,2}
    slice2:= slice1
    slice2=append(slice2,3)
    slice2[0]=0
    slice2[1]=1
    slice2[2]=2    
    SliceRise(slice1)
    SliceRise(slice2)
    fmt.Println(slice1)
    fmt.Println(slice2)
   
}

執行結果是:
[2 3 1]
[1 2 3 1]
[1 2]
[1 2 3]

想請問為什麼 slice1=[1,2],不是[2,3,1]
slice2=[1,2,3],不是[1,2,3,1]? 謝謝.

二位前輩好:
我再請問一下,為什麼slice1內容不變,slice2內容卻改變了? 謝謝.
froce iT邦大師 1 級 ‧ 2021-06-17 21:41:21 檢舉
slice2哪有變?
slice2=append(slice2,3)
之後slice2 = [1 2 3]
後面就固定了啊。
EN iT邦好手 1 級 ‧ 2021-06-18 14:20:10 檢舉
fredfu5431 我更新了答案,您看一下!
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

0
EN
iT邦好手 1 級 ‧ 2021-06-17 19:58:49

Hi fredfu5431 and froce,
不好意思,我忽略了 Slice 的特性,在 Go 中, Slice 有以下屬性:

  • cap
  • addr of elements
  • length

在你提供的範例中, slice1slice2cap 一開始都是 2 (他們參照了同一塊記憶體空間)。
不過,你在這邊做了一個關鍵的動作:

slice2=append(slice2,3)

因為 slice2 壓根沒有這麼多容量,所以當你做了 append() 時, Go 就為 slice2 分配了更大的空間,並把原本的值複製過去,這時 slice2 的屬性:

  • cap: 4
  • length: 3

再來,你呼叫了兩次 SliceRise() ,並且 SliceRise 中都用了一次 append ,不過,因為 slice1slice2 的 cap 已經不同了,所以會有不同的結果:

  • slice1 來說:
    slice1 沒有這麼多容量,所以當你做了 append() 時, Go 就為 s 分配了更大的空間,所以你在 SliceRise(slice1) 中操作的是全新的記憶體空間。
  • slice2 來說:
    一開始的 append() 讓 cap 更大了,所以你再一次呼叫 append() 時, slice2 還是裝的下(也就意味著這裡的 sslice2 參照的記憶體相同),操作的自然是同一塊記憶體了。
看更多先前的回應...收起先前的回應...

前輩您好:
謝謝您的回應及說明.
再請教一下,雖然s及slice2參照的是同一塊記憶體,slice2的len及cap仍然不變,是這樣嗎?謝謝

EN iT邦好手 1 級 ‧ 2021-06-18 22:26:16 檢舉

你好,以我的了解,是這樣沒錯!

了解了,謝謝您,感謝感謝

EN iT邦好手 1 級 ‧ 2021-06-19 00:02:39 檢舉

不會,我也學習了 XD

froce iT邦大師 1 級 ‧ 2021-06-19 01:21:56 檢舉

其實你應該留下原本的回答的,因為我沒仔細看你的code...


append的行為似乎不太對喔。
https://www.gushiciku.cn/pl/2dtT/zh-tw

這例子應該是在

slice2=append(slice2,3)

的時候,產生新的slice2,cap為4
然後

SliceRise(slice1)

的時候,產生新的slice1,cap也為4

最後

SliceRise(slice2)

的時候,因為元素個素小於等於4,沒產生新的slice2

用cap(slice)可以看
有問題可以把記憶體位置print出來看
不過我不知道是不是append遇到如果還有連續的記憶體空間的話,就還是用同一個記憶體位址
用下面的playround跑看起來是,但cap是依照上面說的規則變的。

另外其實一開始

slice2:= slice1

的時候,slice2已經和slice1脫鉤了,slice2是slice1的clone

https://play.golang.org/p/HyPIIMdhH0x

EN iT邦好手 1 級 ‧ 2021-06-19 12:57:51 檢舉

其實我跟你表達的意思差不多,我不覺得有哪個部分不對⋯
然後我本來的答案跟你下面提供的程式碼一樣,都是提到傳址的概念。會特別標註您只是因為我們一開始好像都誤會了樓主的意思,所以提供一下更新後的回答給各位。

0
froce
iT邦大師 1 級 ‧ 2021-06-17 20:15:40

golang傳參是複製一份值傳進去,所以你的SliceRise裡面的s都不是本來的slice
你想要的需要用指標。

package main

import (
	"fmt"
)

func SliceRise(s *[]int) {	
	*s = append(*s, 0)	
	for i := range *s {
		(*s)[i]++
	}
	fmt.Println(*s)
}

func main() {
	//hello.goSliceExtend()
	slice1 := []int{1,2}
    slice2:= slice1
    slice2=append(slice2,3)
    slice2[0]=0
    slice2[1]=1
    slice2[2]=2    
    SliceRise(&slice1)
    SliceRise(&slice2)
    fmt.Println(slice1)
    fmt.Println(slice2)
   
}

我要發表回答

立即登入回答