从0开始学GO之结构体、切片与Range


更多参见: 从0开始学GO之目录

结构体

Go 语言中数组可以存储同一类型的数据,但在结构体中可以为不同项定义不同的数据类型。

结构体是一种复合的数据类型,它是由一系列具有相同类型或不同类型的数据构成的数据集合。每个数据称为结构体的成员。

定义结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

结构体初始化

普通变量与指针变量

package main
import "fmt"

type Books struct {
	title string
	author string
	subject string
	book_id int
}

func main() {
	// 普通变量
	Book1 := Books{"Go 语言", "www.runoob.com", "Go 语言教程", 12345} // 也可以如下
	// var Book1 Books        // 普通变量
	//Book1.title = "Go 语言"
	//Book1.author = "www.runoob.com"
	//Book1.subject = "Go 语言教程"
	//Book1.book_id = 6495407
	// var Book1 Books  = Books{"Go 语言", "www.runoob.com", "Go 语言教程", 12345}
	// 指针变量
	Book2 :=  &Books{"Python 教程", "www.runoob.com", "Python 语言教程", 54321}      // 也可以如下
	// var Book2 *Books =  &Books{"Python 教程", "www.runoob.com", "Python 语言教程", 54321}
	fmt.Println(Book1)
	fmt.Println(Book2)

}

在这里插入图片描述

结构体作为函数参数

值传递与引用传递

package main
import "fmt"

type Books struct {
	title string
	author string
	subject string
	book_id int
}

func printBookAndChange1( book Books ) {
	fmt.Printf( "Book title : %s\n", book.title)
	fmt.Printf( "Book author : %s\n", book.author)
	fmt.Printf( "Book subject : %s\n", book.subject)
	fmt.Printf( "Book book_id : %d\n", book.book_id)
	book.title = book.title + "_Add"
	book.book_id = -12345
}

func printBookAndChange2( book *Books ) {
	fmt.Printf( "Book title : %s\n", book.title)
	fmt.Printf( "Book author : %s\n", book.author)
	fmt.Printf( "Book subject : %s\n", book.subject)
	fmt.Printf( "Book book_id : %d\n", book.book_id)
	book.title = book.title + "_Add"
	book.book_id = -12345
}

func main() {
	Book1 := Books{"Go 语言", "www.runoob.com", "Go 语言教程", 12345}
	Book2 :=  &Books{"Python 教程", "www.runoob.com", "Python 语言教程", 54321}
	fmt.Println("======================")
	fmt.Println(Book1)
	fmt.Println(Book2)
	fmt.Println("======================")
	printBookAndChange1(Book1)
	fmt.Println("======================")
	printBookAndChange2(Book2)
	fmt.Println("======================")
	fmt.Println(Book1)
	fmt.Println(Book2)
	fmt.Println("======================")
	//p.成员 和(*p).成员 操作是等价的
	Book2.book_id = -1
	(*Book2).title = "Python"
	fmt.Println(Book2)
}

在这里插入图片描述

切片

Go 语言切片是对数组的抽象。数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。

在这里插入图片描述

图片参见 https://www.jianshu.com/p/659da9efd55f

定义切片

切片不需要说明长度,可以声明一个未指定大小的数组来定义切片:

var identifier []type

或使用 make() 函数来创建切片:

var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)

也可以指定容量,其中 capacity 为可选参数。

make([]T, length, capacity)

切片初始化

s :=[] int {1,2,3 } 
直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。
s := arr[:] 
初始化切片 s,是数组 arr 的引用。
s := arr[startIndex:endIndex] 
将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。
s := arr[startIndex:] 
默认 endIndex 时将表示一直到arr的最后一个元素。
s := arr[:endIndex] 
默认 startIndex 时将表示从 arr 的第一个元素开始。
s1 := s[startIndex:endIndex] 
通过切片 s 初始化切片 s1。
s :=make([]int,len,cap) 
通过内置函数 make() 初始化切片s,[]int 标识为其元素类型为 int 的切片。

切片的操作

操作含义
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的长度,总是<=casp(s)
cap(s)切片s的容量,总是>=len(s)

示例说明:

    array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
操作结果lencap说明
array[:6:8][0 1 2 3 4 5]68省略 low
array[5:][5 6 7 8 9]55省略 high、 max
array[:3][0 1 2]310省略 high、 max
array[:][0 1 2 3 4 5 6 7 8 9]1010全部省略

