- 自定义类型和类型别名
- 结构体
- 定义,初始化,构造函数,以及访问
- 匿名结构体
- 函数的传址和传值
- 结构体指针
- 结构体内存布局
- 以结构体的方式体现内存对齐
- 结构体匿名字段
- 结构体嵌套
- 嵌套匿名字段
- 匿名嵌套结构体的字段名冲突
package main
import "fmt"
//自定义类型和类型别名
//type后面跟的是类型
type myInt int //自定义类型
type yourInt = int
func main() {
var n myInt = 100
var m yourInt = 100
fmt.Printf("%T, %vn", n, n) //main.myInt, 100
fmt.Printf("%T, %v", m, m) //int, 100
}
结构体
结构体是值类型
定义,初始化,构造函数,以及访问可以安全地返回指向局部变量的指针,因为局部变量将在函数的作用域中继续存在(Go编译器使用栈逃逸机制将这种局部变量的空间分配在堆上)
并且不论通过结构体对象还是结构体指针来访问 结构体变量
都可以通过 . 进行,如果是结构体指针的话,指针将会被自动解引用
构造函数需要自己写,因为struct是值类型,所以需要返回指针类型,避免拷贝
package main
import "fmt"
type person struct {
name string
age int
}
//可以安全地返回指向局部变量的指针,因为局部变量将在函数的作用域中继续存在
func newPerson(name string) *person {
p := person{name: name, age: 42}
return &p
}
func main() {
p1 := person{name: "messi", age: 20}
fmt.Printf("%T, %vn", p1, p1) //main.person, {messi 20}
p2 := &person{name: "xavi", age: 25}
fmt.Printf("%T, %vn", p2, p2) /
s.age = 20
s.name = "messi"
fmt.Printf("%v, %T", s, s) //{messi 20}, struct { name string; age int }
}
函数的传址和传值
package main
import "fmt"
type person struct {
name string
age int
}
func changePV(p person) {
p.age++
}
func changePA(p *person) {
//在Go中会自动对指针类型的变量进行解引用,所以不需要 (*p).age++
p.age++
}
func main() {
p1 := new(person)
p1.age = 20
p1.name = "messi"
fmt.Printf("%#v, %Tn", p1, p1)
changePV(*p1)
fmt.Printf("after changePV%#v, %Tn", p1, p1) //
changePA(p1)
fmt.Printf("after changePA%#v, %Tn", p1, p1)
}
结构体指针
package main
import "fmt"
type person struct {
name string
age int
}
func changePV(p person) {
p.age++
}
func changePA(p *person) {
//在Go中会自动对指针类型的变量进行解引用,所以不需要 (*p).age++
p.age++
}
func main() {
var pStruct_1 bool = false
if pStruct_1 {
p := new(person)
//p.age = 20
//p.name = "messi"
fmt.Printf("%Tn", p)
fmt.Printf("%vn", p)
fmt.Printf("%pn", p) //new出来的地址
fmt.Printf("%pn", &p) //p变量本身的地址
}
//结构体初始化
var pStruct_2 bool = false
if pStruct_2 {
p := person{
name: "messi",
age: 20,
}
fmt.Printf("%#vn", p)
}
//直接取结构体的地址进行初始化,相当于通过new初始化,并返回new出来的地址
var pStruct_3 bool = true
if pStruct_3 {
p := &person{
name: "messi",
age: 20,
}
fmt.Printf("%%v:%#v, p本身的: %p, new申请的地址: %pn", p, &p, p)
//%v:&main.person{name:"messi", age:20}, p本身的: 0x14000124018, new申请的地址: 0x1400011c018
}
}
结构体内存布局
package main
import "fmt"
type person struct {
a int64
b int64
c int64
}
func main() {
n := person{
a: 1,
b: 2,
c: 3,
}
fmt.Printf("&n :%pn", &n)
fmt.Printf("&n.a:%pn", &n.a)
fmt.Printf("&n.b:%pn", &n.b)
fmt.Printf("&n.c:%pn", &n.c)
}
以结构体的方式体现内存对齐
内存对齐
结构体匿名字段使用匿名字段时,会将字段类型作为变量的名字,也就意味着同类型变量只能出现一次
package main
import "fmt"
type person struct {
int
string
}
func main() {
p := person{20, "messi"}
fmt.Printf("%Tn", p)
fmt.Println(p.int)
}
但是可以通过自定义类型来出现多次,由此推测 只要编译时类型不一样就行,如下所示
package main
import "fmt"
type myInt int
type yourInt = int
type person struct {
int
string
myInt
yourInt
}
func main() {
p := person{20, "messi", 20, 20}
fmt.Printf("%T, %vn", p, p)
fmt.Println(p.myInt)
}
结构体嵌套
package main
import "fmt"
type Address struct {
sheng string
shi string
qu string
}
type person struct {
name string
age int
address Address
}
func main() {
p := person{name: "messi", age: 20, address: Address{
sheng: "shanxi", shi: "xian", qu: "yanta"},
}
fmt.Println(p)
}
嵌套匿名字段
通过匿名字段,可实现对内部结构体变量的直接访问
package main
import "fmt"
type Address struct {
sheng string
shi string
qu string
}
type person struct {
name string
age int
Address
}
func main() {
p := person{name: "messi", age: 20, Address: Address{
sheng: "shanxi", shi: "xian", qu: "yanta"},
}
fmt.Println(p.sheng) //可直接访问Address中的sheng
}
匿名嵌套结构体的字段名冲突
如果外层结构体和内层结构体存在字段名的冲突,则使用内层结构体的类型名作为字段名
对冲突字段进行访问
由此推断出,访问变量时,优先查找本层结构体
package main
import "fmt"
type Address struct {
sheng string
shi string
qu string
}
type person struct {
name string
age int
sheng string
Address
}
func main() {
p := person{name: "messi", age: 20, sheng: "beijing", Address: Address{
sheng: "shanxi", shi: "xian", qu: "yanta"},
}
fmt.Println(p.sheng, p.Address.sheng)
}



