Go語言切片注意事項

Go語言切片是在項目中經常使用的,在當前實例中會實現切片的賦值、賦值、追加以及在函數中通過值傳遞、引用傳遞的方式來修改切片值

package main

import "fmt"

func main() {
	arrStr := [...]string{"java", "c","c++","python","c#","basic"}
	sliceStr := arrStr[1:4:5]  //下標一表示起始位置,下標二表示結束位置(不包含該位置元素),下標三表示cap容量 cap=len+1,容量可以設置大於len+1
	fmt.Println("len(arrStr),cap(arrStr)", len(arrStr), cap(arrStr))
	fmt.Println(arrStr)
	fmt.Println("len(sliceStr),cap(sliceStr)", len(sliceStr), cap(sliceStr))
	fmt.Println(sliceStr)

	sliceStr[0] = "Go"
	fmt.Println("after assigning slice, arrStr:")
	sliceStr = append(sliceStr, "ruby")
	fmt.Println(arrStr)  //不超容量重新賦值後之前的數組會跟着改變
	fmt.Println("len(sliceStr),cap(sliceStr)", len(sliceStr), cap(sliceStr))
	fmt.Println(sliceStr)

	sliceStr2 := arrStr[1:4]
	fmt.Println("after assigning slice again")
	sliceStr2 = append(sliceStr2, "ruby", "php", "javascript", "pl-sql")
	fmt.Println(arrStr)  //超容量重新賦值後之前的數組不會跟着改變
	fmt.Println("len(sliceStr2),cap(sliceStr2)", len(sliceStr2), cap(sliceStr2))
	fmt.Println(sliceStr2)

	sliceStr3 := []string{"are","you","ok"}
	var sliceStr4 = make([]string ,3)
	copy(sliceStr4, sliceStr3)  //copy會複製原有切片的值,修改時不影響原有元素。
	var sliceStr5 = make([]string,5,10)

	sliceStr4[0] = "how"
	sliceStr4[1] = "are"
	sliceStr4 = append(sliceStr4, "you")

	sliceStr5 = sliceStr3[0:3]
	sliceStr5 = append(sliceStr5, "do","not")

	fmt.Println(sliceStr3)
	fmt.Println(sliceStr4)

	fmt.Println("before changing, sliceStr5:")
	fmt.Println(sliceStr5)


	changeSlice(sliceStr5)  //切片是引用類型,在函數中改變內容的話,在函數外部能看到已經生效了。
	fmt.Println("after changing sliceStr5:")
	fmt.Println(sliceStr5)

	languages := []string{"C","C++","Java","Go","PHP","Python","Ruby"}
	already := languages[:len(languages)-2]

	//這種賦值方法可以保證儘快地將languages數組做垃圾回收操作
	var newAlready = make([]string, len(already))    //先聲明同樣大小的切片
	copy(newAlready, already)						//生成一個切片的副本,原始數組就可以儘快被垃圾回收
	fmt.Println("newAlready:",newAlready)

	other := []string{"Object-C","Swift", "Erlang"}
	newAlready = append(newAlready, other...)   //追加切片的正確寫法
	fmt.Println("after append other slice, newAlready:", newAlready)

	//使用可變參數的函數來更改切片本身的值
	change(other...)
	fmt.Println("after changing, other:", other)

	var other1 = make([]string, len(other))
	copy(other1, other)
	fmt.Printf("before change1(), pointer:%p,cap: %d,", other1, cap(other1)) //獲取切片的內存地址
	fmt.Println("before change1(), other1:", other1)
	change1(other1)  //
	fmt.Printf("after change1(), pointer:%p,cap: %d,len:%d,", other1, cap(other1), len(other1)) //這裏的地址仍然是以前的地址,所以修改有效,追加無效。
	fmt.Println("after change1(), other1:", other1)


	other1 = append(other1, "javascript")
	fmt.Printf("before change2(), pointer:%p,cap: %d,len:%d,", other1, cap(other1), len(other1)) //獲取切片的內存地址
	fmt.Println("before change2(), other1:", other1)
	change2(&other1)  //
	fmt.Printf("after change2(), pointer:%p,cap: %d,", other1, cap(other1)) //這裏的地址仍然是以前的地址,所以修改有效,追加無效。
	fmt.Println("after change2(), other1:", other1)

	other1 = append(other1, "vb.net", "shell") //一旦超出原有長度,會將原有的容量翻倍
	fmt.Printf(" pointer:%p,cap: %d,len:%d \n", other1, cap(other1), len(other1))

	useAppend()

}

