Go基础(结构体)

Tutorial: Go初级 Category: Go Published: 2026-04-07 13:58:26 Views: 21 Likes: 0 Comments: 0
package main

import (
    "encoding/json"
    "fmt"
    "unsafe"
)

// 1. 自定义变量类型
type newInt int
type myInt = int

// 2. 定义结构体
type person struct {
    name, city string
    age        int8
}

type student struct {
    name string
    age  int
}

type test struct {
    a int8
    b int8
    c int8
    d int8
}

func f1() {
    var a newInt
    var b myInt
    fmt.Printf("type a: %T\n", a) // type a: main.newInt
    fmt.Printf("type b: %T\n", b) // type b: int
}

func f2() {
    var p1 person
    p1.name = "张三"
    p1.city = "西安"
    p1.age = 16
    fmt.Printf("p1=%v\n", p1)  // p1={张三 西安 16}
    fmt.Printf("p1=%#v\n", p1) // p1=main.person{name:"张三", city:"西安", age:16}
}

// 3. 匿名结构体
func f3() {
    var s1 struct {
        name string
        age  int8
    }
    s1.name = "李四"
    s1.age = 16

    fmt.Printf("s1=%v\n", s1)  // s1={李四 16}
    fmt.Printf("s1=%#v\n", s1) // s1=struct { name string; age int8 }{name:"李四", age:16}
}

// 4. 指针类型结构体
func f4() {
    var p1 = new(person)       // 使用new关键字对结构体进行实例化,得到的是结构体的地址
    fmt.Printf("p1:%T\n", p1)  // p1:*main.person
    fmt.Printf("p1:%#v\n", p1) // p1:&main.person{name:"", city:"", age:0}
    p1.name = "小王子"
    p1.age = 28
    p1.city = "上海"             // 赋值 注意有语法糖 底层是 (*p1).city
    fmt.Printf("p1:%#v\n", p1) // p1:&main.person{name:"小王子", city:"上海", age:28}

    p2 := &person{}             // 使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。
    fmt.Printf("p2: %T\n", p2)  // p2: *main.person
    fmt.Printf("p2: %#v\n", p2) // p2: &main.person{name:"", city:"", age:0}

}

// 5. 结构体初始化
func f5() {
    // 没有初始化的结构体,其成员变量都是对应其类型的零值
    var p1 person
    fmt.Printf("p1:%T\n", p1)  // p1:main.person
    fmt.Printf("p1:%#v\n", p1) // p1:main.person{name:"", city:"", age:0}

    // 使用键值对初始化 可部分初始化,一部分默认零值
    p2 := person{
        name: "王五",
        age:  17,
    }
    fmt.Printf("p2:%#v\n", p2) // p2:main.person{name:"王五", city:"", age:17}

    // 结构体指针键值对初始化
    p3 := &person{
        name: "赵六",
    }
    fmt.Printf("p3:%#v\n", p3) // p3:&main.person{name:"赵六", city:"", age:0}

    // 使用值列表初始化 字段、顺序必须一一对应
    p4 := &person{
        "隔壁老王",
        "隔壁",
        35,
    }
    fmt.Printf("p4:%#v\n", p4) // p4:&main.person{name:"隔壁老王", city:"隔壁", age:35}

}

// 6. 结构体内存布局 结构体占用一块连续的内存。
func f6() {
    p := test{
        1, 2, 3, 4,
    }
    fmt.Printf("type of p: %T\n", p) // type of p: main.test
    fmt.Printf("val of p: %#v\n", p) // val of p: main.test{a:1, b:2, c:3, d:4}
    fmt.Printf("p.a: %v\n", &p.a)    // p.a: 0xc000014190
    fmt.Printf("p.b: %v\n", &p.b)    // p.a: 0xc000014191
    fmt.Printf("p.c: %p\n", &p.c)    // p.a: 0xc000014192
    fmt.Printf("p.d: %p\n", &p.d)    // p.a: 0xc000014193

    // 传值 与 传地址
    p1 := &test{
        5, 6, 7, 8,
    }
    fmt.Printf("type of p1: %T\n", p1) // type of p1: *main.test
    fmt.Printf("val of p1: %#v\n", p1) // val of p1: &main.test{a:5, b:6, c:7, d:8}
    fmt.Printf("p1.a: %#v\n", &p1.a)   // p1.a: (*int8)(0xc0000141c0)
    fmt.Printf("p1.b: %#v\n", &p1.b)   // p1.b: (*int8)(0xc0000141c1)
    fmt.Printf("p1.c: %#p\n", &p1.c)   // p1.c: c0000141c2
    fmt.Printf("p1.d: %#p\n", &p1.d)   // p1.d: c0000141c3   %p 前加 # 没用
}

