【Go语言】结构体

一、结构体创建、访问、修改

范例:

package main

import (
    "fmt"
    "time"
)

// 定义结构体 User,属性包含 id,socre,name,addr,tel,enrollment
type User struct {
    Id              int
    Score           float32
    enrollment      time.Time
    Name, Addr, Tel string // 多个相同类型的字段可以写在同一行
}

func struct_init() {
    // 声明引用结构体,会用相应类型的默认值初始化struct里的每一个字段
    var u User
    // 初始化结构体,相应类型的默认值初始化struct里的每一个字段
    u = User{}
    fmt.Printf("u.Id = %d, u.Score = %.1f, u.Name = %s,u.Addr = %s,u.Tel = %s,u.enrollment =%v\n", u.Id, u.Score, u.Name, u.Addr, u.Tel, u.enrollment)

    // 赋值初始化,未赋值的字段会用相应类型的默认值初始化字段
    u = User{Id: 1, Name: "Janzen", Addr: "ShenZhen"}
    fmt.Printf("u.Id = %d, u.Score = %.1f, u.Name = %s,u.Addr = %s,u.Tel = %s,u.enrollment =%v\n", u.Id, u.Score, u.Name, u.Addr, u.Tel, u.enrollment)

    // 赋值初始化,可以省略字段名,但需要跟结构体定义的字段顺序一致
    u = User{1, 95.0, time.Now(), "sophia", "ShangHai", "189xxxx****"}
    fmt.Printf("u.Id = %d, u.Score = %.1f, u.Name = %s,u.Addr = %s,u.Tel = %s,u.enrollment =%v\n", u.Id, u.Score, u.Name, u.Addr, u.Tel, u.enrollment)
}

func main() {
    struct_init()
}

 输出结果:

  

 

成员函数(方法)

范例:

// 定义结构体 User,属性包含 id,socre,name,addr,tel,enrollment
type User struct {
    Id              int
    Score           float32
    enrollment      time.Time
    Name, Addr, Tel string // 多个相同类型的字段可以写在同一行
}


// 定义一个结构体User的成员函数(方法), 等价于 func hello (u User, man string) string {}
func (u User) hello(man string) string {
    return "Hello student " + u.Name + ", I'm your teacher " + man
}

func hello(u User, man string) string {
    return "Hello student " + u.Name + ", I'm your teacher " + man
}

// 定义一个不需要调用结构体User里的成员参数的方法, 可以传匿名,也可以省略——
// 等价于 func (_ User) hi (man string) string {}
func (User) hi(man string) string {
    return "Hi, Nice to meet you " + man + "!"
}

func main() {

    stu := User{Name: "Janzen"}
    // 调用结构体方法
    fmt.Println(stu.hello("Qzc"), stu.hi("Qzc"))

    // 普通函数调用
    fmt.Println(hello(stu, "QiuZC"))
}

输出结果:

  

 

为任意类型添加方法

范例:

// 定义结构体 User,属性包含 id,socre,name,addr,tel,enrollment
type User struct {
    Id              int
    Score           float32
    enrollment      time.Time
    Name, Addr, Tel string // 多个相同类型的字段可以写在同一行
}

// 自定义类型,UserMap 本质上是个map
type UserMap map[int]User


// 为自定义的类型添加成员方法
func (um UserMap) GetUser(id int) User {
    return um[id]
}

func main() {
    stus := UserMap{
        1: User{1, 95.0, time.Now(), "sophia", "ShangHai", "189xxxx****"},
        2: User{2, 98.0, time.Now(), "janzen", "ShenZhen", "159xxxx****"},
    }
    fmt.Println(stus.GetUser(1))
    fmt.Println(stus.GetUser(2))
}

 

输出结果:

  

 

可见性

结构体名称以大写字母开头时,package 外部可见,在此前提下结构体中以大写开头的成员变量或成员函数在 package 外部也可见

 

匿名结构体

通常用于此结构体仅需一次使用的情况

范例:

func main() {
    // 声明 stu 是一个匿名结构体,通常用于此结构体仅需一次使用的情况
    var stu struct {
        Name string
        Age int
    }
stu.Name
= "Janzen" stu.Age = 18 fmt.Println(stu.Name,stu.Age) }

 

输出结果:

  

 

结构体中的匿名成员

匿名成员,直接使用数据类型作为字段名,因此匿名字段不能出现重复的数据类型