func changeSlice(strSlice []string) {
	strSlice[4] = "something"
}

func change(s ...string) {
	s[0] = "Basic"
	s = append(s, "C#") //追加的元素不會在函數外部顯示
	fmt.Println("inside changing(), s:", s)
}

/**
切片包含長度、容量和指向數組第零個元素的指針。
當切片傳遞給函數時,即使它通過值傳遞,指針變量也將引用相同的底層數組。
因此,當切片作爲參數傳遞給函數時,函數內所做的更改也會在函數外可見。讓我們寫一個程序來檢查這點。
 */
func change1(s []string) {
	s[0] = "Sql"
	fmt.Printf("before append(), pointer:%p,cap: %d \n", s, cap(s))
	s = append(s, "C#")  //append函數執行後,會導致s切片的地址發生變化,傳入的切片與現在的切片已經不同
	fmt.Printf("inside change1(), pointer:%p,cap: %d,", s, cap(s))
	fmt.Println("inside change1(), s:", s)
}

/**
	傳入變量地址,將會在函數內外都修改生效
 */
func change2(s *[]string) {
	(*s)[0] = "Oracle"
	fmt.Printf("before append(), pointer:%p,cap: %d,len:%d, \n", *s, cap(*s), len(*s))
	*s = append(*s, "C#")  //append函數執行後,會導致s切片的地址發生變化,傳入的切片與現在的切片已經不同
	fmt.Printf("inside change2(), pointer:%p,cap: %d,len:%d,", *s, cap(*s), len(*s))
	fmt.Println("inside change2(), s:", *s)
}


func useAppend() {
	var numbers []int
	for i := 0; i < 10; i++ {
		numbers = append(numbers, i)
		fmt.Printf("len: %d  cap: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
	}
}

運行結果:

len(arrStr),cap(arrStr) 6 6
[java c c++ python c# basic]
len(sliceStr),cap(sliceStr) 3 4
[c c++ python]
after assigning slice, arrStr:
[java Go c++ python ruby basic]
len(sliceStr),cap(sliceStr) 4 4
[Go c++ python ruby]
after assigning slice again
[java Go c++ python ruby basic]
len(sliceStr2),cap(sliceStr2) 7 10
[Go c++ python ruby php javascript pl-sql]
[are you ok]
[how are ok you]
before changing, sliceStr5:
[are you ok do not]
after changing sliceStr5:
[are you ok do something]
newAlready: [C C++ Java Go PHP]
after append other slice, newAlready: [C C++ Java Go PHP Object-C Swift Erlang]
inside changing(), s: [Basic Swift Erlang C#]
after changing, other: [Basic Swift Erlang]
before change1(), pointer:0xc0000641e0,cap: 3,before change1(), other1: [Basic Swift Erlang]
before append(), pointer:0xc0000641e0,cap: 3 
inside change1(), pointer:0xc0000703c0,cap: 6,inside change1(), s: [Sql Swift Erlang C#]
after change1(), pointer:0xc0000641e0,cap: 3,len:3,after change1(), other1: [Sql Swift Erlang]
before change2(), pointer:0xc000070420,cap: 6,len:4,before change2(), other1: [Sql Swift Erlang javascript]
before append(), pointer:0xc000070420,cap: 6,len:4, 
inside change2(), pointer:0xc000070420,cap: 6,len:5,inside change2(), s: [Oracle Swift Erlang javascript C#]
after change2(), pointer:0xc000070420,cap: 6,after change2(), other1: [Oracle Swift Erlang javascript C#]
 pointer:0xc00007c0c0,cap: 12,len:7 
len: 1  cap: 1 pointer: 0xc00001c140
len: 2  cap: 2 pointer: 0xc00001c160
len: 3  cap: 4 pointer: 0xc0000142e0
len: 4  cap: 4 pointer: 0xc0000142e0
len: 5  cap: 8 pointer: 0xc000018100
len: 6  cap: 8 pointer: 0xc000018100
len: 7  cap: 8 pointer: 0xc000018100
len: 8  cap: 8 pointer: 0xc000018100
len: 9  cap: 16 pointer: 0xc00006e100
len: 10  cap: 16 pointer: 0xc00006e100
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章