// 7. 空结构体 不占空间
func f7() {
    var v struct{}
    var s = []int{1, 2, 3}
    fmt.Println(s)                // [1 2 3]
    fmt.Println(unsafe.Sizeof(v)) // 0
    fmt.Println(unsafe.Sizeof(s)) // 24
}

// 8. 面试题
func f8() {
    m := make(map[string]*student)
    stus := []student{
        {name: "小王子", age: 18},
        {name: "娜扎", age: 23},
        {name: "大王八", age: 9000},
    }

    fmt.Println(stus)      // [{小王子 18} {娜扎 23} {大王八 9000}]
    fmt.Printf("%#v\n", m) // map[string]*main.student{}
    for _, stu := range stus {
        fmt.Println(stu.name, &stu) // &{小王子 18   &{娜扎 23}  &{大王八 9000}
        fmt.Printf("%p\n", &stu)    // 0xc0001120c0  地址是不变的
        m[stu.name] = &stu
    }

    fmt.Println(m) // map[大王八:0xc0000040d8 娜扎:0xc0000040d8 小王子:0xc0000040d8]
    for k, v := range m {
        fmt.Println(k, "=>", v.name)
        // 小王子 => 大王八
        // 娜扎 => 大王八
        // 大王八 => 大王八

    }
}

// 9. 构造函数
func newPerson(name, city string, age int8) *person {
    return &person{
        name: name,
        city: city,
        age:  age,
    }
}

func f9() {
    var p1 = newPerson("张三", "西安", 16)
    fmt.Printf("%#v\n", p1) // &main.person{name:"张三", city:"西安", age:16}
}

// 10. 方法和接收者  方法属于特定的类型
func (p person) Dream() {
    fmt.Printf("%s的梦想是学号Go语言\n", p.name)
    p.age = 100                // 值类型的接收者,修改的是副本
    fmt.Printf("修改副本:%v\n", p) // 修改副本:{小明 西安 100}
}

func (p *person) setAge(newAge int8) {
    p.age = newAge // 方法类型的接收者,修改原值
}

func f10() {
    var p1 = newPerson("小明", "西安", 10)
    p1.Dream()
    fmt.Printf("Dream后:%v\n", p1) // Dream后:&{小明 西安 10}
    p1.setAge(11)
    fmt.Printf("setAge后:%v\n", p1) // setAge后:&{小明 西安 11}
}

// 11. 结构体匿名字段
type anonymousPerson struct {
    string
    int8
}

func f11() {
    p1 := anonymousPerson{
        "小王子",
        18,
    }
    fmt.Printf("%#v\n", p1) // main.anonymousPerson{string:"小王子", int8:18}
    // 匿名字段的说法并不代表没有字段名,而是默认会采用类型名作为字段名,
    // 结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
}

// 12. 嵌套结构体
type Address struct {
    Provice string
    City    string
}

type User struct {
    Name    string
    Age     int8
    Address Address
}

type User1 struct {
    Name    string
    Age     int8
    Address // 匿名字段
}