可以理解为 数据类型名称作为成员字段,成员调用同样使用数据类型名称

范例:

// 含有匿名成员的结构体
type Student struct {
    Id      int
    string  // 匿名字段,直接使用数据类型作为字段名,因此匿名字段不能出现重复的数据类型
    float32 // 匿名字段,直接使用数据类型作为字段名,因此匿名字段不能出现重复的数据类型
}

func main() {
    var stu = Student{Id: 1, string: "Janzen", float32: 98.2}
    fmt.Printf("Student Id=%d, name=%s, score=%.1f\n", stu.Id, stu.string, stu.float32)
}

 

输出结果:

  

 

二、结构体指针

& 代表目标进行取值,作用对象为数据类型的值,返回结果为 指针地址

* 代表指针解析,作用对象为 指针地址,返回结果为 指针对应的数据类型的值

创建结构体指针

范例:

// 定义结构体 User,属性包含 id,socre,name,addr,tel,enrollment
type User struct {
    Id              int
    Score           float32
    enrollment      time.Time
    Name, Addr, Tel string
}

func main() {
    // 通过取值符&得到指针,等价于 user := new(User)
    var u User
    user1 := &u
    fmt.Printf("user1 = %p\n", user1)

    // 通过new() 函数实体化一个结构体,并返回指针
    user2 := new(User)
    fmt.Printf("user2 = %p\n", user2)

    // 直接创建结构体指针
    user3 := &User{
        Id:         1,
        Score:      95.1,
        enrollment: time.Now(),
        Name:       "janzen",
        Addr:       "Sz",
        Tel:        "159xxxx****",
    }
    fmt.Printf("user3 = %p\n", user3)
}

 

输出结果:

  

 

构造函数

函数返回指针k可以避免值拷贝发生

范例:

type User struct {
    Id              int
    Score           float32
    enrollment      time.Time
    Name, Addr, Tel string
}


func NewUser(id int, score float32, enrollment time.Time, name, addr, tel string) *User {
    return &User{
        Id:         id + 1,
        Score:      score,
        enrollment: enrollment,
        Name:       name,
        Addr:       addr,
        Tel:        tel,
    }
}

func main() {
    u := NewUser(1, 98.0, time.Now(), "janzen", "ShenZhen", "159xxxx****")
    fmt.Println(u.Id)
}

 

输出结果:

  

 

方法接收指针

结构体方法接收指针

 范例:

// 定义结构体 User,属性包含 id,socre,name,addr,tel,enrollment
type User struct {
    Id              int
    Score           float32
    enrollment      time.Time
    Name, Addr, Tel string // 多个相同类型的字段可以写在同一行
}

// 定义成员方法,传入结构体指针,函数内部修改参数直接改变原结构体数据
func (u *User) modify_Name2(name string) {
    u.Name = name
    fmt.Printf("change2 name to %s\n", u.Name)
}

func main() {
    u := User{1, 95.0, time.Now(), "sophia", "ShangHai", "189xxxx****"}
    u.modify_Name1("janzen")
    fmt.Printf("u.Nmae = %s\n", u.Name)
    u.modify_Name2("Qzc")
    fmt.Printf("u.Nmae = %s\n", u.Name)
}

输出结果:

  

 普通函数接收指针

范例:

// 定义标准函数,传入结构体指针,函数内部修改参数直接改变原结构体数据
func modify_Name2(u *User, name string) {
    u.Name = name
    fmt.Printf("change2 name to %s\n", u.Name)
}

func main() {
    u := User{1, 95.0, time.Now(), "sophia", "ShangHai", "189xxxx****"}
    modify_Name1(u, "janzen")
    fmt.Printf("u.Nmae = %s\n", u.Name)
    modify_Name2(&u, "Qzc")
    fmt.Printf("u.Nmae = %s\n", u.Name)
}

 

输出结果:

  

 

三、结构体嵌套

定义嵌套结构体

范例:

type Persion struct {
    Nmae string
    sex  byte
}

type Group struct {
    Id     int
    Leader Persion //嵌套结构体
}

func main() {
    group := new(Group)
    group.Id = 10001  //结构体父级成员访问
    group.Leader.Nmae = "Qzc"  //嵌套结构体子成员访问
    group.Leader.sex = 1
    fmt.Println(group)
}

 

 

输出结果:

  

 

嵌套结构体字段名冲突

范例:

type Persion struct {
    Nmae string
    sex  byte
    Tel  string
}

