【GO】复合数据类型

复合类型



前言

主要是介绍go语言复合数据类型,包括指针,切片


分类

在这里插入图片描述

指针

指针是一个代表着某个内存地址的值。这个内存地址往往是在内存中存储的另一个变量的值的起始位置。Go语言对指针的支持介于Java语言和C/C++语言之间,它既没有像Java语言那样取消了代码对指针的直接操作的能力,也避免了C/C++语言中由于对指针的滥用而造成的安全和可靠性问题

基本操作

Go语言虽然保留了指针,但与其它编程语言不同的是:
 默认值 nil,没有 NULL 常量
 操作符 “&” 取变量地址, “*” 通过指针访问目标对象
 不支持指针运算,不支持 “->” 运算符,直接⽤ “.” 访问目标成员

func main() {
    var a int = 10              //声明一个变量,同时初始化
    fmt.Printf("&a = %p\n", &a) //操作符 "&" 取变量地址

    var p *int = nil //声明一个变量p, 类型为 *int, 指针类型
    p = &a
    fmt.Printf("p = %p\n", p)
    fmt.Printf("a = %d, *p = %d\n", a, *p)

    *p = 111 //*p操作指针所指向的内存,即为a
    fmt.Printf("a = %d, *p = %d\n", a, *p)
}

注意不要操作没有合法指向的内存,即不能给野指针赋值

package main //必须有个main包

import "fmt"

func main() {
	var p *int
	p = nil
	fmt.Println("p = ", p)

	//*p = 666 //err, 因为p没有合法指向

	var a int
	p = &a //p指向a
	*p = 666
	fmt.Println("a = ", a)

}

变量内存和变量地址的区别
在这里插入图片描述
指针操作的原理
在这里插入图片描述

new函数

表达式new(T)将创建一个T类型的匿名变量,所做的是为T类型的新值分配并清零一块内存空间,然后将这块内存空间的地址作为结果返回,而这个结果就是指向这个新的T类型值的指针值,返回的指针类型为*T。

func main() {
    var p1 *int
    p1 = new(int)              //p1为*int 类型, 指向匿名的int变量
    fmt.Println("*p1 = ", *p1) //*p1 =  0

    p2 := new(int) //p2为*int 类型, 指向匿名的int变量
    *p2 = 111
    fmt.Println("*p2 = ", *p2) //*p1 =  111
}
package main //必须有个main包

import "fmt"

func main() {
	//a := 10 //整型变量a

	var p *int
	//指向一个合法内存
	//p = &a

	//p是*int, 指向int类型
	p = new(int)

	*p = 666
	fmt.Println("*p = ", *p)

	q := new(int) //自动推导类型
	*q = 777
	fmt.Println("*q = ", *q)

}

我们只需使用new()函数,无需担心其内存的生命周期或怎样将其删除,因为Go语言的内存管理系统会帮我们打理一切。

在这里插入图片描述

指针做函数参数

func swap01(a, b int) {
    a, b = b, a
    fmt.Printf("swap01 a = %d, b = %d\n", a, b)
}

func swap02(x, y *int) {
    *x, *y = *y, *x
}

func main() {
    a := 10
    b := 20

    //swap01(a, b) //值传递
    swap02(&a, &b) //变量地址传递
    fmt.Printf("a = %d, b = %d\n", a, b)
}

对比一下,普通变量做函数参数

package main //必须有个main包

import "fmt"

func swap(a, b int) {
	a, b = b, a
	fmt.Printf("swap: a = %d, b = %d\n", a, b)
}

func main() {
	a, b := 10, 20

	//通过一个函数交换a和b的内容
	swap(a, b) //变量本身传递,值传递(站在变量角度)
	fmt.Printf("main: a = %d, b = %d\n", a, b)
}

数组

概述

数组是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度。

数组⻓度必须是常量,且是类型的组成部分。 [2]int 和 [3]int 是不同类型

    var n int = 10
    var a [n]int  //err, non-constant array bound n
    var b [10]int //ok

数组的基本使用

package main //必须有个main包

import "fmt"

func main() {
	//定义一个数组, [10]int和[5]int是不同类型
	//[数字], 这个数字作为数组元素个数
	var a [10]int
	var b [5]int

	fmt.Printf("len(a) = %d, len(b) = %d\n", len(a), len(b))

	//注意:定义数组是,指定的数组元素个数必须是常量
	//n := 10
	//var c [n]int //err non-constant array bound n

	//操作数组元素,从0开始,到len()-1, 不对称元素,这个数字,叫下标
	//这是下标,可以是变量或常量
	a[0] = 1
	i := 1
	a[i] = 2 // a[1] = 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)
	}

}