切片和底层数组关系

package main

import "fmt"

func main() {
	s1 :=[] int {1,2,3 }
	fmt.Println("s1 = ",s1)  // s1 =  [1 2 3]
	arr := [] int {1,2,3,4,5,6}
	s2 := arr[:] //  s2,是数组 arr 的引用。
	s3 := arr[1:5]   // 将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。
	s4:= arr[2:]
	s5:= arr[:5]
	s6 := s3[1:3]
	fmt.Println("s2 = ",s2) // s2 =  [1 2 3 4 5 6]
	fmt.Println("s3 = ",s3) // s3 =  [2 3 4 5]
	fmt.Println("s4 = ",s4) // s4 =  [3 4 5 6]
	fmt.Println("s5 = ",s5) // s5 =  [1 2 3 4 5]
	fmt.Println("s6 = ",s6) // s6 =  [3 4]
	s2[0] = -2
	fmt.Println("arr = ",arr) // arr =  [-2 2 3 4 5 6]
	s3[1] = -3
	fmt.Println("arr = ",arr) // arr =  [-2 2 -3 4 5 6]
	s4[2] = -4
	fmt.Println("arr = ",arr) // arr =  [-2 2 -3 4 -4 6]
	s5[3] = -5
	fmt.Println("arr = ",arr) // arr =  [-2 2 -3 -5 -4 6]
	s6[1] = -6
	fmt.Println("arr = ",arr) // arr =  [-2 2 -3 -6 -4 6]
}

在这里插入图片描述

len() 、cap() 、append、copy函数

切片是可索引的,并且可以由 len() 方法获取长度。

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

append函数向 slice 尾部添加数据,返回新的 slice 对象。append函数会智能地底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据

函数 copy 在两个 slice 间复制数据,复制⻓度以 len 小的为准,两个 slice 可指向同⼀底层数组。

package main

import "fmt"

func main() {
	s := make([]int, 0, 1) // 长度 0  容量 1
	c := cap(s)
	printSlice(s)
	for i := 0; i < 50; i++ {
		s = append(s, i)
		if n := cap(s); n > c {
			printSlice(s)
			c = n
		}
	}
	printSlice(s)
	fmt.Println("==1===============")
	s1 := make([]int, 5, 7)
	copy(s1, s)    // dst:s1, src:s
	printSlice(s) // len=50 cap=64
	printSlice(s1) // len=5 cap=7
	fmt.Println("==2===============")
	s1[0] = -1  // s1 改变 s 不改变
	printSlice(s)
	printSlice(s1) // len=5 cap=7 slice=[-1 1 2 3 4]
	fmt.Println("==3===============")
	data := [...]int{0, 1, 2, 3, 4, 5}  // 长度 6
	fmt.Println("data = ",data)
	s2 := make([]int, 5, 7) // 长度 5  容量 7
	printSlice(s2) // len=5 cap=7 slice=[0 0 0 0 0]
	s2 = data[:4] //{0, 1, 2, 3}
	s2[0] = 100 // 对 data 有影响
	fmt.Println("data = ",data) // data =  [100 1 2 3 4 5]
	c2 := cap(s2)
	printSlice(s2) // len=4 cap=6 slice=[0 1 2 3]
	fmt.Println("==4===============")
	for i := 0; i < 10; i++ {
		s2 = append(s2, i)
		if n := cap(s2); n > c2 {
			printSlice(s2)
			c = n
		}
	}
	fmt.Println("==5===============")
	// 以2倍容量重新分配底层数组,并复制原来的数据
	s2[0] = -1  // 对 data 无影响
	printSlice(s2) // len=14 cap=24 slice=[-1 1 2 3 0 1 2 3 4 5 6 7 8 9]
	fmt.Println("data = ",data) // data =  [0 1 2 3 0 1]
}

func printSlice(x []int){
	fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

在这里插入图片描述

range 范围

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

package main
import "fmt"
func main() {
    //这是我们使用range去求一个slice的和。使用数组跟这个很类似
    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)
    //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。
    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }
    //range也可以用在map的键值对上。
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }
    //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
    for i, c := range "go" {
        fmt.Println(i, c)
    }
}

在这里插入图片描述

参考:

https://www.runoob.com/go/go-structures.html

https://www.runoob.com/go/go-range.html

个人能力有限,如有错误或者其他建议,敬请告知欢迎探讨,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值