type Group struct {
    Id     int
    Leader Persion //嵌套结构体
}

type Org struct {
    Nmae    string
    Persion // 嵌套匿名结构体
}

func main() {
    org := new(Org)
    org.Nmae = "CKL.Cot"            //结构体父成员访问
    org.Persion.Tel = "159xxxx****" //嵌套匿名结构体成员标准访问,中间字段为类型名称
    org.sex = 1                     //嵌套匿名结构体成员访问,非冲突字段,可以直接省略中间字段名
    org.Persion.Nmae = "Qzc"        //嵌套匿名结构体内外部成员Name产生冲突字段,必须指定中间字段名
    fmt.Println(org)
}

 

输出结果:

   

 

 

四、深拷贝与浅拷贝

 

  • 深拷贝,拷贝的是值,开辟了新的内存空间,修改操作不影响原先内存里的数据

  

  • 浅拷贝,拷贝的是指针,指向的还是原来的内存空间,修改操作直接作用在原内存空间上

  

 

范例:

 

func Deep_copy() {
    type user struct {
        Nmae string
        Age  int8
        Addr string
    }

    type Project struct {
        Name  string
        Owner user  //接收结构体
    }
    u := user{"janzen", 27, "ShenZhen"}
    // 此时 u 发生了值拷贝(深拷贝),因此修改p.Owner.Age 或者 u.Age 都不会影响另一个结构体内数据
    p := Project{"Test_project", u}
    fmt.Printf("u.Age(%p) = %d\tp.Owner.Age(%p) = %d\n", &u.Age, u.Age, &p.Owner.Age, p.Owner.Age)

    p.Owner.Age = 28
    fmt.Printf("u.Age = %d\tp.Owner.Age = %d\n", u.Age, p.Owner.Age)

    u.Age = 30
    fmt.Printf("u.Age = %d\tp.Owner.Age = %d\n", u.Age, p.Owner.Age)
}

func main() {
    Deep_copy()
}

 

输出结果:

  

 

范例:

func Shallow_copy() {
    type user struct {
        Nmae string
        Age  int8
        Addr string
    }

    type Project struct {
        Name  string
        Owner *user  // 接收结构体指针
    }
    u := user{"janzen", 27, "ShenZhen"}
    // 此时 u 进行了指针传递(浅拷贝),因此修改p.Owner.Age 或者 u.Age 会影响另一个结构体内数据
    p := Project{"Test_project", &u}
    fmt.Printf("u.Age(%p) = %d\tp.Owner.Age(%p) = %d\n", &u.Age, u.Age, &p.Owner.Age, p.Owner.Age)

    p.Owner.Age = 28
    fmt.Printf("u.Age = %d\tp.Owner.Age = %d\n", u.Age, p.Owner.Age)

    u.Age = 30
    fmt.Printf("u.Age = %d\tp.Owner.Age = %d\n", u.Age, p.Owner.Age)
}

func main() {
    Shallow_copy()
}

  

输出结果:

·  

 

 结构体切片 指针传参

当一个由结构体组成的切片作为参数传入函数时,由于 切片Slice 的值是由:底层数据值指针(array unsafe.Pointer)、len、cap 组成,因此对结构体切片内的结构体参数修改也会反映到原数组上

范例:

type user struct {
    Nmae string
    Age  int8
    Addr string
}

// 传入切片值
func update_age_slice_value(users []user) {
    users[0].Age += 1
}
// 传入切片指针
func update_age_slice_ptr(users *[]user) {
    (*users)[1].Age += 1
}
// 传入切片内的结构体指针
func update_age_struct_ptr(users []*user) {
    users[0].Age += 1
}

func main() {
    users := []user{user{"janzen", 27, "ShenZhen"}, user{"QZC", 15, "ShenZhen"}}
    update_age_slice_value(users)
    fmt.Printf("users[0].Age = %d\n", users[0].Age)
    update_age_slice_ptr(&users)
    fmt.Printf("users[1].Age = %d\n", users[1].Age)

    users2 := []*user{{"janzen", 20, "ShenZhen"}}
    fmt.Println(users2[0])
    update_age_struct_ptr(users2)
    fmt.Printf("users2[0].Age = %d\n", users2[0].Age)

}

 

输出结果:

   

 

posted @ 2023-07-30 17:56  Janzen_Q  阅读(15)  评论(0编辑  收藏  举报