操作数组

数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。

    var a [10]int
    for i := 0; i < 10; i++ {
        a[i] = i + 1
        fmt.Printf("a[%d] = %d\n", i, a[i])
    }

    //range具有两个返回值,第一个返回值是元素的数组下标,第二个返回值是元素的值
    for i, v := range a {
        fmt.Println("a[", i, "]=", v)
    }

内置函数 len(长度) 和 cap(容量) 都返回数组⻓度 (元素数量):

    a := [10]int{}
    fmt.Println(len(a), cap(a))//10 10

初始化:

   a := [3]int{1, 2}           // 未初始化元素值为 0
    b := [...]int{1, 2, 3}      // 通过初始化值确定数组长度
    c := [5]int{2: 100, 4: 200} // 通过索引号初始化元素,未初始化元素值为 0
    fmt.Println(a, b, c)        //[1 2 0] [1 2 3] [0 0 100 0 200]

    //支持多维数组
    d := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
    e := [...][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} //第二维不能写"..."
    f := [4][2]int{1: {20, 21}, 3: {40, 41}}
    g := [4][2]int{1: {0: 20}, 3: {1: 41}}
    fmt.Println(d, e, f, g)
package main //必须有个main包

import "fmt"

func main() {
	//声明定义同时赋值,叫初始化
	//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)

}

package main //必须有个main包

import "fmt"

func main() {
	//有多少个[]就是多少维
	//有多少个[]就用多少个循环
	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.Printf("\n")
	}

	fmt.Println("a = ", a)

	//有3个元素,每个元素又是一维数组[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)
}

相同类型的数组之间可以使用 == 或 != 进行比较,但不可以使用 < 或 >,也可以相互赋值:

    a := [3]int{1, 2, 3}
    b := [3]int{1, 2, 3}
    c := [3]int{1, 2}
    fmt.Println(a == b, b == c) //true false

    var d [3]int
    d = a
    fmt.Println(d) //[1 2 3]

在函数间传递数组

根据内存和性能来看,在函数间传递数组是一个开销很大的操作。在函数之间传递变量时,总是以值的方式传递的。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复制,并传递给函数。

func modify(array [5]int) {
    array[0] = 10 // 试图修改数组的第一个元素
    //In modify(), array values: [10 2 3 4 5]
    fmt.Println("In modify(), array values:", array)
}

func main() {
    array := [5]int{1, 2, 3, 4, 5} // 定义并初始化一个数组
    modify(array)                  // 传递给一个函数,并试图在函数体内修改这个数组内容
    //In main(), array values: [1 2 3 4 5]
    fmt.Println("In main(), array values:", array)
}

数组指针做函数参数:
如果用数组指针做参数,在传参的函数调用返回后,也会修改原始数组里面的值

func modify(array *[5]int) {
    (*array)[0] = 10
    //In modify(), array values: [10 2 3 4 5]
    fmt.Println("In modify(), array values:", *array)
}

func main() {
    array := [5]int{1, 2, 3, 4, 5} // 定义并初始化一个数组
    modify(&array)                 // 数组指针
    //In main(), array values: [10 2 3 4 5]
    fmt.Println("In main(), array values:", array)
}

切片

切片概述

数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。显然这种数据结构无法完全满足开发者的真实需求。Go语言提供了数组切片(slice)来弥补数组的不足。

切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。

slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。

在这里插入图片描述
注意数组的初始化方式array := [...]int{10,40,30}根据值来进行初始化

切片的创建和初始化

slice和数组的区别:声明数组时,方括号内写明了数组的长度或使用…自动计算长度,而声明slice时,方括号内没有任何字符。

var s1 []int //声明切片和声明array一样,只是少了长度,此为空(nil)切片
    s2 := []int{}

    //make([]T, length, capacity) //capacity省略,则和length的值相同
    var s3 []int = make([]int, 0)
    s4 := make([]int, 0, 0)    

    s5 := []int{1, 2, 3} //创建切片并初始化

注意:make只能创建slice、map和channel,并且返回一个有初始值(非零)。

Go的new和make分配内存的区别

