美文网首页
Go语言基础03——复合类型

Go语言基础03——复合类型

作者: Spring618 | 来源:发表于2019-03-15 18:34 被阅读0次

    复合类型的分类

    指针、数组、切片slice、字典map、结构体struct

    指针

    package main
    
    import "fmt"
    
    func main() {
    
        fmt.Println("指针使用演示案例")
    
        var a int
        a = 777
        fmt.Println("a = ", a)   //变量的内存
        fmt.Println("&a = ", &a) //变量的地址
    
        // 1. 指针的基本使用
        var p *int
        p = &a
        fmt.Printf("a的地址:%v,a的值:%v\n", p, *p) //a的地址:0xc00000a0c0,a的值:777
    
        *p = 666
        fmt.Println("a = ", a)
    
        // 2. 不要操作没有合法指向的内存
        p = nil
        // *p = 88 //err,panic: runtime error: invalid memory address or nil pointer dereference
    
        // 3. new函数的使用
        // 表达式new(T)将创建一个T类型的匿名变量,所做的是为T类型的新值分配并清零一块内存空间,
        //然后将这块内存空间的地址作为结果返回,而这个结果就是指向这个新的T类型值的指针值,返回的指针类型为*T。
        //我们只需使用new()函数,无需担心其内存的生命周期或怎样将其删除,因为Go语言的内存管理系统会帮我们打理一切。
        p = new(int)
        fmt.Printf("p的地址:%v,p的值:%v\n", p, *p) //p的地址:0xc00000a0e0,p的值:0
    
        // 4. 地址传递-交换两个数的值
        i, j := 3, 4
        fmt.Printf("i的值:%v,j的值:%v\n", i, j)
        swap(&i, &j)
        fmt.Printf("交换后,i的值:%v,j的值:%v\n", i, j)
    }
    func swap(a, b *int) {
        *a, *b = *b, *a
    }
    
    

    数组

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

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

    package main
    
    import "fmt"
    
    func main() {
    
        fmt.Println("数组使用演示案例")
    
        var arr [10]int
    
        for i := 0; i < len(arr); i++ {
            arr[i] = i * i
        }
        for _, data := range arr {
            fmt.Println(data)
        }
    
        // 1. 数组的初始化
        var n = 10
        //var arr1 [n]int //err, non-constant array bound n
    
        const n1 = 10
        var arr3 [n1]int //ok
    
        fmt.Println(n, arr3)
    
        var arr4 [5]int = [5]int{1, 2, 3, 4, 5} // 全部初始化
        fmt.Println("arr4 = ", arr4)
        var arr5 [5]int = [5]int{1, 2, 3} // 部分初始化
        fmt.Println("arr5 = ", arr5)
        var arr6 [5]int = [5]int{1: 77, 2: 88} //指定元素初始化
        fmt.Println("arr6 = ", arr6)
    
        // 2. 二维数组
    
        var aa [3][4]int
        for i := 0; i < 3; i++ {
            for j := 0; j < 4; j++ {
                aa[i][j] = i + j
            }
        }
        fmt.Println("aa = ", aa)
    
        bb := [3][4]int{{1, 2, 3, 4}, {4, 3, 2, 1}, {3, 3}}
        fmt.Println("bb = ", bb)
        // 多维数组的部分初始化
        cc := [3][4]int{1: {4, 3, 2, 1}}
        fmt.Println("cc = ", cc)
    
        // 3. 数组的比较和赋值
        //相同类型的数组之间可以使用 == 或 != 进行比较,但不可以使用 < 或 >,也可以相互赋值:
    
        var x1 [5]int = [5]int{1, 2, 3, 4, 5}
        var x2 [5]int = [5]int{1, 2, 3, 4, 5}
        var x3 [3]int = [3]int{1, 2, 3}
        fmt.Println("x1 == x2 ? ", x1 == x2) //err, invalid operation: x1 == x3 (mismatched types [5]int and [3]int)
        fmt.Println("x1 == x3 ? ", x1 == x3) //err, invalid operation: x1 == x3 (mismatched types [5]int and [3]int)
    
        xx1 := [5]int{1, 2, 3, 4, 5}
        xx2 := [5]int{1, 2, 3, 4, 5}
    
        fmt.Println("xx1 == xx2 ? ", xx1 == xx2) //true
    
    }
    
    

    随机数和冒泡排序

    package main
    
    import "fmt"
    import "time"
    import "math/rand"
    
    func main() {
    
        fmt.Println("随机数和冒泡排序使用演示案例")
        // 1. 随机数
        rand.Seed(time.Now().UnixNano()) //以当前系统时间作为种子参数
    
        for i := 0; i < 5; i++ {
            v := rand.Int()    // 很大的随机数
            v = rand.Intn(100) // 100以内的随机数
            fmt.Println("v = ", v)
        }
    
        // 2. 冒泡排序
    
        var arr [10]int
        // 随机数初始化数组
        for i := 0; i < 10; i++ {
            arr[i] = rand.Intn(100)
        }
        fmt.Println("排序前 arr:", arr)
        //冒泡排序
        for i := 0; i < len(arr)-1; 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]
                }
            }
        }
    
        fmt.Println("排序后 arr:", arr)
    }
    
    

    数组做函数参数和数组指针做函数参数

    package main
    
    import "fmt"
    
    func modify(arr [10]int) {
        arr[0] = 999
        fmt.Println("修改中 arr:", arr)
    }
    func modify2(p *[10]int) {
        (*p)[0] = 666 // 这里要注意一下
        fmt.Println("modify2 修改中 arr:", *p)
    }
    
    func main() {
    
        fmt.Println("随机数和冒泡排序使用演示案例")
        var arr [10]int
        for i := 0; i < 10; i++ {
            arr[i] = i
        }
        fmt.Println("修改前 arr:", arr)
        modify(arr)
        fmt.Println("修改后 arr:", arr)
    
        modify2(&arr)
        fmt.Println("modify2 修改后 arr:", arr)
    }
    
    

    切片

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

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

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

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

    package main
    
    import "fmt"
    
    func main() {
    
        fmt.Println("切片使用演示案例")
    
        var arr1 [5]int = [5]int{1, 2, 3, 4, 5} //这是数组的定义和初始化
        arr2 := [...]int{1, 2, 3, 4}            // 这也是数组的定义和初始化
        fmt.Printf("%T,%T\n", arr1, arr2)       //[5]int,[4]int
    
        a := []int{1, 2, 3, 4, 5}                           //这是切片
        s := a[0:3:5]                                       //这是切片
        fmt.Printf("%T,%T\n", a, s)                         //[]int,[]int
        fmt.Printf("cap(s):%d len(s):%d\n", cap(s), len(s)) // 5,3
    
        s1 := a[1:3:5]
        fmt.Printf("cap(s1):%d len(s1):%d\n", cap(s1), len(s1)) // 4,2
    
        // 1. 切片的创建:
        //slice和数组的区别:声明数组时,方括号内写明了数组的长度或使用...自动计算长度,而声明slice时,方括号内没有任何字符。
        // 1.1 自动推到类型,同时初始化
        ss := []int{1, 2, 3, 4} //
        fmt.Println("ss=", ss)  //ss= [1 2 3 4]
        // 1.2 借助make函数,格式:make(切片类型,长度,容量)
        ss2 := make([]int, 5, 8)
        fmt.Println("ss2=", ss2)
        // 1.3 没有指定容量,则容量=长度
        ss3 := make([]int, 6) //注意:make只能创建slice、map和channel,并且返回一个有初始值(非零)。
        fmt.Println("ss3=", ss3)
    
        var ss4 []int //声明切片和声明数组差不多,只是少了长度,此时为nil
        ss5 := []int{}
        fmt.Println(ss4, ss5) //[] []
    
        //2. 切片截取
    
        slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
        fmt.Println("slice :", slice)
        //[low:hign:max]  len = hign-low cap = max-high
        d1 := slice[:]
        fmt.Printf("d1 len:%d,cap:%d,v:%v \n", len(d1), cap(d1), d1) //len:9,cap:9,v:[1 2 3 4 5 6 7 8 9
    
        data := d1[3]
        fmt.Println("data ", data) //4,操作某个元素和操作数组方式一样
    
        d2 := slice[3:6:7]
        fmt.Printf("d2 len:%d,cap:%d,v:%v \n", len(d2), cap(d2), d2) //len:3,cap:4,v:[4 5 6]
    
        d3 := slice[3:]
        fmt.Printf("d3 len:%d,cap:%d,v:%v \n", len(d3), cap(d3), d3) //len:6,cap:6,v:[4 5 6 7 8 9]
    
        d4 := slice[:6]
        fmt.Printf("d4 len:%d,cap:%d,v:%v \n", len(d4), cap(d4), d4) //len:6,cap:9,v:[1 2 3 4 5 6]
    
        // 3. 切片和底层数组关系
        slice1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
        z1 := slice1[2:6]
        z1[1] = 666
        fmt.Println(z1, slice1) //[3 666 5 6] [1 2 3 666 5 6 7 8 9]
    
        z2 := z1[1:3]               //666 5
        z2[0] = 777                 //z2: 777 5  z1:3 777 5 6 sl:1 2 3 777 5 6 7 8 9
        fmt.Println(z1, z2, slice1) //[3 777 5 6] [777 5] [1 2 3 777 5 6 7 8 9]
    
        // 4. append函数的使用,append函数向 slice 尾部添加数据,返回新的 slice 对象
    
        z3 := []int{}
        fmt.Println(z3) //[]
        z3 = append(z3, 1)
        z3 = append(z3, 2) //[1 2]
        fmt.Println(z3)
        // 4.1 append函数会智能地底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据:
    
        z4 := make([]int, 0, 1)
        fmt.Printf("z4 len:%d,cap:%d,v:%v \n", len(z4), cap(z4), z4) //z4 len:0,cap:1,v:[]
    
        for i := 0; i < 50; i++ {
            z4 = append(z4, i)
            fmt.Printf("len:%d,cap:%d \n", len(z4), cap(z4))
        }
    
        // 5. copy 的使用,函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准,两个 slice 可指向同一底层数组。
    
        //该函数主要是切片(slice)的拷贝,不支持数组
        //将第二个slice里的元素拷贝到第一个slice里,拷贝的长度为两个slice中长度较小的长度值
        src := []int{1, 2, 3}
        dest := []int{66, 77, 88, 99}
        copy(src, dest)
        fmt.Println(src, dest) //[66 77 88] [66 77 88 99]
    
        src1 := []int{1, 2, 3}
        dest1 := []int{66, 77, 88, 99}
        copy(dest1, src1)
        fmt.Println(src1, dest1) //[1 2 3] [1 2 3 99]
    
        // 6. 切片作为函数参数:值传递
        slice2 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
        modify(slice2)
    
        fmt.Println(slice2) //[1 2 666 4 5 6 7 8 9]
    }
    func modify(s []int) {
        s[2] = 666
    }
    
    

    Map

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

    package main
    
    import "fmt"
    
    func main() {
    
        fmt.Println("Map使用演示案例")
    
        // 1. map的创建
    
        var m1 map[int]string // 只是声明一个map,没有初始化,此时为nil
        fmt.Println("m1  = ", m1)
        // m1[1] = "java" //err,panic: assignment to entry in nil map
    
        m2 := map[int]string{}
        m3 := make(map[int]string)
        m4 := make(map[int]string, 10) // 第二个参数为指定容量
    
        m2[1] = "java"
        m3[1] = "c++"
        m4[1] = "go"
    
        fmt.Println("m2  = ", m2)
        fmt.Println("m3  = ", m3)
        fmt.Println("m4  = ", m4)
    
        // 2.  初始化
    
        // 自动类型推到
        m5 := map[int]string{1: "java", 2: "c++", 3: "go"}
    
        // 定义的时候初始化
        var m6 = map[int]string{1: "go", 2: "java", 3: "c++"}
        fmt.Println("m5  = ", m5)
        fmt.Println("m6  = ", m6)
    
        // 3. map的赋值
        m7 := map[int]string{1: "java", 2: "c++", 3: "go"}
        m7[2] = "python" //修改
        m7[4] = "html"   //追加
        fmt.Println("m7  = ", m7)
        m8 := make(map[int]string)
        m8[1] = "java" //赋值
        fmt.Println("m8  = ", m8)
    
        // 4. map的遍历
        m9 := map[int]string{1: "java", 2: "c++", 3: "go"}
        for k, v := range m9 {
            fmt.Println("m9-->", k, v)
        }
        for k := range m9 { //第二个返回值value可以省略
            fmt.Println("m9-->", k)
        }
    
        // 判断某个key所对应的value是否存在,第一个返回值是value(如果存在的话)
        v, ok := m9[2]
        fmt.Println("m9-->", v, ok) //m9--> c++ true
        v1, ok1 := m9[4]
        fmt.Println("m9-->", v1, ok1) //m9-->  false
    
        // 5. map的删除
        m10 := map[int]string{1: "java", 2: "c++", 3: "go"}
        delete(m10, 2) //删除key值为2的map
        fmt.Println("m10  = ", m10)
        delete(m10, 12) //删除key值为12的map,不会报错
        fmt.Println("m10  = ", m10)
    
        // 6. map做函数参数为引用传递
        m11 := map[int]string{1: "java", 2: "c++", 3: "go"}
        change(m11)
        fmt.Println("m11  = ", m11) //C:\Users\Ivy\Desktop\code>
    }
    
    func change(m1 map[int]string) {
        m1[2] = "python"
    }
    
    

    结构体

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

    package main
    
    import "fmt"
    
    //定义一个结构体类型
    type Student struct {
        id   int
        name string
        age  int
        sex  byte
        addr string
    }
    
    func main() {
    
        fmt.Println("结构体使用演示案例")
    
        // 1. 结构体普通变量初始化
        // 顺序初始化,每个成员必须初始化
        // var s1 Student = Student{1, "zz", 12, 'm'}//err, too few values in Student literal
        var s1 Student = Student{1, "zz", 12, 'm', "bj"}
        fmt.Println("s1 = ", s1)
    
        // 指定成员初始化
        s2 := Student{id: 2, name: "ls"} //没有初始化的成员自动赋值为0
        fmt.Println("s2 = ", s2)         //s2 =  {2 ls 0 0 }
    
        // 2. 结构体指针变量初始化
    
        var p1 *Student = &Student{2, "jack", 18, 'm', "sh"}
        fmt.Println("p1 = ", p1)   //p1 =  &{2 jack 18 109 sh}
        fmt.Println("&p1 = ", &p1) //&p1 =  0xc000006030
        fmt.Println("*p1 = ", *p1) //*p1 =  {2 jack 18 109 sh}
    
        p2 := &Student{3, "mike", 19, 'm', "wh"}
        fmt.Println("p2 = ", p2)   //p2 =  &{3 mike 19 109 wh}
        fmt.Println("&p2 = ", &p2) //&p2 =  0xc000006038
        fmt.Println("*p2 = ", *p2) //*p2 =  {3 mike 19 109 wh}
    
        // 3. 结构成员的使用:普通变量
        var s3 Student
        s3.id = 3                //成员操作使用.运算符。定义完就可以使用了,这个比较厉害
        fmt.Println("s3 = ", s3) //{3  0 0 }
    
        // 4.  结构体成员的使用:指针变量
    
        var s4 Student
        var s5 *Student
        s5 = &s4
        s5.id = 5
        s5.name = "cal"
        (*s5).addr = "sh"          // 通过指针操作成员 s5.addr 和(*s5).addr完全等价
        fmt.Println("*s5 = ", *s5) //*s5 =  {5 cal 0 0 sh}
    
        // 使用new申请一个结构体,先分配空间,在赋值
    
        s6 := new(Student)
        s6.name = "vi"
        fmt.Println("s6 = ", s6)   //s6 =  &{0 vi 0 0 }
        fmt.Println("*s6 = ", *s6) //*s6 =  {0 vi 0 0 }
    
        // 5.结构体比较和赋值
        //如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,
        //那样的话两个结构体将可以使用 == 或 != 运算符进行比较,但不支持 > 或 < 。
    
        s7 := Student{1, "zk", 'm', 18, "bj"}
        s8 := Student{1, "zk", 'm', 18, "bj"}
        s9 := Student{1, "zz", 'm', 18, "bj"}
        fmt.Println("s7 = s8", s7 == s8) //true
        fmt.Println("s7 = s9", s7 == s9) //false
    
        // 6. 结构体做函数参数:值传递
        s10 := Student{1, "zk", 18, 'm', "bj"}
        fmt.Println("before s10 = ", s10) //{1 zk 18 109 bj}
        change(s10)
        fmt.Println("after s10 = ", s10) //{1 zk 18 109 bj}
    
        changePoint(&s10)
        fmt.Println("after point s10 = ", s10) // {666 zk 18 109 bj}
    }
    
    func change(s Student) {
        s.id = 666
        fmt.Println("ing s = ", s)
    }
    
    func changePoint(s *Student) {
        s.id = 666
        fmt.Println("ing s = ", s)
    }
    
    

    go语言可见性规则验证

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

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

    如果想使用别的包的函数、结构体类型、结构体成员
    函数名,类型名,结构体成员变量名,首字母必须大写,可见
    如果首字母是小写,只能在同一个包里使用

    END.

    相关文章

      网友评论

          本文标题:Go语言基础03——复合类型

          本文链接:https://www.haomeiwen.com/subject/oyyrmqtx.html