持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第35天,点击查看活动详情
一、结构体和结构体变量区别及联系
- 结构体是
自定义的数据类型,代表一类事物. - 结构体变量(实例)是具体的,实际的,代表一个具体变量
二、struct的内存布局
2.1 案例演示
package main
import "fmt"
// 定义Cat结构体
type Cat struct {
Name string
Age int
Color string
}
func main() {
// 使用struct
// 创建Cat变量
var cat1 Cat
//cat1 = { 0 }
// cat1的地址= 0xc000100480
fmt.Printf("cat1的地址= %p\n", &cat1)
// 对变量赋值
cat1.Name = "小白"
cat1.Age = 3
cat1.Color = "白色"
// cat.Name 的地址 = 0xc000100480
// cat.Age 的地址 = 0xc000100490
// cat.Color 的地址 = 0xc000100498
fmt.Printf("cat.Name 的地址 = %p\n", &cat1.Name)
fmt.Printf("cat.Age 的地址 = %p\n", &cat1.Age)
fmt.Printf("cat.Color 的地址 = %p\n", &cat1.Color)
}
三、结构体的声明和使用陷阱
3.1 语法(注意变量名大小写,是否在其他包可用)
type 结构体名称 struct{
字段名 类型
......
}
// 声明案例
type Student struct{
Name string
Age int
Score float64
}
3.2 结构体的字段/属性
3.2.1 基本介绍
- 从概念或叫法上看:结构体字段= 属性=field
- 字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。比如前面定义猫结构体的
Name string就是属性
3.2.2 注意事项和说明
- 字段声明语法同变量,示例:字段名字段类型
- 字段的类型可以为:基本类型、数组或引用类型
- 在创建一个结构体变量后, 如果没有给字段赋值,都对应一个零值(默认值):
- 布尔类型是false,数值是0,字符串是""。
- 数组类型的默认值和它的元素类型相关,比如score [3]int则为[0, 0, 0]
- 指针,slice, 和map的零值都是nil, 即还没有分配空间,需要先
make。详见案例3.2.3案例一
不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个。详见案例3.2.4案例二
3.2.3 案例一
package main
import "fmt"
// 若结构体字段类型是 : 指针,slice,map 的零值都是nil,未分配空间,需要先进行make
type Person struct {
Name string
Age int
Scores [3]float64
ptr *int // 指针类型 ,需要 new
slice []int // 切片
strMap map[string]string // map
}
func main() {
// 定义结构体变量
var p1 Person
// 查看p1的内容
// p1 = { 0 [0 0 0] <nil> [] map[]}
fmt.Printf("p1 = %v\n", p1)
// 测试指针 切片 map三种类型的初值
// ptr nil
// slice nill
// strMap nil
if p1.ptr == nil {
fmt.Println("ptr nil")
}
if p1.slice == nil {
fmt.Println("slice nill")
}
if p1.strMap == nil {
fmt.Println("strMap nil")
}
// 使用slice
p1.slice = make([]int, 10)
p1.slice[0] = 1000
// p1.slice = [1000 0 0 0 0 0 0 0 0 0]
fmt.Printf("p1.slice = %v\n", p1.slice)
// 使用map
p1.strMap = make(map[string]string)
p1.strMap["Name"] = "feng"
// p1.strMap["Name"] = feng
fmt.Printf("p1.strMap[\"Name\"] = %v\n", p1.strMap["Name"])
}
3.2.4 案例二
package main
import "fmt"
// 若结构体字段类型是 : 指针,slice,map 的零值都是nil,未分配空间,需要先进行make
type Person struct {
Name string
Age int
Scores [3]float64
ptr *int // 指针类型 ,需要 new
slice []int // 切片
strMap map[string]string // map
}
type Student struct {
Name string
Age int
}
func main() {
// 定义结构体变量
var p1 Person
// 查看p1的内容
// p1 = { 0 [0 0 0] <nil> [] map[]}
fmt.Printf("p1 = %v\n", p1)
// 测试指针 切片 map三种类型的初值
// ptr nil
// slice nill
// strMap nil
if p1.ptr == nil {
fmt.Println("ptr nil")
}
if p1.slice == nil {
fmt.Println("slice nill")
}
if p1.strMap == nil {
fmt.Println("strMap nil")
}
// 使用slice
p1.slice = make([]int, 10)
p1.slice[0] = 1000
// p1.slice = [1000 0 0 0 0 0 0 0 0 0]
fmt.Printf("p1.slice = %v\n", p1.slice)
// 使用map
p1.strMap = make(map[string]string)
p1.strMap["Name"] = "feng"
// p1.strMap["Name"] = feng
fmt.Printf("p1.strMap[\"Name\"] = %v\n", p1.strMap["Name"])
/***********************************************************/
// 不同结构体的变量字段独立
var stu1 Student
stu1.Name = "Aliy"
stu1.Age = 22
// stu1 名字 = Aliy , 年龄 = 22
fmt.Println("stu1 名字 = ", stu1.Name, ", 年龄 = ", stu1.Age)
stu2 := stu1
stu2.Name = "Bob"
// stu2 名字 = Bob , 年龄 = 22
fmt.Println("stu2 名字 = ", stu2.Name, ", 年龄 = ", stu2.Age)
// 若想影响到原来的字段的变量值
stu3 := &stu1
stu3.Name = "Cindy"
// stu3 名字 = Cindy , 年龄 = 22
fmt.Println("stu3 名字 = ", stu3.Name, ", 年龄 = ", stu3.Age)
// stu1 名字 = Cindy , 年龄 = 22
fmt.Println("stu1 名字 = ", stu1.Name, ", 年龄 = ", stu1.Age)
}
四、创建结构体变量和访问结构体字段
4.1 方式一
var 变量名 struct类型
4.2 方式二
var 变量名 struct类型=struct类型{}
案例
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
// 声明实例
var person Person = Person{"Aliy", 22}
// 或者
person2 := Person{"Bob", 20}
// 或者
person3 := Person{}
person3.Name = "Cindy"
person3.Age = 18
// person = {Aliy 22}
fmt.Printf("person = %v\n", person)
// person2 = {Bob 20}
fmt.Printf("person2 = %v\n", person2)
// person3 = {Cindy 18}
fmt.Printf("person3 = %v\n", person3)
}
4.3 方式三
var 变量名 *struct类型 = new(struct类型)
案例
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
// 声明方式三
var person *Person = new(Person)
// 因为 person是一个指针,因此便准的给字段赋值方式如下
(*person).Name = "AliyPtr"
(*person).Age = 22
// person = {AliyPtr 22}
fmt.Printf("person = %v\n", *person)
// go设计者为使程序员使用方便,在底层对person.Name 进行处理,自动给person加上取值运算
// 形成这样 (*person).Name
// 可以直接进行如下操作
person.Name = "Aliy"
// no * ----> {Aliy 22}
fmt.Printf("no * ----> %v", *person)
}
4.4 方式四
var 变量名 *struct类型 = &struct类型{}
案例
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
// 声明方式四
var person *Person = &Person{}
// 因为person是一个指针,标准访问如下
// (*person).Name = "AliyPrt"
// 类似方式三,也可以像如下写法
person.Name = "Aliy"
person.Age = 22
// person = {Aliy 22}
fmt.Printf("person = %v\n", *person)
}
4.5 总结
- 第3种和第4种方式返回的是结构体指针。
- 结构体指针访问字段的标准方式应该是: (*结 构体指针).字段名,比如(*person).Name = "tom"
- 但go做了-一个简化,也支持结构体指针.字段名,比如person.Name = "tom"。更加符合程序员使用的习惯,go编译器底层对person.Name做了转化(*person).Name