1.new(T)创建一个没有任何数据的类型为T的实例,并返回该实例的指针;
2.make(T, args)只能创建 slice、map和channel,并且返回一个有初始值args(非零)的T类型的实例,非指针。

var p *[]int = new([]int)   // 分配slice结构内存 : *p = nil
var v []int = make([]int, 3)  // 分配一个有 3 个整数的slice

打印结果是[0,0,0]

切片的操作

(1)切片截取
在这里插入图片描述
示例说明:
array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

在这里插入图片描述

package main //必须有个main包

import "fmt"

func main() {
	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[1]
	fmt.Println("data = ", data)

	s2 := array[3:6:7] //a[3], a[4], a[5]   len = 6-3=3    cap = 7-3=4
	fmt.Println("s2 = ", s2)
	fmt.Printf("len = %d, cap = %d\n", len(s2), cap(s2))

	s3 := array[:6] //从0开始,去6个元素,容量为10, 常用
	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))

}

(2)切片和底层数组关系

 s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

    s1 := s[2:5]       //[2 3 4]
    s1[2] = 100        //修改切片某个元素改变底层数组
    fmt.Println(s1, s) //[2 3 100] [0 1 2 3 100 5 6 7 8 9]

    s2 := s1[2:6] // 新切片依旧指向原底层数组 [100 5 6 7]
    s2[3] = 200
    fmt.Println(s2) //[100 5 6 200]

    fmt.Println(s) //[0 1 2 3 100 5 6 200 8 9]

(3)内建函数

append函数:向 slice 尾部添加数据,返回新的 slice 对象:

var s1 []int //创建nil切换
//s1 := make([]int, 0)
s1 = append(s1, 1)       //追加1个元素
s1 = append(s1, 2, 3)    //追加2个元素
s1 = append(s1, 4, 5, 6) //追加3个元素
fmt.Println(s1)          //[1 2 3 4 5 6]

s2 := make([]int, 5)
s2 = append(s2, 6)
fmt.Println(s2) //[0 0 0 0 0 6]

s3 := []int{1, 2, 3}
s3 = append(s3, 4, 5)
fmt.Println(s3)//[1 2 3 4 5]

append函数会智能地底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据:

func main() {
    s := make([]int, 0, 1)
    c := cap(s)
    for i := 0; i < 50; i++ {
        s = append(s, i)
        if n := cap(s); n > c {
            fmt.Printf("cap: %d -> %d\n", c, n)
            c = n
        }
    }
    /*
        cap: 1 -> 2
        cap: 2 -> 4
        cap: 4 -> 8
        cap: 8 -> 16
        cap: 16 -> 32
        cap: 32 -> 64
    */
}

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

data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := data[8:]  //{8, 9}
s2 := data[:5] //{0, 1, 2, 3, 4}
copy(s2, s1)    // dst:s2, src:s1

fmt.Println(s2)   //[8 9 2 3 4]
fmt.Println(data) //[8 9 2 3 4 5 6 7 8 9]

切片做函数参数

func test(s []int) { //切片做函数参数
    s[0] = -1
    fmt.Println("test : ")
    for i, v := range s {
        fmt.Printf("s[%d]=%d, ", i, v)
        //s[0]=-1, s[1]=1, s[2]=2, s[3]=3, s[4]=4, s[5]=5, s[6]=6, s[7]=7, s[8]=8, s[9]=9,
    }
    fmt.Println("\n")
}

func main() {
    slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    test(slice)

    fmt.Println("main : ")
    for i, v := range slice {
        fmt.Printf("slice[%d]=%d, ", i, v)
        //slice[0]=-1, slice[1]=1, slice[2]=2, slice[3]=3, slice[4]=4, slice[5]=5, slice[6]=6, slice[7]=7, slice[8]=8, slice[9]=9,
    }
    fmt.Println("\n")
}

map

概述

Go语言中的map(映射、字典)是一种内置的数据结构,它是一个无序的key—value对的集合,比如以身份证号作为唯一键来标识一个人的信息。
在这里插入图片描述
map格式为:

map[keyType]valueType

在一个map里所有的键都是唯一的,而且必须是支持==和!=操作符的类型,切片、函数以及包含切片的结构类型这些类型由于具有引用语义,不能作为映射的键,使用这些类型会造成编译错误:

   dict := map[ []string ]int{} //err, invalid map key type []string

