声明指针
指针也是一种数据类型,也可以使用var
来声明:var 变量名 *数据类型
注意,这里的变量名实际保存的数据是一个十六进制的内存地址,这里的数据类型指的是这个十六进制的内存地址要保存的数据类型。这里就生成了一个int
类型的指针。
func main() {
var a *int//声明变量'a'为'int'类型指针
*a = 100//根据'*a'的地址赋值
fmt.Println(a)//输出'*a'的数据
}
运行报错如下:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x64160555b4]
goroutine 1 [running]:
main.main()
/data/data/com.termux/files/home/go/src/socold.com/socold/zhizhen/main.go:8 +0x1c
exit status 2
上面代码为什么会报错?因为只声明了指针a
,但是没有初始化。a
是一个int
类型的指针,应该保存一个十六进制的内存地址,而a
保存的这个十六进制的内存地址上应该保存一个int
类型的数据。现在的情况是变量a
声明了,是一个*int
的指针,但是没有初始话,也就是说a
啥地址也没保存,你就让我根据地址赋值,但是你还没给我地址呢。所以报错。
注释掉*a = 100
可发现输出nil
。可以使用内置函数new(int)
来为a
申请一个内存地址。
func main() {
var a *int
a = new(int)
*a = 100
fmt.Println(a)
fmt.Println(*a)
}
指针初始化
aa = new(int)
这里需要注意int
要跟声明指针时的数据类型一致。初始化后就可以赋值使用了。
一般这种写法很少用,一般都用var a = new (int)
写法。new()
返回的是一个十六进制的内存地址。这里需要和make()
函数区分开,make()
也是分配内存的函数,一般用了给slice,map,channel分配内存。make()
返回的是数据本身。如下代码:
func main() {
fmt.Println(make([]int, 1))
fmt.Println(new(int))
}
指针
指针只要记住两个符号&
和*
就好。&a
返回a
的内存地址。*b
返回的是值,首先b
得是一个指针型变量,*b
根据指针型变量b
保存的地址,取得该地址对应的数据。
a := 19
b := &a
fmt.Printf("a数据类型:%T 值:%v \n", a, a)
fmt.Printf("b数据类型:%T 值:%v \n", b, b)
fmt.Println("-------------------")
fmt.Printf("a地址:%v a值:%v \n", &a, a)
fmt.Printf("b地址:%v b值:%v \n", &b, b)
fmt.Printf("*b值:%v \n", *b)
map类型
map声明
map类型使用var map[key类型]键值类型
来声明,map是引用类型,需要使用make(map[string]int,10)
来初始化分配内存空间。没有初始化的map类型数据编译时不会报错,运行时会报如下错误:
var ma map[string]int
// ma = make(map[string]int,10)
ma["aa"] = 8
ma["bb"] = 9
ma["cc"] = 10
ma["dd"] = 11
ma["ee"] = 12
fmt.Printf("ma: \n%v", ma)
上面代码报错如下:
panic: assignment to entry in nil map
goroutine 1 [running]:
main.main()
/data/data/com.termux/files/home/go/src/socold.com/socold/zhizhen/main.go:55 +0x3c
exit status 2
[Done] exited with code=1 in 1.402271 seconds
初始化map
初始化map是建议预先估算好map的容量,虽然map可以动态扩容,但是会影响程序运行效率。初始化map的语句ma = make(map[key类型]键值类型,map容量)
。
如何使用map类型数据:ma["aa"]
即可返回键为"aa"
的值8
。如果键不存在会返回键值对应数据类型的0值。一般用if语句来判断key存不存在。如下代码:
v, ok := ma["zz"]
if !ok {
fmt.Println("key:ma['zz']不存在。")
} else {
fmt.Printf("ma: %v \n", v)
}
遍历map
map类型数据的遍历和数组一样用for k,v := range ma{}
语句。map类型遍历出来是随机的,无序的。有可能keyma["aa"]
排第一个也有可能排最后一个。
func main() {
var ma map[string]int
ma = make(map[string]int, 10)
ma["aa"] = 8
ma["bb"] = 9
ma["cc"] = 10
ma["dd"] = 11
ma["ee"] = 12
for k, v := range ma {
fmt.Printf("key:%v value:%v \n", k, v)
}
}
删除map元素
删除map元素需要用delete(ma,"aa")
。如果要删除的key不存在,程序不会进行任何操作。
func main() {
var ma map[string]int
ma = make(map[string]int, 10)
ma["aa"] = 8
ma["bb"] = 9
ma["cc"] = 10
ma["dd"] = 11
ma["ee"] = 12
for k, v := range ma {
fmt.Printf("key:%v value:%v \n", k, v)
}
//删除map类型变量ma中的元素
delete(ma, "aa")
fmt.Println("-----------------------------")
for k, v := range ma {
fmt.Printf("key:%v value:%v \n", k, v)
}
}
map变种
值为切片的map
func main() {
var ma map[string][]int
ma = make(map[string][]int, 3)
aa := []int{0, 9, 8}
bb := []int{1, 2, 3, 4, 5}
cc := []int{6, 7}
ma["切片1"] = aa
ma["切片2"] = bb
ma["切片2"] = cc
fmt.Printf("值为切片的map:\n %v \n", ma)
}
其实吧,这样写没啥问题,但是有点麻烦,看下面这个写法,更简单:
ma["切片1"] = []int{8, 3, 6}
ma["切片2"] = []int{4, 5, 0, 1, 6}
fmt.Printf("%v", ma)
值为map的切片
var qie = make([]map[string]int, 2, 3)
qie[0] = make(map[string]int, 2)
qie[0]["铁锤"] = 18
qie[0]["二牛"] = 14
fmt.Println(qie)
报错提示
某些错误写法的报错提示:
- 索引越界。下面代码编译是没问题,运行出错。愿因是切片
qie
声明并初始化时make()
函数内只给了容量为3,元素数量给的是0,给的报错信息是index out of range [0] with length 0,学名叫索引越界。
索引越界错误提示。var qie = make([]map[string]int, 0, 3) qie[0]["切片1"] = 9 fmt.Println(qie)
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
main.main()
/data/data/com.termux/files/home/go/src/socold.com/socold/zhizhen/main.go:155 +0x3c
exit status 2
[Done] exited with code=1 in 1.514425 seconds
- map没有初始化编译器不会报错,运行时报如下错误,报错提示信息为assignment to entry in nil map
错误提示信息:var qie = make([]map[string]int, 1, 3) qie[0]["切片1"] = 9 fmt.Println(qie)
panic: assignment to entry in nil map
goroutine 1 [running]:
main.main()
/data/data/com.termux/files/home/go/src/socold.com/socold/zhizhen/main.go:168 +0x68
exit status 2
[Done] exited with code=1 in 1.443653 seconds
实例代码
识别字符串中的中文
研究下这个代码,这是一个识别给定字符串中有几个中文的代码。需要引入unicode
包。
func main() {
i := 0
s := "abcd今天不太冷あなたは日本人ですこんにちは안녕하세요"
for _, v := range s {
if unicode.Is(unicode.Han, v) { //unicode.Hangul是韩文,unicode.Hiragana是平假名,应该是日文。
i++
}
}
fmt.Println(i)
}
回文判断
来一个实例,判断给定的字符串是不是回文:
func huiwen(s string) bool {
//建一个切片
s1 := make([]string, 0, len(s))
//把字符串导入切片s1
for _, v := range s {
s1 = append(s1, string(v))
}
//遍历切片s1
for i := 0; i < len(s1)/2; i++ {
if s1[i] != s1[len(s1)-1-i] {
return false
}
}
return true
}
func main() {
//回文判断:
s := "上海自来水来自海上a"
if huiwen(s) {
fmt.Println("是回文")
} else {
fmt.Println("不是回文")
}
}
计算单词出现次数
在来一个实例。给一段英文短文,要求计算每个单词出现的次数,不能区分大小写。
func main() {
//返回给定字符串中个单词出现的次数。
s := "An old woman had a cat. The cat was very old; she could not run quickly, and she could not bite, because she was so old. One day the old cat saw a mouse;"
//预处理字符串
var s1 []string
var word string
for _, v := range s {
if unicode.IsLetter(v) {
//如果是字母,保存到字符串缓存
word += string(unicode.ToLower(v))
} else if word != "" {
//如果不是字母,
s1 = append(s1, word)
word = ""
}
}
//将处理好的缓存切片's1'导入'map'并计算单词出现次数。
//声明并初始化一个map
smap := make(map[string]int, 30)
//切片s1导入map并计算单词出现的次数
for _, v := range s1 {
if _, k := smap[v]; k {
smap[v]++
} else {
smap[v] = 1
}
}
//遍历smap
for v, k := range smap {
fmt.Printf("smap[%v]-[%v] \n", k, v)
}
}