复合类型分类
![](https://img.haomeiwen.com/i4207935/f3a09fb8d3780bfa.jpg)
指针
指针默认值为 nil
// 每个变量都有2层含义,变量的内存和变量的地址
var a int = 10
fmt.Printf( " a = %d \n", a) // 变量的内容 10
fmt.Printf(" &a = %v \n", &a) // 变量的地址 0xc042052058
// 保存某个变量的地址,需要指针类型 *int 保存int的地址 **int 保存 *int的地址
var b int = 10
var p *int
p = &b // 把b的地址值赋值给 p
*p = 22 // *p操作的不是p的内存,是p所指向的内存,就是b的值,因此b的值为22
// 不要操作没有合法指向的内存
var p *int
p = nil
fmt.Println("p = ", p)
*p = 666 //err,因为p没有合法指向
只有 var i int
p = &i
*p = 22 //才可以给i设置成22
// new 函数的使用
var p *int //声明一个指针类型的变量p
p = new (int) // p是指针类型,现在指向了一个没有名字的int地址
*p = 22 // new 出的那个int地址内容就成了22
// 值传递(和js一样,函数调用传递整型时是传递的副本)
func main () {
a,b := 1,2
swap(a, b)
fmt.Println("交换后:a = ",a,"b = ", b) // a =1,b=2
}
func swap(a, b int){
a, b = b, a // a =2,b=1
}
// 地址传递(指针做函数参数)
func main () {
a,b := 1,2
swap(&a, &b)
fmt.Println("交换后:a = ",a,"b = ", b) // a =2,b=1
}
func swap(p, t *int){
*p, *t = *t, *p
}
数组(同一个类型的集合)长度在定义后无法更改。数组是值类型
数组的定义
var arr [4] int // 定义一个长度为4的int数组
数组的初始化
var a [5]int = [5]int{ 1,2,3,4,5}
// 第二种推导式
b := [5]int{ 1,2,3,4,5 }
// 部分初始化,未初始化的自动赋值为0
c := [5]int{1,2,3} // [1,2,3,0,0]
// 指定元素的初始化
d := [5] int {2:10,4:20} // [0,0,10,0,20]
二维数组( 有多少[] 就多少维,就用多少个循环)
var a [3][4]int //定义
arr := [...] int {1,2,3,4,5} // 这种定义方式要赋值,系统自动 推断长度
赋值:
b := [3][4] int {{1,2,3,4},{5,6,7,8},{9,10,11,12}}
// 也可以部分赋值,和缺省赋值,和一维数组一样
数组支持比较和同类型赋值
// 只支持 == 或者!=,比较是不是每一个元素都一样,2个数组比较,数组类型要一样
a := [3]int{1,2,3}
b := [3]int{1,2,3}
c := [2]int{1,2}
a == b // true
a == c // false
// 同类型赋值(长度也要相同)
a := [3]int{1,2,3}
var b [4] int
b = a // 长度不一样,报错
数组做函数参数传参 ( arr1 [4]int, arr2 [5]int ) ,数组的长度不同类型也不一样,go中一般不直接使用数组,而是使用切片
数组在定义时长度就定了,无法更改,给函数传参时也要写长度,而且作为函数参数时候是值传递,
// 冒泡排序
func maopao (arr [5]int){
for i :=0; i<len(arr); i++{
for j :=0; j<len(arr) -i -1; j++ {
if arr[j] > arr[j+1]{
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
a := [5] int{1,22,33,2,1}
maopao(a)
//想要引用传递则要改写
func maopao (arr *[5]int){
for i :=0; i<len(arr); i++{
for j :=0; j<len(arr) -i -1; j++ {
if (*arr)[j] > (*arr)[j+1]{
(*arr)[j], (*arr)[j+1] = (*arr)[j+1], (*arr)[j]
}
}
}
}
a := [5] int{1,22,33,2,1}
maopao(&a)
切片(和数组很像,但不是数组,它通过内部指针和相关属性引用数组片段,以实现边长方案,函数传参时是引用传参)
// slice 并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,只是声明不需要长度
// slice 定义
slice2 := [] int {1,2,3,4,5} //第二种定义方式,不指定大小的数组
创建一个切片几种方式
// 第一种 切片[low, high,max] high - low = len(长度); max- low = cap(容量)
arr := [3]int {1,2,3}
arr1 := arr[1:3]
arr1[1] =0
fmt.Println(arr1)//[2 0]
fmt.Println(arr) //[1 2 0]
// 第二种:自动推导类型,同时初始化
s1 := [] int{1,2,3,4}
// 第三种 :借助make函数,格式make(切片类型, len, cap)
s2 := make([]int,3) // len = cap
切片和底层数组的关系(cap > len,否则报错)
a := []int {1,2,3,4,5,6,7,8,9}
a1 := a[1:3:5] // a1 = [2,3] len=2,cap = 4
a1[1] = 0 // a1 =[2,0] , a=[1,2,0,4,5,6,7,8,9]
a2 := a1[1:3:4] // a2 = [ 0,4]
切片的常用方法(内置函数):
// append内置函数
a := []int{} // len(a) = 0, cap(a) = 0
a = append(a,1) // 在原切片末尾添加元素添加的超过容量会扩容,是扩容为原来2倍
a = append(a,1) // [1,1]
// copy 内置函数
a := []int {1,2,3}
b := []int {4,5}
copy(a, b) // 把b按照顺序覆盖a
fmt.Println(a) // [4 5 3]
fmt.Println(b) // [4 5]
切片函数练习:猜数字游戏
package main
import (
"fmt"
"time"
"math/rand"
)
func main() {
randNum := getRandomNum()
userNum := getNum()
count := game(randNum,userNum)
for{
if count == 4 {
fmt.Println("全中,游戏退出")
break
}else {
userNum := getNum(1)
count = game(randNum,userNum)
}
}
}
// 获取随机数的函数
func getRandomNum() (num int){
rand.Seed(time.Now().UnixNano())
for{
num = rand.Intn(10000)
if num > 1000{
break
}
}
return
}
// 把int 的各个位上的数拆分成切片类型的数组
func chaifen( a int) (slice []int){
qian := a / 1000 // 8334
bai := a % 1000 / 100
shi := a % 100 / 10
ge := a % 10
slice = append(slice, qian)
slice = append(slice, bai)
slice = append(slice, shi)
slice = append(slice, ge)
// fmt.Println(slice)
return
}
// 获取用户输入的int 的函数
func getNum (arge ...int) (num int){
if len(arge) == 0 {
fmt.Println("请输入一个数")
}else {
fmt.Println("请重新输入一个数")
}
for{
fmt.Scan(&num)
if num >= 1000 && num <= 10000{
break
}
fmt.Println("请输入一个1000~10000之间的数")
}
return
}
// 逻辑判断每个slice的各位大小
func game(randNum, userNum int)(cal int){
slice := chaifen(randNum)
slice1 := chaifen(userNum)
//比较2个切片的是否一致
for i:=0; i<len(slice); i++{
if slice[i] == slice1[i]{
cal ++
fmt.Printf("在第 %d 位上中了\n" ,i)
}else if slice[i] > slice1[i]{
fmt.Printf("在第 %d 位上小了\n" ,i)
}else if slice[i] < slice1[i]{
fmt.Printf("在第 %d 位上大了\n" ,i)
}
}
return
}
随机数获取
import (
"math/rand"
"time"
)
func main (){
rand.Seed( time.Now().UnixNano()) // 需要以时间作为种子的参数,不然每次都是一样的随机数
a := rand.Int() // 返回一个很大的int
b := rand.Intn(100) // 返回100以内的int
}
map (字典、映射) 是一种内置的无序的数据结构
map格式为 : map[ keyType ] valueType ,此keyType值唯一,而且是必须支持 == 和 != 操作符的类型,切片、函数,以及包含切片的的结构类型这些类型由于具有引用语义不能作为映射的键,使用会出现编译错误
eg: map1 := map[ int ] string{
011: "aa",
022: "aab"
}
dict := map[ []string] int{} // 会报错
map的基本使用
创建1:
var m1 map[int] string
创建2:
m2 := make(map[int]string)
//创建3 :可以通过make创建,只能指定容量,但是里面一个数据也没有
m3 :=make(map[int] string ,2)
//添加元素:
m3[1] = "make"
m3[2] = "go"
// 键值是惟一的
m4 := map[int]string{1:"make",2:"go"}
// map赋值
m4 := map[int]string{1:"make",2:"go"}
m4[1] = "c++" // 修改了1 为 c++
m4[3] = "java" // 追加,map底层自动扩容,和append类似
// map的遍历
m := map[int] string{1:"java",2:"javascript"}
for key,value : = range m {
fmt.Printf("%d ==> %s",key,value)
}
判断一个key值是否存在
m := map[int] string{1:"java",2:"javascript"}
// 第一个返回值为key所对应的value,第二个返回值为key是否存在的条件,存在为true否则为false
value , ok := m[0]
if ok == true {
// 存在
}else { // 不存在 }
删除map的一个key值(delete()方法)
m := map[int] string{1:"java",2:"javascript"}
delete(m,1) //删除key为1 的内容
map做函数参数(引用传递)
m := map[int] string{1:"java",2:"javascript"}
func del(m map[int]string){ // 传递过来的是m的地址值}
结构体类型:(把不同类型的数据组合成一个有机的整体,便于数据管理,是一种聚合数据类型)
结构体类型体现:学生信息表示法
type Student struct {
id int
name string
sex byte
age int
addr string
}
// 初始化
var s1 Student = Student{1,"make",'m',18,"bj"} // 循序初始化,必须每个成员都初始化
s2 := Student{ name:"make",addr:"bj"} // 指定成员初始化,其它没赋值的有默认值
// 结构体指针变量初始化
var p1 *Student = &Student{ 1,"make",'m',18,"bj" }
或者 p2 := &Student{ id:1,name:"sese" }
结构体成员的使用(需要使用 . 运算符):
type Student struct {
id int
name string
sex byte
age int
addr string
}
var a Student
a.id =1
a.name = "makes"
// 指针变量操作:当指针有合法指向后才操作成员
var b Student
var p1 *Student
p1 = &b
通过指针操作成员, p1.id 和 (*p1).id 等价
p1.id =1
(*p1).name = "xxx"
//通过new函数申请类型
p2 := new (Student)
p2.id = 1
p2.name = "xxx" // p2 == &{1 xxx 0 0 }
p3 := Student{name:"sss"}
p3.id = 1
p3.name = "xxx" // p3 == {1 xxx 0 0 }
// 结构体的比较和赋值
同类型的2个结构体可以相互赋值,当成员相同时,结构体相等
结构体做函数参数:(是值传递,要引用传递--指针)
type Student struct{
name string
age int
sex byte
attr string
}
a :=Student{"java",12,'n',"beijing"}
Prins(a) // 此时函数无法修改a的成员
Prins(&a) // 此时函数可以修改,但是函数参数得改成 *Student
func Prins(p Student){
p.name = "aaa"
fmt.Printf("p.name = %s, p.age = %d, p.sex = %c, p.attr = %s\n",p.name,p.age,p.sex,p.attr)
}
go语言可见性规则
** 如果想使用别的包的函数、结构体类型、结构体成员、函数名、类型名、结构体成员变量名,首字母必须大写才可见**
** 如果首字母是小写,只能在同一个包里面使用 **
网友评论