map值可以是任意类型,没有限制。map里所有键的数据类型必须是相同的,值也必须如何,但键和值的数据类型可以不相同。

注意:map是无序的,我们无法决定它的返回顺序,所以,每次打印结果的顺利有可能不同。

创建和初始化

(1)map的创建

var m1 map[int]string  //只是声明一个map,没有初始化, 此为空(nil)map
fmt.Println(m1 == nil) //true
//m1[1] = "mike" //err, panic: assignment to entry in nil map

//m2, m3的创建方法是等价的
m2 := map[int]string{}
m3 := make(map[int]string)
fmt.Println(m2, m3) //map[] map[]

m4 := make(map[int]string, 10) //第2个参数指定容量
fmt.Println(m4)                //map[]

(2)初始化

//1、定义同时初始化
var m1 map[int]string = map[int]string{1: "mike", 2: "yoyo"}
fmt.Println(m1) //map[1:mike 2:yoyo]

//2、自动推导类型 :=
m2 := map[int]string{1: "mike", 2: "yoyo"}
fmt.Println(m2)

常用操作

(1) 赋值

m1 := map[int]string{1: "mike", 2: "yoyo"}
m1[1] = "xxx"   //修改
m1[3] = "lily"  //追加, go底层会自动为map分配空间
fmt.Println(m1) //map[1:xxx 2:yoyo 3:lily]

m2 := make(map[int]string, 10) //创建map
m2[0] = "aaa"
m2[1] = "bbb"
fmt.Println(m2)           //map[0:aaa 1:bbb]
fmt.Println(m2[0], m2[1]) //aaa bbb

(2) 遍历

m1 := map[int]string{1: "mike", 2: "yoyo"}
//迭代遍历1,第一个返回值是key,第二个返回值是value
for k, v := range m1 {
    fmt.Printf("%d ----> %s\n", k, v)
    //1 ----> mike
    //2 ----> yoyo
}

//迭代遍历2,第一个返回值是key,第二个返回值是value(可省略)
for k := range m1 {
    fmt.Printf("%d ----> %s\n", k, m1[k])
    //1 ----> mike
    //2 ----> yoyo
}

//判断某个key所对应的value是否存在, 第一个返回值是value(如果存在的话)
value, ok := m1[1]
fmt.Println("value = ", value, ", ok = ", ok) //value =  mike , ok =  true

value2, ok2 := m1[3]
fmt.Println("value2 = ", value2, ", ok2 = ", ok2) //value2 =   , ok2 =  false

(3)删除

m1 := map[int]string{1: "mike", 2: "yoyo", 3: "lily"}
//迭代遍历1,第一个返回值是key,第二个返回值是value
for k, v := range m1 {
    fmt.Printf("%d ----> %s\n", k, v)
    //1 ----> mike
    //2 ----> yoyo
    //3 ----> lily
}

delete(m1, 2) //删除key值为3的map

for k, v := range m1 {
    fmt.Printf("%d ----> %s\n", k, v)
    //1 ----> mike
    //3 ----> lily
}

map做函数参数

在函数间传递映射并不会制造出该映射的一个副本,不是值传递,而是引用传递:

func DeleteMap(m map[int]string, key int) {
    delete(m, key) //删除key值为3的map

    for k, v := range m {
        fmt.Printf("len(m)=%d, %d ----> %s\n", len(m), k, v)
        //len(m)=2, 1 ----> mike
        //len(m)=2, 3 ----> lily
    }
}

func main() {
    m := map[int]string{1: "mike", 2: "yoyo", 3: "lily"}

    DeleteMap(m, 2) //删除key值为3的map

    for k, v := range m {
        fmt.Printf("len(m)=%d, %d ----> %s\n", len(m), k, v)
        //len(m)=2, 1 ----> mike
        //len(m)=2, 3 ----> lily
    }
}

结构体

结构体类型

有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性。显然单独定义以上变量比较繁琐,数据不便于管理。

在这里插入图片描述

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

结构体初始化

(1) 普通变量

type Student struct {
    id   int
    name string
    sex  byte
    age  int
    addr string
}

func main() {
    //1、顺序初始化,必须每个成员都初始化
    var s1 Student = Student{1, "mike", 'm', 18, "sz"}
    s2 := Student{2, "yoyo", 'f', 20, "sz"}
    //s3 := Student{2, "tom", 'm', 20} //err, too few values in struct initializer

    //2、指定初始化某个成员,没有初始化的成员为零值
    s4 := Student{id: 2, name: "lily"}
}

