【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) }
输出结果: