六、复合数据类型

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
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值