(2) 指针变量

type Student struct {
    id   int
    name string
    sex  byte
    age  int
    addr string
}

func main() {
    var s5 *Student = &Student{3, "xiaoming", 'm', 16, "bj"}
    s6 := &Student{4, "rocco", 'm', 3, "sh"}
}

结构体成员的使用

(1) 普通变量

 //===============结构体变量为普通变量
    //1、打印成员
    var s1 Student = Student{1, "mike", 'm', 18, "sz"}
    //结果:id = 1, name = mike, sex = m, age = 18, addr = sz
    fmt.Printf("id = %d, name = %s, sex = %c, age = %d, addr = %s\n", s1.id, s1.name, s1.sex, s1.age, s1.addr)

    //2、成员变量赋值
    var s2 Student
    s2.id = 2
    s2.name = "yoyo"
    s2.sex = 'f'
    s2.age = 16
    s2.addr = "guangzhou"
    fmt.Println(s2) //{2 yoyo 102 16 guangzhou}

(2)指针变量

  //===============结构体变量为指针变量
    //3、先分配空间,再赋值
    s3 := new(Student)
    s3.id = 3
    s3.name = "xxx"
    fmt.Println(s3) //&{3 xxx 0 0 }

    //4、普通变量和指针变量类型打印
    var s4 Student = Student{4, "yyy", 'm', 18, "sz"}
    fmt.Printf("s4 = %v, &s4 = %v\n", s4, &s4) //s4 = {4 yyy 109 18 sz}, &s4 = &{4 yyy 109 18 sz}

    var p *Student = &s4
    //p.成员 和(*p).成员 操作是等价的
    p.id = 5
    (*p).name = "zzz"
    fmt.Println(p, *p, s4) //&{5 zzz 109 18 sz} {5 zzz 109 18 sz} {5 zzz 109 18 sz}

结构体比较

如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用 == 或 != 运算符进行比较,但不支持 > 或 < 。

func main() {
    s1 := Student{1, "mike", 'm', 18, "sz"}
    s2 := Student{1, "mike", 'm', 18, "sz"}

    fmt.Println("s1 == s2", s1 == s2) //s1 == s2 true
    fmt.Println("s1 != s2", s1 != s2) //s1 != s2 false
}

结构体作为函数参数

(1) 值传递

func printStudentValue(tmp Student) {
    tmp.id = 250
    //printStudentValue tmp =  {250 mike 109 18 sz}
    fmt.Println("printStudentValue tmp = ", tmp)
}

func main() {
var s Student = Student{1, "mike", 'm', 18, "sz"}

    printStudentValue(s)        //值传递,形参的修改不会影响到实参
    fmt.Println("main s = ", s) //main s =  {1 mike 109 18 sz}
}

(2)引用传递

func printStudentPointer(p *Student) {
    p.id = 250
    //printStudentPointer p =  &{250 mike 109 18 sz}
    fmt.Println("printStudentPointer p = ", p)
}

func main() {
    var s Student = Student{1, "mike", 'm', 18, "sz"}

    printStudentPointer(&s)     //引用(地址)传递,形参的修改会影响到实参
    fmt.Println("main s = ", s) //main s =  {250 mike 109 18 sz}
}

可见性

Go语言对关键字的增加非常吝啬,其中没有private、 protected、 public这样的关键字。

要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母
开头。

目录结构:

在这里插入图片描述

test.go示例代码如下:

//test.go
package test

//student01只能在本文件件引用,因为首字母小写
type student01 struct {
    Id   int
    Name string
}

//Student02可以在任意文件引用,因为首字母大写
type Student02 struct {
    Id   int
    name string
}

main.go示例代码如下:

// main.go
package main

import (
    "fmt"
    "test" //导入test包
)

func main() {
    //s1 := test.student01{1, "mike"} //err, cannot refer to unexported name test.student01

    //err, implicit assignment of unexported field 'name' in test.Student02 literal
    //s2 := test.Student02{2, "yoyo"}
    //fmt.Println(s2)

    var s3 test.Student02 //声明变量
    s3.Id = 1             //ok
    //s3.name = "mike"  //err, s3.name undefined (cannot refer to unexported field or method name)
    fmt.Println(s3)
}

例子2

