目录:
一、指针类型(Pointer)
二、数组类型
三、结构化类型(struct)
四、 切片类型
五、Map 类型
六、迭代
一、指针类型(Pointer)
Go语言指针类似C/C++,区别在于它不支持指针运算。
&
为取地址符&,放到一个变量前使用就会返回相应变量的内存地址。
一个指针变量可以指向任何一个值的内存地址,它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节,占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时,它的默认值为 nil。指针变量通常缩写为 ptr。
func main() {
a:=1
ptr:=&a //等价var ptr *int=&a
fmt.Println(ptr)
fmt.Println(*ptr)
}
#输出
0xc00000a0b8
1
指针最基本的操作就是对地址直接访问(读/写):
func main() {
a:=1
ptr:=&a //等价var ptr *int=&a
fmt.Println(ptr)
*ptr=2
fmt.Println(a)
}
#输出
0xc00000a0b8
2
介绍到这里似乎开始疑惑为什么总有人对指针谈虎色变呢?因为指针一旦结合函数、数组、切片、闭包等投入到实例应用时,越界、野指针、偏移等等各种问题便产生了,作为入门系列不在此展开。
二、数组类型
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列。
格式规则为:var variable_name [SIZE] variable_type
func main() {
var user [10] string
user[0]="rabbit"
fmt.Print(user)
}
#输出
[rabbit ]
或者可不设置size直接进行初始化,Go 语言会根据元素的个数来设置数组的大小:
func main() {
var user =[] string{"rabbit","carrot"}
fmt.Print(user)
}
#输出
[rabbit carrot]
当然可以像其它基本类型一样继续偷懒:
func main() {
user:=[] string{"rabbit","carrot"}
fmt.Print(user)
}
三、结构化类型(struct)
即结构体,结构体是由一系列具有相同类型或不同类型的数据构成的数据集合,结构体定义需要使用 type 和 struct 语句,格式规则样例如下,相对于数组可以存储同一类型的数据,结构体可以为不同项定义不同的数据类型。:
type People struct {
name string
age int
city string
}
基本访问方式:
func main() {
type User struct {
name string
age int
city string
}
user :=User{name:"rabbit",age:18}
fmt.Print(user.name)
}
#输出
rabbit
同样,我们可以结合刚刚提到的指针,将指针结合到结构体变量中:
func main() {
type User struct {
name string
age int
city string
}
user :=User{name:"rabbit",age:18}
ptr:=&user
fmt.Print(ptr.name)
}
#输出
rabbit
此外,你可以像其他数据类型一样将结构体类型作为参数传递给函数。并以以上实例的方式访问结构体变量,这个我们在下一章的函数章节会提到。
四、切片类型
切片是对数组的抽象,Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
我们可以直接对数组进行截取,len()
和cap()
分别可测量长度和测量切片最长可以达到多少。
func main() {
arr:=[]int{1,2,3,4,5}
s:=arr[0:3]
fmt.Println(arr)
fmt.Println(s)
fmt.Println(len(s),cap(s))
}
我们也可以通过内置的make()方法声明一个切片,其规则为:
s :=make([]int,len,cap)
初始化为空切片,实际长度初始化后重新动态校正:
func main() {
arr:=[]int{1,2,3,4,5}
s:=make([]int,0,5)
fmt.Println(len(s),cap(s),s)
s=arr[3:5]
fmt.Println(len(s),cap(s),s)
}
#输出
0 5 []
2 2 [4 5]
make()方法中的cap参数可以省略,此外切片提供了copy()方法和append()方法,用以拷贝切片和增加新元素。
func main() {
arr:=[]int{1,2,3,4,5}
s:=arr[0:3]
s1:=make([]int,10)
fmt.Println(len(s),cap(s),s)
copy(s1,s)
fmt.Println(len(s1),cap(s1),s1)
s1=append(s,6,7,8)
fmt.Println(len(s1),cap(s1),s1)
}
#输出
3 5 [1 2 3]
10 10 [1 2 3 0 0 0 0 0 0 0]
6 10 [1 2 3 6 7 8]
其中copy()方法有个需要注意的地方,如果s1声明的长度为0,你会发现s1并未copy成功,因为copy()不会新建新的内存空间,由它原来的切片长度决定。
五、 Map 类型
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
如果你有python基础,我说字典类型你可能会立刻明白,yes,就是它。
map声明方式有两种,其格式规则为:
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type
/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)
需要注意的是,使用前者需要初始化,我们尝试下基本使用。
func main() {
user:=make(map[string]int)
user["rabbit"]=18
user["carrot"]=17
fmt.Print(user,user["rabbit"])
}
#输出
map[carrot:17 rabbit:18] 18
或者使用前者并借助make初始化:
func main() {
var user map[string]int
user=make(map[string]int)
user["rabbit"]=18
user["carrot"]=17
fmt.Print(user,user["rabbit"])
}
此外,在切片小节我们提到了,make()方法是有一个可省略的容量属性的,我们上述代码同样选择了忽略,但请留意它的存在,如下使用也是允许的。
user=make(map[string]int,100)
此外,map支持delete()方法:
func main() {
var user map[string]int
user=make(map[string]int,100)
user["rabbit"]=18
user["carrot"]=17
fmt.Println(user)
delete(user,"carrot")
fmt.Println(user)
}
#输出
map[carrot:17 rabbit:18]
map[rabbit:18]
六、迭代
当然,迭代并不算数据类型,放在本节最后讨论下复合数据类型的迭代方式,主要借助range方法。
首先遍历我们刚看完的map类型:
func main() {
user:=make(map[string]int,100)
user["rabbit"]=18
user["carrot"]=17
for k,v :=range user{
fmt.Printf("%s - > %d\n",k,v)
}
}
#输出
carrot - > 17
rabbit - > 18
然后尝试下数组迭代:
func main() {
arr:=[]int{1,2,3,4,5}
for i,num:=range arr{
println(i,num)
}
}
#输出
0 1
1 2
2 3
3 4
4 5
可以看到相对于python区别还是比较大的,返回值为两个,索引和值。
在此暂时以这两个常用类型为例,后边根据进度补充。