struct注意事项:
1.字段声明语法同变量,示例: 字段名 字段类型
2.字段的类型可以为:基本类型、数组或引用类型
3.在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),布尔类型是false,数值是0,字符串是""。
数组类型的默认值和它的元素类型相关,比如score[3]int 则为[0,0,0]
指针,slice,和map的零值都为nil,即还没有分配空间
type Person struct {
Name string
Age int
Score [5]float64
ptr *int
slice []int
map1 map[string]string
}
func main() {
var p1 Person
fmt.Println(p1)
fmt.Println()
if p1.ptr == nil {
fmt.Println("ok1")
}
if p1.slice == nil {
fmt.Println("ok2")
}
if p1.map1 == nil {
fmt.Println("ok3")
}
//使用 slice 一定要先make
p1.slice = make([]int, 10)
p1.slice[0] = 100
p1.map1 = make(map[string]string)
p1.map1["key1"] = "tom"
fmt.Println(p1)
}
结果为:
4.不同结构体变量的字段是独立,互不影响,一个结构体变量字段的改变,不影响另外一个。因为结构体是值类型,不是引用类型。
type Monster struct {
Name string
Age int
}
func main() {
var monster1 Monster
monster1.Name = "牛博文"
monster1.Age = 500
monster2 := monster1
monster2.Name = "芜湖"
fmt.Println("monster1 = ", monster1)
fmt.Println("monster2 = ", monster2)
}
结果:
monster2的改变并没有影响monster1。但如果想要monster2的改变影响monster1,那么应该把monster2变为指针:
monster2 := &monster1
monster2.Name = "芜湖"
fmt.Println("monster1 = ", monster1)
fmt.Println("monster2 = ", *monster2)
此时结果为:
5. 不能这样写:*p2.Name 会报错,因为.的运算符优先级比*高,应该:(*p2).Name
6. 结构体中所有字段在内存中是连续的
r1 := Rect{Point{1, 2}, Point{3, 4}}
fmt.Println(r1)
//r1有四个int,在内存中是连续分布的
//打印地址
fmt.Printf("r1.leftUp.x的地址是 %p\n", &r1.leftUp.x)
fmt.Printf("r1.leftUp.y的地址是 %p\n", &r1.leftUp.y)
fmt.Printf("r1.righttUp.x的地址是 %p\n", &r1.rightUp.x)
fmt.Printf("r1.rightUp.y的地址是 %p\n", &r1.rightUp.y)
fmt.Println()
//r2有两个 *Point类型,这两个*Point类型的本身地址是连续的,但是他们指向的地址不一定是连续的
r2 := Rect2{&Point{10, 20}, &Point{30, 40}}
//打印地址
fmt.Printf("r2.leftUp 本身的地址是%p\n", &r2.leftUp)
fmt.Printf("r2.rightUp 本身的地址是%p\n", &r2.rightUp)
fmt.Printf("r2.leftUp 指向的地址是%p\n", r2.leftUp)
fmt.Printf("r2.rightUp 指向的地址是%p\n", r2.rightUp)
fmt.Printf("r2.leftUp.x的地址是 %p\n", &r2.leftUp.x)
fmt.Printf("r2.leftUp.y的地址是 %p\n", &r2.leftUp.y)
fmt.Printf("r2.righttUp.x的地址是 %p\n", &r2.rightUp.x)
fmt.Printf("r2.rightUp.y的地址是 %p\n", &r2.rightUp.y)
7. 结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字,个数,类)
type A struct {
num int
str string
}
type B struct {
num int
str string
}
func main() {
var a A
var b B
a = A(b)
fmt.Println(a, b)
} //输出结果为 {0 } {0 }
8. 结构体进行type重新定义(相当于取别名), Golang认为是新的数据类型,但是相互之间可以强转
type Student struct {
Name string
Age int
}
type Stu Student
func main() {
var stu1 Student
var stu2 Stu
// stu2 = stu1 这样会报错,因为golang认为Stu是重新定义的结构体
stu1 = Student(stu2)
fmt.Println(stu1, stu2)
} //输出结果为:{ 0} { 0}
9. struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化
type Monsters struct {
Name string `json:"name"` //`json:"name"`就是struct tag
Age int `json:"age"`
Skill string `json:"skill"`
}
func main() {
//1. 创建一个monster变量
monster := Monsters{"牛魔王", 500, "芭蕉扇"}
//2. 将monster变量序列化为 json格式字串
// json.Marshal函数中使用了反射
jsonMonster, err := json.Marshal(monster)
if err != nil {
fmt.Println("json 处理错误", err)
}
//如果age, name, skill首字母是小写,name返回空序列,所以必须要大写
//但如果某些程序员或用户不习惯大写,非要小些,那么可以在struct定义的时候加上 `json:"name"`
//注意:式键盘左上角的``,不是引号''
fmt.Println("jsonMonster", jsonMonster)
fmt.Println("jsonMonster", string(jsonMonster))
}
创建struct实例的四种方式
方式一:
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{}
p1.Name = "tom"
p1.Age = 18
fmt.Println(p1) //{tom 18}
}
方式二:
p2 := Person{"marry", 18}
fmt.Println(p2) //{marry 18}
方式3:
// var person *person = new (Person)
var p3 *Person = new(Person)
//因为p3是一个指针,因此标准的给字段赋值
(*p3).Name = "smith"
//(*p3).Name = "smith" 也可以这样写 p3.Name = "smith"
//原因:go的设计者为了程序员使用方便,底层会对p3.Name = "smith"进行处理
//会给 p3 加上取值运算(*p3).Name = "smith"
p3.Name = "amy"
(*p3).Age = 30
fmt.Println(*p3) //{amy 30}
方式4:
// var person *Person = &person{}
var person *Person = &Person{}
//因为person是一个指针,因此标准的访问字段的方法
//(*person).Name = "scott"
//go的设计者为了程序员使用方便,也可以person.Name = "scott"
//原因和上面一样,底层会对 person.Name = "scott"进行处理,会加上(*person)
(*person).Name = "scott"
person.Name = "scott~~"
(*person).Age = 88
person.Age = 10
fmt.Println(*person)
第3种和第4种方式返回的是 结构体指针
结构体指针访问字段的标准方式应该是:(*结构体指针)字段名,比如:(*person).Name = "tom"
但go做了一个简化,也支持结构体指针.字段名,比如:preson.Name = "tom "。更符合程序员使用的习惯,go编译器底层对person.Name做了转化(*person).Name