package test

import "fmt"

//如果首字母是小写,只能在同一个包里使用
type stu struct {
	id int
}

type Stu struct {
	//id int //如果首字母是小写,只能在同一个包里使用
	Id int
}

//如果首字母是小写,只能在同一个包里使用
func myFunc() {
	fmt.Println("this is myFunc")
}

func MyFunc() {
	fmt.Println("this is MyFunc -=======")
}

例子2 main函数

package main //必须有个main包

import "test"
import "fmt"

func main() {
	//包名.函数名
	test.MyFunc()

	//包名.结构体里类型名
	var s test.Stu
	s.Id = 666
	fmt.Println("s = ", s)
}

随机数的使用

package main //必须有个main包

import "fmt"
import "math/rand"
import "time"

func main() {
	//设置种子, 只需一次
	//如果种子参数一样,每次运行程序产生的随机数都一样
	rand.Seed(time.Now().UnixNano()) //以当前系统时间作为种子参数

	for i := 0; i < 5; i++ {

		//产生随机数
		//fmt.Println("rand = ", rand.Int()) //随机很大的数
		fmt.Println("rand = ", rand.Intn(100)) //限制在100内的数
	}

}

冒泡排序

package main //必须有个main包

import "fmt"
import "math/rand"
import "time"

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) //100以内的随机数
		fmt.Printf("%d, ", a[i])
	}
	fmt.Printf("\n")

	//冒泡排序,挨着的2个元素比较,升序(大于则交换)
	for i := 0; i < n-1; i++ {
		for j := 0; j < n-1-i; j++ {
			if a[j] > a[j+1] {
				a[j], a[j+1] = a[j+1], a[j]
			}
		}
	}

	fmt.Printf("\n排序后:\n")
	for i := 0; i < n; i++ {
		fmt.Printf("%d, ", a[i])
	}
	fmt.Printf("\n")

}

猜数字游戏

package main //必须有个main包

import "fmt"
import "math/rand"
import "time"

func CreatNum(p *int) {
	//设置种子
	rand.Seed(time.Now().UnixNano())

	var num int
	for {
		num = rand.Intn(10000) //一定是4位数
		if num >= 1000 {
			break
		}
	}

	//fmt.Println("num = ", num)

	*p = num

}

func GetNum(s []int, num int) {
	s[0] = num / 1000       //取千位
	s[1] = num % 1000 / 100 //取百位
	s[2] = num % 100 / 10   //取百位
	s[3] = num % 10         //取个位
}

func OnGame(randSlice []int) {
	var num int
	keySlice := make([]int, 4)

	for {
		for {
			fmt.Printf("请输入一个4位数:")
			fmt.Scan(&num)

			// 999 < num < 10000
			if 999 < num && num < 10000 {
				break
			}

			fmt.Println("请输入的数不符合要求")
		}
		//fmt.Println("num = ", num)
		GetNum(keySlice, num)
		//fmt.Println("keySlice = ", keySlice)

		n := 0
		for i := 0; i < 4; i++ {
			if keySlice[i] > randSlice[i] {
				fmt.Printf("第%d位大了一点\n", i+1)
			} else if keySlice[i] < randSlice[i] {
				fmt.Printf("第%d位小了一点\n", i+1)
			} else {
				fmt.Printf("第%d位猜对了\n", i+1)
				n++
			}
		}

		if n == 4 { //4位都猜对了
			fmt.Println("全部猜对!!!")
			break //跳出循环
		}
	}
}

func main() {
	var randNum int

	//产生一个4位的随机数
	CreatNum(&randNum)
	//fmt.Println("randNum: ", randNum)

	randSlice := make([]int, 4)
	//保存这个4位数的每一位
	GetNum(randSlice, randNum)
	//fmt.Println("randSlice = ", randSlice)

	/*
		n1 := 1234 / 1000 //取商
		//(1234 % 1000) //取余数,结果为234   234/100取商得到2
		n2 := 1234 % 1000 / 100
		fmt.Println("n1 = ", n1)
		fmt.Println("n2 = ", n2)
	*/

	OnGame(randSlice) //游戏

}

数组,切片,map,结构体的区别

(1)数组和结构体做函数参数,支持值传递和引用传递(指针)
(2)切片和map作函数参数是引用传递

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Bug 挖掘机

支持洋子

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

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

打赏作者

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

抵扣说明:

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

余额充值