func f12() {
    p1 := User{
        Name: "隔壁老王",
        Age:  30,
        Address: Address{
            Provice: "陕西",
            City:    "西安",
        },
    }
    fmt.Println(p1)         // {隔壁老王 30 {陕西 西安}}
    fmt.Printf("%#v\n", p1) // main.User{Name:"隔壁老王", Age:30, Address:main.Address{Provice:"陕西", City:"西安"}}

    var p2 User1
    p2.Name = "小红"
    p2.Age = 28
    p2.Address.Provice = "陕西" // 匿名字段默认使用类型名作为字段名
    p2.City = "商洛"            // 匿名字段可以省略, 直接用 p2
    fmt.Println(p2)           // {小红 28 {陕西 西安}}
    fmt.Printf("%#v\n", p2)   // main.User1{Name:"小红", Age:28, Address:main.Address{Provice:"陕西", City:"商洛"}}
}

// 13. 结构体的继承
type Animal struct {
    name string
}

func (a *Animal) Move() {
    fmt.Printf("%s 会跑\n", a.name)
}

type Dog struct {
    feet    int
    *Animal // 通过结构体匿名字段实现继承 (字段,方法)
}

func (d *Dog) Bark() {
    // 自己的方法
    fmt.Printf("%s 会汪汪汪的叫\n", d.name)
}

func f13() {
    d := &Dog{
        feet: 4,
        Animal: &Animal{
            name: "吉娃娃",
        },
    }
    d.Move() // 吉娃娃 会跑
    d.Bark() // 吉娃娃 会汪汪汪的叫
}

// 14. 结构体与JSON序列化
type Student1 struct {
    ID   int
    Name string
}

type Class struct {
    Name     string
    Students []*Student1
}

func f14() {
    cls := &Class{ // 班级初始化
        Name:     "三年级一班",
        Students: make([]*Student1, 0, 200), // 学生初始化
    }

    for i := 0; i < 3; i++ {
        stu := &Student1{
            ID:   i,
            Name: fmt.Sprintf("stu%02d", i),
        }
        cls.Students = append(cls.Students, stu)
    }

    // JSON序列化
    data, err := json.Marshal(cls)
    if err != nil {
        fmt.Println("json序列化失败")
        return
    }
    fmt.Printf("json: %s\n\n", data)
    /*
        json: {
            "Name":"三年级一班",
            "Students":[
                {"ID":0,"Name":"stu00"},
                {"ID":1,"Name":"stu01"},
                {"ID":2,"Name":"stu02"}
            ]
        }
    */

    // JSON 反序列化
    str := `{"Name":"三年级一班","Students":[{"ID":0,"Name":"stu00"},{"ID":1,"Name":"stu01"},{"ID":2,"Name":"stu02"}]}`
    cls1 := &Class{}
    err = json.Unmarshal([]byte(str), cls1)
    if err != nil {
        fmt.Println("jsonF反序列化失败")
        return
    }
    fmt.Printf("%#v\n", cls1)
    /*
        &main.Class{
            Name:"三年级一班",
            Students:[]*main.Student1{
                (*main.Student1)(0xc0000a61f8),
                (*main.Student1)(0xc0000a6210),
                (*main.Student1)(0xc0000a6228)
            }
        }
    */

}

// 15. 注意 因为slice和map这两种数据类型都包含了指向底层数据的指针,因此我们在需要复制它们时要特别注意
type Person2 struct {
    name   string
    age    int8
    dreams []string
}

// 错误的做法
func (p *Person2) SetDreams(dreams []string) {
    p.dreams = dreams
}

// 正确的做法
func (p *Person2) SetDreams1(dreams []string) {
    p.dreams = make([]string, len(dreams))
    copy(p.dreams, dreams)
}

func f15() {
    p1 := Person2{name: "小王子", age: 18}
    data := []string{"吃饭", "睡觉", "打豆豆"}
    p1.SetDreams(data) // p1.SetDreams1(data)

    // 你真的想要修改 p1.dreams 吗?
    data[1] = "不睡觉"
    fmt.Println(p1.dreams) // [吃饭 不睡觉 打豆豆] 并没有 setDreams,但 dreams 却改了
}

func main() {
    // f1()
    // f2()
    // f3()
    // f4()
    // f5()
    // f6()
    // f7()
    // f8()
    // f9()
    // f10()
    // f11()
    // f12()
    // f13()
    // f14()
    f15()
}