结构体创建、访问与修改
定义结构体
type user struct {
id int
score float32
enrollment time.Time
name, addr string //多个字段类型相同时可以简写到一行里
}
声明和初始化结构体
var u user //声明,会用相应类型的默认值初始化struct里的每一个字段
u = user{} //用相应类型的默认值初始化struct里的每一个字段
u = user{id: 3, name: "zcy"} //赋值初始化
u = user{4, 100.0, time.Now(), "zcy", "beijing"} //赋值初始化,可以不写字段名,但需要跟结构体定义里的字段顺序一致
//或者使用new,通过new()函数实体化一个结构体,并返回其指针
u:=new(user)
访问与修改结构体
u.enrollment = time.Now() //给结构体的成员变量赋值
fmt.Printf("id=%d, enrollment=%v, name=%s\n", u.id, u.enrollment, u.name)//访问结构体的成员变量
成员方法
给结构体添加方法
//可以把user理解为hello函数的参数,即hello(u user, man string)
func (u user) hello(man string) {
fmt.Println("hi " + man + ", my name is " + u.name)
}
//函数里不需要访问user的成员,可以传匿名,甚至 _ 也不传
func (_ user) think(man string) {
fmt.Println("hi " + man + ", do you know my name?")
}
为自定义类型添加方法
type UserMap map[int]User //自定义类型 定义了一个数据类型是map的结构体,map的值都是User类型的
//可以给自定义类型添加任意方法
func (um UserMap) GetUser(id int) User {
return um[id]
}
结构体的可见性
- go语言关于可见的统一规则:大写字母开头跨package也可以访问;否则只能本package内部访问。
- 结构体名称以大写开头时,package外部可见,在此前提下,结构体中以大写开头在成员变量或成员方法在package外部也可见。
匿名结构体
var stu struct { //声明stu是一个结构体,但这个结构体是匿名的
Name string
Addr string
}
stu.Name = "zcy"
stu.Addr = "bj"
匿名结构体通常用于只使用一次的情况。
结构体中含有匿名成员
type Student struct {
Id int
string //匿名字段
float32 //直接使用数据类型作为字段名,所以匿名字段中不能出现重复的数据类型
}
var stu = Student{Id: 1, string: "zcy", float32: 79.5}
fmt.Printf("anonymous_member string member=%s float member=%f\n", stu.string, stu.float32) //直接使用数据类型访问匿名成员
结构体指针
指针是在变量前加&
,就是取址
解析指针中的值就用*
创建结构体指针
var u User
user := &u //通过取址符&得到指针
user = &User{ //直接创建结构体指针
Id: 3, Name: "zcy", addr: "beijing",
}
user = new(User) //通过new()函数实体化一个结构体,并返回其指针
构造函数
//构造函数。返回指针是为了避免值拷贝
func NewUser(id int, name string) *User {
return &User{
Id: id,
Name: name,
addr: "China",
Score: 59,
}
}
方法接收指针
//user传的是值,即传的是整个结构体的拷贝。在函数里修改结构体不会影响原来的结构体
func hello(u user, man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
//传的是user指针,在函数里修改user的成员会影响原来的结构体
func hello2(u *user, man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
//把user理解为hello()的参数,即hello(u user, man string)
func (u user) hello(man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
//可以理解为hello2(u *user, man string)
func (u *user) hello2(man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
//例子
package main
import "fmt"
type User struct {
name string
string
int
}
//不传指针,那么传的就是值,只是个拷贝的值
func (u User) h() {
u.name = "qqqq"
}
//传指针,就是修改的原结构体 绝大多数时候用的都是传指针
func (u *User) h2() {
u.name = "wwwww"
}
func (u User) NewUser(name string, city string, age int) *User {
u.name = name
u.string = city
u.int = age
return &u
}
func main() {
user := User{"abc", "aaa", 12}
user.h()
fmt.Println(user.name) //abc
user.h2()
fmt.Println(user.name) //wwwww
user2 := User{"cccc", "dddd", 15}
fmt.Println(user2.name) //cccc
}
结构体嵌套
type user struct {
name string
sex byte
}
type paper struct {
name string
auther user //结构体嵌套
}
p := new(paper)
p.name = "论文标题"
p.auther.name = "作者姓名"
p.auther.sex = 0
type vedio struct {
length int
name string
user//匿名字段,可用数据类型当字段名
}
结构体嵌套时字段名冲突的问题
v := new(vedio)
v.length = 13
v.name = "视频名称"
v.user.sex = 0 //通过字段名逐级访问
v.sex = 0 //对于匿名字段也可以跳过中间字段名,直接访问内部的字段名
v.user.name = "作者姓名" //由于内部、外部结构体都有name这个字段,名字冲突了,所以需要指定中间字段名
深拷贝与浅拷贝
type User struct {
Name string
}
type Vedio struct {
Length int
Author User
}
Go语言里的赋值都会发生值拷贝。
type User struct {
Name string
}
type Vedio struct {
Length int
Author *User
}
- 深拷贝,拷贝的是值,比如Vedio.Length。
- 浅拷贝,拷贝的是指针,比如Vedio.Author。
- 深拷贝开辟了新的内存空间,修改操作不影响原先的内存。
- 浅拷贝指向的还是原来的内存空间,修改操作直接作用在原内存空间上。
传slice,对sclice的3个字段进行了拷贝,拷贝的是底层数组的指针,所以修改底层数组的元素会反应到原数组上。
users := []User{{Name: "康熙"}}
func update_users(users []User) {
users[0].Name = "光绪"
}
练习
需求:创建一个student结构体,包含姓名和语数外三门课的成绩。用一个slice容纳一个班的同学,求每位同学的平均分和整个班三门课的平均分,全班同学平均分低于60的有几位
package main
import "fmt"
//创建一个student结构体,包含姓名和语数外三门课的成绩。用一个slice容纳一个班的同学,求每位同学的平均分和整个班三门课的平均分,全班同学平均分低于60的有几位
type student struct {
name string
yunwen, shuxue, waiyu float32
junzhi float32
}
type class struct {
xueshen []*student
yunwen, shuxue, waiyu float32
bujige []*student
}
func (s *student) pinjunfen() {
s.junzhi = (s.yunwen + s.shuxue + s.waiyu) / 3.0
}
func (c *class) pinjun() {
var sumyuwen float32 = 0.0
var sumshuxue float32 = 0.0
var sumwaiyu float32 = 0.0
for _, s := range c.xueshen {
sumyuwen += s.yunwen
sumshuxue += s.shuxue
sumwaiyu += s.waiyu
s.pinjunfen()
if s.junzhi < 60 {
c.bujige = append(c.bujige, s)
}
}
c.yunwen = sumyuwen / float32(len(c.xueshen))
c.shuxue = sumshuxue / float32(len(c.xueshen))
c.waiyu = sumwaiyu / float32(len(c.xueshen))
}
func main() {
c := new(class)
s1 := student{
name: "s1",
yunwen: 12.5,
shuxue: 60.2,
waiyu: 44.3,
}
s2 := student{
name: "s2",
yunwen: 60,
shuxue: 60.2,
waiyu: 75,
}
s3 := student{
name: "s3",
yunwen: 50,
shuxue: 33.6,
waiyu: 25.3,
}
s4 := student{
name: "s4",
yunwen: 100,
shuxue: 82,
waiyu: 98,
}
s5 := student{
name: "s5",
yunwen: 88,
shuxue: 90,
waiyu: 60,
}
c.xueshen = []*student{&s1, &s2, &s3, &s4, &s5}
c.pinjun()
fmt.Printf("第四个学生的平均分%.2f 班级的语文平均分%.2f 班级的数学平均分%.2f 班级的外语平均分%.2f 平均分不及格的同学数%d\n", c.xueshen[3].junzhi, c.yunwen, c.shuxue, c.waiyu, len(c.bujige))
fmt.Printf("%+v", c)
}
遇到的问题:
之前定义的xueshen是student的切片
type class struct {
xueshen []student
yunwen, shuxue, waiyu float32
bujige []student
}
遇到的问题是,当打印平均分时,平均分显示0,表明在class
中的pingjun
方法中s.pinjunfen()
并没有修改到原数据,只是修改的一个拷贝。
所以将这里都修改成student指针的切片,这样就可以修改到原有的数据了。
在下面给c加入数据时,传的也是地址c.xueshen = []*student{&s1, &s2, &s3, &s4, &s5}
或者
修改成,在将student
加入到class
的xueshen
之前,就让每个student
自己执行一遍pingjunfen
,这样,数据也能保证正常修改
package main
import "fmt"
//创建一个student结构体,包含姓名和语数外三门课的成绩。用一个slice容纳一个班的同学,求每位同学的平均分和整个班三门课的平均分,全班同学平均分低于60的有几位
type student struct {
name string
yunwen, shuxue, waiyu float32
junzhi float32
}
type class struct {
xueshen []student
yunwen, shuxue, waiyu float32
bujige []student
}
func (s *student) pinjunfen() {
s.junzhi = (s.yunwen + s.shuxue + s.waiyu) / 3.0
}
func (c *class) pinjun() {
var sumyuwen float32 = 0.0
var sumshuxue float32 = 0.0
var sumwaiyu float32 = 0.0
for _, s := range c.xueshen {
sumyuwen += s.yunwen
sumshuxue += s.shuxue
sumwaiyu += s.waiyu
//不再让遍历的时候执行pingjunfen(),而是让student自己先做一遍
// s.pinjunfen()
if s.junzhi < 60 {
c.bujige = append(c.bujige, s)
}
}
c.yunwen = sumyuwen / float32(len(c.xueshen))
c.shuxue = sumshuxue / float32(len(c.xueshen))
c.waiyu = sumwaiyu / float32(len(c.xueshen))
}
func main() {
c := new(class)
s1 := student{
name: "s1",
yunwen: 12.5,
shuxue: 60.2,
waiyu: 44.3,
}
s2 := student{
name: "s2",
yunwen: 60,
shuxue: 60.2,
waiyu: 75,
}
s3 := student{
name: "s3",
yunwen: 50,
shuxue: 33.6,
waiyu: 25.3,
}
s4 := student{
name: "s4",
yunwen: 100,
shuxue: 82,
waiyu: 98,
}
s5 := student{
name: "s5",
yunwen: 88,
shuxue: 90,
waiyu: 60,
}
//让所有的student先自己做一遍pingjunfen(),再加到class的xueshen中
s1.pinjunfen()
s2.pinjunfen()
s3.pinjunfen()
s4.pinjunfen()
s5.pinjunfen()
c.xueshen = []student{s1, s2, s3, s4, s5}
c.pinjun()
fmt.Printf("第四个学生的平均分%.2f 班级的语文平均分%.2f 班级的数学平均分%.2f 班级的外语平均分%.2f 平均分不及格的同学数%d\n", c.xueshen[3].junzhi, c.yunwen, c.shuxue, c.waiyu, len(c.bujige))
fmt.Printf("%+v", c)
}
标签:语言,float32,waiyu,user,student,GO,结构,string,name
From: https://www.cnblogs.com/guangdelw/p/17766778.html