1.数组
func main() {
var a [10] int = [10] int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
for index, data := range a {
fmt.Println(index, ":", data)
}
//部分初始化,没有初始化的元素,自动赋值为0
b := [10] int{1, 2, 3, 3, 2, 1}
for index, data := range b {
fmt.Println(index, ":", data)
}
//指定下标为2和下标为4的两个元素的初始值,余下的自动赋值0
c := [10] int{2: 10, 4: 20}
for index, data := range c {
fmt.Println(index, ":", data)
}
//通过初始化确定数组长度
d := [...] int{1, 2, 3}
for index, data := range d {
fmt.Println(index, ":", data)
}
}
注意:在main( )函数中,定义数组arrs, 然后调用modify( )方法传递数组,同时在modify( )方法中修改数组中第一个元素。 最终输出结果发现,并不会影响main( )函数中数组arrs的值 ,这一点与其它编程语言是有区别的。
func modify(arrs [5]int) {
arrs[0] = 6
fmt.Println("modify:", arrs) //modify: [6 2 3 4 5]
}
func main() {
arrs := [5] int{1, 2, 3, 4, 5}
modify(arrs)
fmt.Println("main:", arrs) //main: [1 2 3 4 5]
}
2.切片
切片:切片与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大,所以可以将切片理解成“动态数组”,但是,它不是数组。
func main() {
s := []int{1, 2, 3} //初始化切片
fmt.Println(s) //[1 2 3]
s = append(s, 4, 5, 6) //追加数据
fmt.Println(s) //[1 2 3 4 5 6]
fmt.Println(s[2]) //3
var s1 []int //切片第二种定义方式:声明切片和声明数组一样,只是少了长度,此为空(nil)切片
s1 = []int{1, 2, 3}
fmt.Println(s1) //[1 2 3]
//切片第三中定义方式:借助make函数, 格式 make(切片类型, 长度, 容量)
//长度是已经初始化的空间。容量是已经开辟的空间,包括已经初始化的空间和空闲的空间。
//切片的长度是5(存有数据,注意如果没有赋值,默认值都是0),容量是10,只不过有5个空闲区域。
s2 := make([]int, 5, 10) //在使用make( )函数定义切片时,一定要注意,切片长度要小于容量
fmt.Println(s2) //[0 0 0 0 0]
fmt.Println("长度是:", len(s2)) //长度是: 5
fmt.Println("容量是:", cap(s2)) //容量是: 10
}
2.1 切片截取
func main() {
s := [] int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(s) //[1 2 3 4 5 6 7 8 9 10]
//使用s[low:high:max]来表示
//第一个数(low)表示下标的起点(从该位置开始截取)
//第二个数(high)表示取到哪结束,也就是下标的终点(不包含该位置)。计算(len=high-low)
//第三个数用来计算容量,所谓容量:是指切片目前可容纳的最多元素个数。计算(cap=max-low)
slice := s[1:5:8]
fmt.Println(slice) //[2 3 4 5]
fmt.Println("长度是:", len(slice)) //长度是: 4
fmt.Println("容量是:", cap(slice)) //容量是: 7
}
2.2 截取其它的操作
操作 含义 s[n] 切片s中索引位置为n的项 s[:] 从切片s的索引位置0到len(s)-1处所获得的切片 s[low:] 从切片s的索引位置low到len(s)-1处所获得的切片 s[:high] 从切片s的索引位置0到high处所获得的切片,len=high s[low:high] 从切片s的索引位置low到high处所获得的切片,len=high-low s[low:high:max] 从切片s的索引位置low到high处所获得的切片,len=high-low,cap=max-low len(s) 切片s的长度,总是<=cap(s) cap(s) 切片s的容量,总是>=len(s)
2.3 copy函数使用
func main() {
s1 := [] int{1, 2}
s2 := [] int{6, 6, 6, 6, 6, 6}
copy(s2, s1)
fmt.Println(s1) //[1 2]
fmt.Println(s2) //[1 2 6 6 6 6]
}
2.4 总结
s6 := array[2:5],将array切片中的array[2],array[3],array[4]截取作为新切片s6,实际上是切片s6指向了原切片array。所以修改s6,也会影响到array。 容量扩容:当容量小于1024时是按照2倍容量扩容,当大于等于1024是不是按照2倍容量扩容。 在GO语言中,数组作为参数进行传递是值传递 ,而切片作为参数进行传递是引用传递 。 值传递:方法调用时,实参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值 。 引用传递:也称为传地址。函数调用时,实际参数的引用被传递给函数中相对应的形式参数(实参与形参指向了同一块存储区域),在函数执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值 。 建议:以后开发中使用切片来代替数组。
func modify(s []int) {
s[0] = 6
fmt.Println("modify:", s) //modify: [6 2 3 4 5]
}
func main() {
arrs := [] int{1, 2, 3, 4, 5}
modify(arrs)
fmt.Println("main:", arrs) //main: [6 2 3 4 5]
}
3.Map
GO语言中的字典结构是有键和值构成的。 键的类型,必须是支持==和!=操作符的类型,切片、函数以及包含切片的结构类型不能作为字典的键 map 作为函数参数是引用传递 。
func main() {
a := map[int]string{1: "java", 2: "python", 3: "go"}
fmt.Println(a)
fmt.Println(len(a)) //len( )函数返回map拥有的键值对的数量
var b map[int]string
b = make(map[int]string)
b[100] = "张三"
b[2] = "李四"
b[5] = "王五"
fmt.Println(b)
delete(b, 100) //删除map中的某个元素。
fmt.Println(b)
c := make(map[string]string) //使用make( )函数来定义
c["zhangsan"] = "张三"
c["lisi"] = "李四"
c["wangwu"] = "王五"
fmt.Println(c)
}
4.结构体
结构体作为函数参数进行传递,是值传递 。 只需要记住:数组和结构体是值传递,其它都是引用传递。
type Student struct {
id int
name string
age int
sex string
addr string
}
func main() {
var s1 Student = Student{1, "张三", 18, "男", "重庆"}
fmt.Println(s1)
var s2 Student
s2.id = 2
s2.name = "李四"
s2.age = 20
s2.sex = "女"
s2.addr = "沙坪坝"
fmt.Println(s2)
s3 := Student{3, "王五", 18, "男", "重庆"}
fmt.Println(s3)
s_arr := []Student{s1, s2, s3} //结构体数组
for i := 0; i < len(s_arr); i++ {
fmt.Println(s_arr[i].name)
}
}
5.指针
指针变量的默认值为nil 不要操作没有合法指向的内存
func main() {
i := 100
fmt.Println(i) //100
fmt.Println(&i) //0xc042050080 获取变量在内存中的地址
//通过指针变量来存储,将获取的地址进行保存
//如果指针变量p 存储的是一个字符串类型变量的地址,那么指针变量p的类型为*string
var p *int //定义一个指针变量
p = &i //把变量i的地址赋值给指针变量P
fmt.Println(p) //0xc042050080
//既然指针变量p指向了变量i的存储单元,那么是否可以通过指针变量p,来操作变量i中存储的数据?
//答案是可以的,具体操作方式如下:
*p = 80 //注意:在使用指针变量p来修改变量i的值的时候,前面一定要加上*
fmt.Println(i) //80
fmt.Println(*p) //80 *p的作用就是根据存储的变量的地址,来操作变量的存储单元
}
5.1 new( )函数
指针变量,除了以上介绍的指向以外(p=&a),还可以通过new( )函数来指向。
func main() {
//new( )函数的作用就是C语言中的动态分配空间。但是在这里与C语言不同的地方,就是最后不需要关系该空间的释放。
//GO语言会自动释放。这也是比C语言使用方便的地方。
var p *int
p = new(int) //new(int)作用就是创建一个整型大小(4字节)的空间,然后让指针变量p指向了该空间
*p = 58 //通过指针变量p进行赋值后,该空间中的值就是58.
fmt.Println(p) //0xc0420080c8
fmt.Println(*p) //58
q := new(int) //自动推导类型
*q = 88
fmt.Println(q) //0xc042008108
fmt.Println(*q) //88
}
5.2 指针做函数参数
普通变量作为函数参数进行传递是值传递,指针作为参数进行传递时,为引用传递 ,也就是传递的地址。
func Swap(a int, b int) {
a, b = b, a
fmt.Println("Swap:", "a=", a, ";b=", b)
}
func Swap1(a *int, b *int) {
*a, *b = *b, *a
fmt.Println("Swap1:", "a=", *a, ";b=", *b)
}
func main() {
a := 10
b := 20
Swap(a, b) //Swap: a= 20 ;b= 10
fmt.Println("main:", "a=", a, ";b=", b) //main: a= 10 ;b= 20
Swap1(&a, &b) //Swap1: a= 20 ;b= 10
fmt.Println("main1:", "a=", a, ";b=", b) //main1: a= 20 ;b= 10
}
5.3 数组指针
用数组作为函数参数,但是数组作为参数进行传递是值传递,如果想引用传递 ,可以使用数组指针。
func Swap(p *[5]int) {
(*p)[0] = 6
}
func main() {
a := [5]int{1, 2, 3, 4, 5}
Swap(&a)
fmt.Println(a) //[6 2 3 4 5]
}
5.4 指针数组
上一小节,讲解到的是数组指针,也就是让一个指针指向数组 ,然后可以通过该指针来操作数组。 指针数组指的是一个数组中存储的都是指针(也就是地址)。也就是一个存储了地址的数组。
func main() {
var p [2]*int //定义指针数组
i := 10
j := 20
p[0] = &i
p[1] = &j
fmt.Println(p) //[0xc042050080 0xc042050088]
fmt.Println(*p[0]) //10 没有加小括号。(注意运算顺序)
fmt.Println(*p[1]) //20
}
5.5 结构体指针变量
我们前面定义了指针指向了数组,解决了数组引用传递的问题。那么指针是否可以指向结构体,也能够解决结构体引用传递的问题呢?完全可以 。
type Student struct {
id int
name string
}
func Test1(s Student) {
s.name = "张三"
fmt.Println("Test1:", s) //Test1: {1 张三}
}
func Test2(s *Student) {
s.name = "郑少秋" //直接通过“.”运算符来操作结构体成员
fmt.Println("Test2:", *s) //Test2: {2 郑少秋}
}
func main() {
//用结构体作为函数的参数,默认的是值传递
s1 := Student{1, "王五"}
fmt.Println(s1) //{1 王五}
Test1(s1) //Test1: {1 张三}
fmt.Println(s1) //{1 王五}
//通过结构体指针,可以实现结构体的引用传递。
p := &Student{2, "楚留香"}
fmt.Println(*p) //{2 楚留香}
Test2(p) //Test2: {2 郑少秋}
fmt.Println(*p) //{2 郑少秋}
}
5.6 多级指针
func main() {
a := 10
p1 := &a
p2 := &p1 //二级指针
fmt.Println(a) //10
fmt.Println(*p1) //10
fmt.Println(**p2) //10
fmt.Println(&a) //0xc042050080
fmt.Println(p1) //0xc042050080
fmt.Println(*p2) //0xc042050080
fmt.Printf("%T\n", a) //int
fmt.Printf("%T\n", p1) //*int
fmt.Printf("%T\n", p2) //**int
}