下述两个函数test1与test2运行结果有何区别?
func test1() {
intArray := []int{1, 2, 3, 4}
for _, val := range intArray {
val++
}
// 仍然为[1, 2, 3, 4]
fmt.Println(intArray)
}
func test2() {
intArray := []int{1, 2, 3, 4}
for i := 0; i < len(intArray); i++ {
intArray[i]++
}
// 改变为[2, 3, 4, 5]
fmt.Println(intArray)
}
for i, val := range(intArray)
另一方面,i和val在循环内都是同一个变量,只在循环头声明一次,即两者的地址不变。这是另一个易错的地方。下述两个函数test3与test4运行结果有何区别?
package main
import "fmt"
func test3() {
slice := []int{0, 1, 2, 3}
myMap := make(map[int]*int)
for index , value := range slice {
myMap[index] = &value
}
// map[0]=3, map[1]=3, map[2]=3, map[3]=3
prtMap(myMap)
}
func test4() {
slice := []int{0, 1, 2, 3}
myMap := make(map[int]*int)
for index , value := range slice {
// 每次进入循环体,声明一个新变量valueCopy,并把value赋值给它
valueCopy := value
myMap[index] = &valueCopy
}
// map[0]=0, map[1]=1, map[2]=2, map[3]=3
prtMap(myMap)
}
func prtMap(myMap map[int]*int) {
for key, value := range myMap {
fmt.Printf("map[%v]=%v\n", key, *value)
}
}
再举一个闭包的例子
package main
import (
"fmt"
"time"
)
func main() {
str := []string{"I","am","Jason"}
for _,v := range str{
// 每个goroutine的v的地址相同,同时为外部v的地址
go func() {
// 这里的v是引用外部变量v的地址
fmt.Println(v)
}()
}
time.Sleep(3 * time.Second)
}
/*
v的地址存储的值为Jason
输出结果:
Jason
Jason
Jason
*/
修改方法,利用函数的值传递性质:
package main
import (
"fmt"
"time"
)
func main() {
str := []string{"I","am","Jason"}
for _,v := range str{
// 把外部的v值拷贝给函数内部的v
// 每个goroutine的v的地址不同,同时不同于外部v的地址
go func(v string) {
fmt.Println(v)
}(v)
}
time.Sleep(3 * time.Second)
}
/*
输出结果: {"I", "am", "Jason"}的排列组合
*/
Slice
for i, v := range x
package main
import (
"fmt"
)
func main() {
x := []int{1, 2, 3, 7, 16, 22, 17, 42}
fmt.Println("We will start out with", x)
for i, v := range x {
fmt.Println("The current value is", v)
x = append(x[:i], x[i+1:]...)
fmt.Println("And after it is removed, we get", x)
}
}
结果:
We will start out with [1 2 3 7 16 22 17 42]
The current value is 1 // i = 0
And after it is removed, we get [2 3 7 16 22 17 42]
The current value is 3 // i = 1
And after it is removed, we get [2 7 16 22 17 42]
The current value is 16 // i = 2
And after it is removed, we get [2 7 22 17 42]
The current value is 17 // i = 3
And after it is removed, we get [2 7 22 42]
The current value is 42 // i = 4
panic: runtime error: slice bounds out of range
goroutine 1 [running]:
main.main()
/tmp/sandbox217462357/main.go:13 +0x3a0
再看一个例子:
package main
import (
"fmt"
)
type Guest struct {
id int
name string
surname string
friends []int
}
func (self Guest) removeFriend(id int) {
for i, other := range self.friends {
if other == id {
self.friends = append(self.friends[:i], self.friends[i+1:]...)
break
}
}
}
func main() {
test := Guest{0, "jason", "jn", []int{1,2, 3, 4, 5}}
fmt.Println(test)
test.removeFriend(4)
fmt.Println(test)
}
结果:
{0 jason jn [1 2 3 4 5]}
// 为什么结果不是{0 jason jn [1, 2, 3, 5]},后面多了个5
{0 jason jn [1 2 3 5 5]}
注意,注意test调用removeFriend方法是通过值拷贝得到self,而self.friends的四元组和self.test的三元组内容是一样的{paddr, len, cap}(其中paddr指向[1, 2, 3, 4, 5]),但二者是不同的变量。
self.friends = append(self.friends[:i](paddr, 3, 5), self.friends[i+1:]...(5))
字符串
在 Go 中,字符串是以 UTF-8 为格式进行存储的,在字符串上调用 len 函数,取得的是字符串包含的 byte 的个数。
func test5() {
str := "beijing,北京"
// 14
fmt.Println(len(str))
// 10
fmt.Println(utf8.RuneCountInString(str))
// 10
fmt.Println(len([]rune(str)))
// 10
fmt.Println(utf8.RuneCount([]byte(str)))
}
再看下面例子,在go语言中支持两种方式遍历字符串。
1.以字节数组的方式遍历。
str := "beijing,北京"
for i := 0; i < len(str); i++{
fmt.Printf("%d: %v : %T\n", i, str[i], str[i])
}
输出结果为:
0: 98 : uint8
1: 101 : uint8
2: 105 : uint8
3: 106 : uint8
...
11: 228 : uint8
12: 186 : uint8
13: 172 : uint8
可以看出,这个字符串长度为14,尽管从直观上来说,这个字符串应该只有10个字符,这是因为每个中文字符在UTF-8中占3个字节,而不是1个字节。
2.以Unicode字符遍历
str := "beijing,北京"
for i, val := range str {
fmt.Printf("%d: %v : %T\n", i, val, val)
}
输出结果为:
0: 98 : int32
1: 101 : int32
2: 105 : int32
3: 106 : int32
4: 105 : int32
5: 110 : int32
6: 103 : int32
7: 44 : int32
8: 21271 : int32
9: 20140 : int32
以Unicode字符方式遍历时,每个字符的类型是rune,而不是byte。rune类型在go语言中占用四个字节。