一、指针
1.指针的基本操作
var a int = 10
//每个变量有2层含义:变量的内容,变量的地址
fmt.Printf("a=%d\n", a)//变量的内容
fmt.Printf("&a=%v\n", &a)//变量的地址
//保存某个变量的地址,需要指针类型,*int保存int的地址,**int保存*int的地址
var p *int//声明指针类型变量
p = &a//指针变量指向谁,就把谁的地址赋值给指针变量
fmt.Printf("p=%v,&a=%v\n", p, &a)
*p = 666//*p操作的不是p的内存,是p所指向的内存(就是a)
fmt.Printf("*p=%v,a=%v\n", *p, a)
2.不要操作没有合法指向的内存
var p *int
p = nil//p指向空指针
fmt.Println("p=", p)
*p = 666
3.new()函数的使用
var p *int
p=new(int)//申请一个合法内存
//无需担心其内存的生命周期或删除问题,内存管理系统会自动回收
*p=666
fmt.Println("*p=",*p)
4.普通变量做函数参数
func main() {
a, b := 10, 20
swap(a, b)//通过一个函数交换a和b的内容
fmt.Printf("main:a=%d,b=%d\n", a, b)
}
func swap(a, b int) {//形参,值传递
a, b = b, a
fmt.Printf("swap:a=%d,b=%d\n", a, b)
}
5.指针作函数参数
func main() {
a, b := 10, 20
swap(&a, &b)//地址传递
fmt.Printf("main:a=%d,b=%d\n", a, b)
}
func swap(a, b *int) {
*a, *b = *b, *a
fmt.Printf("swap:a=%d,b=%d\n", *a, *b)
}
二、数组
1.数组的必要性
作为同一类型数据的集合,数组的操作要比单独操作数据简便灵活
var id [50]int
for i := 0; i < len(id); i++ {
id[i] = i + 1
fmt.Printf("id[%d]=%d\n", i, id[i])
}
2.数组的基本使用
//[数字],这个数作为数组元素的个数
var a [10]int//注意:定义数组时,数组元素的个数必须为常量
var b [5]int
fmt.Printf("len(a)=%d,len(b)=%d\n", len(a), len(b))
//数组下标可以是常量或者变量
a[0] = 1
i := 1
a[i] = 2
//给每个元素赋值
for i := 0; i < len(a); i++ {
a[i] = i + 1
}
//迭代打印
for i, data := range a {
fmt.Printf("a[%d]=%d\n", i, data)
}
3.数组的初始化
//声明定义同时赋值,叫初始化
//1.全部初始化
var a [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println("a=", a)
b := [5]int{1, 2, 3, 4, 5}
fmt.Println("b=", b)
//部分初始化,没有初始化的元素,自动赋值为0
c := [5]int{1, 2, 3}
fmt.Println("c=", c)
//指定某个元素初始化
d := [5]int{2: 10, 4: 20}
fmt.Println("d=", d)
4.二维数组
//有多少个[]就是有多少维,赋值时就用多少个循环
var a [3][4]int
k := 0
for i := 0; i < 3; i++ {
for j := 0; j < 4; j++ {
k++
a[i][j] = k
fmt.Printf("a[%d][%d]=%d", i, j, a[i][j])
}
fmt.Println()
}
fmt.Println("a=", a)
//有三个元素,每一个元素又是一个一维数组[4]int
b := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}
fmt.Println("b=", b)
//部分初始化,没有初始化的值为0
c := [3][4]int{{1, 2, 3}, {5, 6, 7, 8}, {9, 10}}
fmt.Println("c=", c)
d := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
fmt.Println("d=", d)
e := [3][4]int{1: {5, 6, 7, 8}}
fmt.Println("e=", e)
5.数组比较与赋值
//支持==,!=的比较,比较是不是每个元素都一样,2个数组比较,数组类型要相同
a := [5]int{1, 2, 3, 4, 5}
b := [5]int{1, 2, 3, 4, 5}
c := [5]int{2, 2, 3, 4, 5}
fmt.Println("a==b", a == b)
fmt.Println("a==c", a == c)
//同类型的数组可以赋值
var d [5]int
d = a
fmt.Println("d=", d)
6.数组作函数参数
//数组作函数参数,它是值传递
//实参数组的每个元素给形参数组拷贝一份
//形参的数组是实参数组的复制品
func modify(a [5]int) {
a[0] = 666
fmt.Println("modify a=", a)
}
func main() {
a := [5]int{1, 2, 3, 4, 5}//初始化
modify(a)//形参传递
fmt.Println("main a:=", a)
}
7.数组指针作函数参数
//p指向实参数组a,它是指向数组,它是数组指针
//*p代表指针所指向的内存,就是实参a
func modify(p *[5]int) {
(*p)[0] = 666
fmt.Println("modify a=", *p)
}
func main() {
a := [5]int{1, 2, 3, 4, 5}//初始值
modify(&a)//地址传递
fmt.Println("main a:=", a)
}
三、随机数
1.随机数的使用
//设置种子,只需要一次
//如果种子参数一样,每次运行程序产生的随机数都一样
rand.Seed(time.Now().UnixNano())//取当前的系统时间作为种子
for i := 0; i < 5; i++ {
fmt.Println("rand=", rand.Int())//不限制随机数的大小
fmt.Println("rand=", rand.Intn(1000))//随机数大小限制在1000以内
}
2.冒泡排序
func swap(a, b *int) {
tmp := *a
*a = *b
*b = tmp
}
func main() {
rand.Seed(time.Now().UnixNano())
var a [10]int
n := len(a)
for i := 0; i < n; i++ {
a[i] = rand.Intn(100)
fmt.Printf("%d,", a[i])
}
fmt.Println()
//冒泡排序,挨着2个元素比较,降序则交换
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if a[j] > a[j+1] {
//a[j],a[j+1]=a[j+1][j]
swap(&a[j], &a[j+1])
}
}
}
for i, data := range a {
fmt.Printf("a[%d]=%d\n", i, data)
}
fmt.Println()
}
四、切片
1.切片的长度和容量
a := []int{1, 2, 3, 0, 0}//数组初始化
s := a[0:3:5]//从0位置,截取长度为3,总容量为5
fmt.Println("s=", s)
fmt.Println("len(s)=", len(s))
fmt.Println("cap(s)=", cap(s))
s = a[1:4:5]
fmt.Println("s=", s)
fmt.Println("len(s)=", len(s))
fmt.Println("cap(s)=", cap(s))
2.切片的创建
s1 := []int{1, 2, 3, 4}
fmt.Println("s1=", s1)
s2 := make([]int, 5, 10)//借助make函数,格式make(slice,type,len,cap)
fmt.Printf("len=%d,cap=%d\n", len(s2), cap(s2))
s3 := make([]int, 5)//不指定容量,那长度和容量相同
fmt.Printf("len=%d,cap=%d\n", len(s3), cap(s3))
//数组[]里面的长度是固定的一个常量,数组不能修改长度,len和cap永远等于[]里的值
a := [5]int{}
fmt.Printf("len=%d,cap=%d\n", len(a), cap(a))
//切片,[]里面为空或者为...,切片的长度或容量可以不固定
s := []int{}
fmt.Printf("len=%d,cap=%d\n", len(s), cap(s))
s = append(s, 11)//给切片末尾追加一个成员
fmt.Printf("len%d,cap=%d\n", len(s), cap(s))
3.切片的截取
array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
//[low:high:max]取下标从low开始,len=high-low,cap=max-low
s1 := array[:]//[0:len(array):len(array)]不指定容量和长度一样
fmt.Println("s1=", s1)
fmt.Printf("len=%d,cap=%d\n", len(s1), cap(s1))
data := array[0]//操作单个元素与数组相同
fmt.Println("data=", data)
s2 := array[3:6:7]
fmt.Println("s2=", s2)
fmt.Printf("len=%d,cap=%d\n", len(s2), cap(s2))
s3 := array[:6]//从0开始,取6个元素,容量不是6
fmt.Println("s3=", s3)
fmt.Printf("len=%d,cap=%d\n", len(s3), cap(s3))
s4 := array[3:]//从下标3开始,到结尾,长度和容量相等
fmt.Println("s4=", s4)
fmt.Printf("len=%d,cap=%d\n", len(s4), cap(s4))
4.切片与底层数组的关系
a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := a[2:5]
fmt.Println("s1=", s1)
s1[1] = 666
fmt.Println("s1=", s1)
fmt.Println("a=", a)
s2 := s1[2:7]
s2[2] = 777
fmt.Println("s2=", s2)
fmt.Println("a=", a)
5.append函数的使用
s1 := []int{}
fmt.Printf("len=%d,cap=%d\n", len(s1), cap(s1))
fmt.Println("s1=", s1)
s1 = append(s1, 1)
s1 = append(s1, 2)
s1 = append(s1, 3)
fmt.Println("s1=", s1)
fmt.Printf("len=%d,cap=%d\n", len(s1), cap(s1))
s2 := []int{1, 2, 3}
fmt.Println("s2=", s2)
s2 = append(s2, 5)
s2 = append(s2, 5)
s2 = append(s2, 5)
fmt.Println("s2=", s2)
fmt.Printf("len=%d,cap=%d\n", len(s2), cap(s2))
6.append扩容特点
2倍扩容
s := make([]int, 0, 1)
oldCap := cap(s)
for i := 0; i < 8; i++ {
s = append(s, i)
if newCap := cap(s); oldCap < newCap {
fmt.Printf("cap:%d=====>%d\n", oldCap, newCap)
oldCap = newCap
}
}
7.copy的使用
srcSlice := []int{1, 2}
dstSlice := []int{6, 6, 6, 6, 6}
copy(dstSlice, srcSlice)
fmt.Println("dstSlice=", dstSlice)
8.切片做函数参数
切片做参数,引用传递
//切片做参数,引用传递
func InitData(s []int) {
rand.Seed(time.Now().UnixNano())//设置种子
for i := 0; i < len(s); i++ {
s[i] = rand.Intn(100)
}
}
func BubbleSort(s []int) {
n := len(s)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if s[j] > s[j+1] {
swap(&s[j], &s[j+1])
}
}
}
}
func swap(a, b *int) {
tmp := *a
*a = *b
*b = tmp
}
func main() {
var n int = 10
//创建一个长度为n的切片
s := make([]int, n)
InitData(s)//初始化数组
fmt.Println("排序前:", s)
BubbleSort(s)//冒泡排序
fmt.Println("排序后:", s)
}
9.猜数字游戏
func CreateNum(p *int) {//实参传递
rand.Seed(time.Now().UnixNano())
var num int
for {
num = rand.Intn(10000)
if num > 1000 {
*p = num
break
}
}
}
func GetNum(randNum int, randSlice []int) {
for i := 3; i >= 0; i-- {
randSlice[i] = randNum % 10
randNum /= 10
}
}
func OnGame(randSlice []int) {
var num int
numSlice := make([]int, 4)
for {
fmt.Printf("请输入一个4位数:")
fmt.Scan(&num)
if num < 1000 || num > 9999 {
fmt.Printf("输入不符合要求")
continue
} else {
GetNum(num, numSlice)
}
fmt.Println("numSlice=", numSlice)
n := 0
for i := 0; i < 4; i++ {
if randSlice[i] < numSlice[i] {
fmt.Printf("第%d位大了一点", i+1)
} else if randSlice[i] > numSlice[i] {
fmt.Printf("第%d位小了一点", i+1)
} else {
fmt.Printf("第%d位猜对了", i+1)
n++
}
}
if n == 4 {
fmt.Println("全部猜对")
break
}
}
}
func main() {
var randNum int
CreateNum(&randNum)//产生一个4位随机数
fmt.Println("randNum=", randNum)
randSlice := make([]int, 4)
GetNum(randNum, randSlice)//对4位数进行切片存储
fmt.Println("randSlice=", randSlice)
OnGame(randSlice)//猜数
}
五、map
1.map的基本使用
//定义一个变量,类型为map[int]string
var m1 map[int]string
fmt.Println("m1=", m1)
//map只有len,没有cap,可以通过make创建
m2 := make(map[int]string)
fmt.Println("m2=", m2)
fmt.Println("len(m2)=", len(m2))
//可以通过make,指定长度,只是指定了容量,但是里面确实一个数据也没有
m3 := make(map[int]string, 10)
m3[1] = "mike"
m3[2] = "go"
fmt.Println("m3=", m3)
//map是无序的,无法决定它的返回顺序
fmt.Printf("len(m3)=%d\n", len(m3))//2
//键值是唯一的
m4 := map[int]string{1: "mike", 2: "go", 3: "c"}
fmt.Println("m4=", m4)
2.map的赋值
m1 := map[int]string{1: "mike", 2: "yoyo"}
fmt.Println("m1=", m1)
m1[1] = "c++" //赋值
fmt.Println("m1=", m1)
m1[3] = "go" //追加,map底层会扩容,和append类似
fmt.Println("m1=", m1)
3.map的遍历
m := map[int]string{1: "mike", 2: "yoyo", 3: "go"}
//第一个返回值为key,第二个为value,遍历结果无序
for key, value := range m {
fmt.Printf("%d----->%s\n", key, value)
}
//如何判断一个key值是否存在
//第一个返回值为key所对应的value,第二个返回值为key是否存在的条件
//存在ok为true,否则为false
value, ok := m[0]
if ok == true {
fmt.Println("m[1]=", value)
} else {
fmt.Println("key不存在")
}
4.map的删除
m := map[int]string{1: "mike", 2: "yoyo", 3: "go"}
fmt.Println("m=", m)
delete(m, 1)//删除key为1的内容
fmt.Println("m=", m)
5.map做函数参数
//实参引用
func test(m map[int]string) {
delete(m, 1)
}
func main() {
m := map[int]string{1: "mike", 2: "yoyo", 3: "go"}
fmt.Println("m=", m)
test(m)
fmt.Println("m=", m)
}
六、结构体
1.结构体初始化
//定义一个结构体
type Student struct {
id int
name string
sex byte
age int
addr string
}
func main() {
//顺序初始化,每个成员必须初始化
var s1 Student = Student{1, "mike", 'm', 18, "bj"}
fmt.Println("s1=", s1)
//指定成员初始化,没有初始化的成员,自动赋值为0
s2 := Student{name: "mike", addr: "bj"}
fmt.Println("s2=", s2)
}
2.结构体指针变量的初始化
type Student struct {
id int
name string
sex byte
age int
addr string
}
func main() {
var p1 *Student = &Student{1, "mike", 'm', 18, "bj"}
fmt.Println("*p1=", *p1)
p2 := &Student{name: "mike", addr: "bj"}
fmt.Printf("p2 type is %T\n", p2)//*main.Student
fmt.Println("p2=", p2)
}
3.结构体成员的使用:普通成员
var s Student
//操作成员需要.运算符
s.id = 1
s.name = "mike"
s.sex = 'm'//字符
s.age = 18
s.addr = "bj"
fmt.Println("s=", s)
4.结构体成员的使用:指针变量
//1.指针有合法指向后,才操作成员
//先定义一个普通结构体变量
var s Student
//在定义一个指针变量,保存s的地址
var p1 *Student
p1 = &s
//通过指针操作成员,p1.id和(*p1).id完全等价,只能同.运算符
p1.id = 1
p1.name = "mike"
p1.sex = 'm'//字符
p1.age = 18
p1.addr = "bj"
fmt.Println("p1=", p1)
//2.通过new申请一个结构体
p2 := new(Student)
p2.id = 1
p2.name = "mike"
p2.sex = 'm'
p2.age = 18
p2.addr = "bj"
fmt.Println("p2=", p2)
5.结构体比较和赋值
var s1 Student = Student{1, "mike", 'm', 18, "bj"}
s2 := Student{1, "mike", 'm', 18, "bj"}
s3 := Student{2, "mike", 'm', 18, "bj"}
fmt.Println("s1==s2", s1 == s2)
fmt.Println("s1==s3", s1 == s3)
//同类型的2个结构体变量可以相互赋值
var tmp Student
tmp = s3
fmt.Println("tmp=", tmp)
6.结构体做函数参数
func test01(s Student) {
s.id = 666
fmt.Println("s=", s)
}
func main() {
s := Student{1, "mke", 'm', 18, "bj"}
test01(s)//值传递,形参无法改变实参
fmt.Println("main", s)
}
func test02(s *Student) {
s.id = 666
fmt.Println("test:", s)
}
func main() {
s := Student{1, "mike", 'm', 18, "bj"}
test02(&s)
fmt.Println("main", s)
}
七、go语言可见性规则
如果想使用别的包的函数、结构体、结构体成员
函数名,类型名,结构体成员变量名,首字母必须大写
可见,如果首字母小写,只能在同一个包里使用