结构体类型的定义
type Course struct {
price int
name string
url string
}
Course结构体内部有三个变量,分别是价格、课程名、url。特别需要注意是结构体内部变量的大小写,首字母大写是公开变量,首字母小写是内部变量,分别相当于类成员变量的 Public 和 Private 类别。内部变量只有属于同一个 package(简单理解就是同一个目录)的代码才能直接访问。
结构体变量的创建
创建一个结构体变量有多种形式,我们先看结构体变量最常见的创建形式
package main
import "fmt"
type Course struct {
price int
name string
url string
}
func main() {
var c Course = Course {
price: 100,
name: "scrapy分布式爬虫",
url: "", // 注意这里的逗号不能少
}
fmt.Printf("%+v\n", c)
}
通过显示指定结构体内部字段的名称和初始值来初始化结构体,可以只指定部分字段的初值,甚至可以一个字段都不指定,那些没有指定初值的字段会自动初始化为相应类型的「零值」。这种形式我们称之为 「KV 形式」。
结构体的第二种创建形式是不指定字段名称来顺序字段初始化,需要显示提供所有字段的初值,一个都不能少。这种形式称之为「顺序形式」。
package main
import "fmt"
type Course struct {
price int
name string
url string
}
func main() {
var c Course = Course {100, "scrapy分布式爬虫", ""}
fmt.Printf("%+v\n", c)
}
结构体变量和普通变量都有指针形式,使用取地址符就可以得到结构体的指针类型
var c *Course = &Course {100, "scrapy分布式爬虫", ""}
使用new() 函数来创建一个「零值」结构体
var c *Course = new(Course)
:::info 注意 new() 函数返回的是指针类型。下面再引入结构体变量的第四种创建形式,这种形式也是零值初始化,就数它看起来最不雅观。 :::
var c Course
最后我们再将三种零值初始化形式放到一起对比观察一下
var c1 Course = Course{}
var c2 Course
var c3 *Course = new(Course)
零值结构体和 nil 结构体
nil 结构体是指结构体指针变量没有指向一个实际存在的内存。这样的指针变量只会占用 1 个指针的存储空间,也就是一个机器字的内存大小。
var c *Course = nil
而零值结构体是会实实在在占用内存空间的,只不过每个字段都是零值。如果结构体里面字段非常多,那么这个内存空间占用肯定也会很大
结构体的拷贝
结构体之间可以相互赋值,它在本质上是一次浅拷贝操作,拷贝了结构体内部的所有字段。结构体指针之间也可以相互赋值,它在本质上也是一次浅拷贝操作,不过它拷贝的仅仅是指针地址值,结构体的内容是共享的。
package main
import "fmt"
type Course struct {
price int
name string
url string
}
func main() {
var c1 Course = Course {50, "scrapy分布式爬虫", ""}
var c2 Course = c1
fmt.Printf("%+v\n", c1)
fmt.Printf("%+v\n", c2)
c1.price = 100
fmt.Printf("%+v\n", c1)
fmt.Printf("%+v\n", c2)
var c3 *Course = &Course{50, "scrapy分布式爬虫", ""}
var c4 *Course = c3
fmt.Printf("%+v\n", c3)
fmt.Printf("%+v\n", c4)
c3.price = 100
fmt.Printf("%+v\n", c3)
fmt.Printf("%+v\n", c4)
}
slice的结构体
通过观察 Go 语言的底层源码,可以发现所有的 Go 语言内置的高级数据结构都是由结构体来完成的。
切片头的结构体形式如下,它在 64 位机器上将会占用 24 个字节
type slice struct {
array unsafe.Pointer // 底层数组的地址
len int // 长度
cap int // 容量
}
:::info 此处解释一下slice的函数传递本质上也是值传递 :::
字符串头的结构体
它在 64 位机器上将会占用 16 个字节
type string struct {
array unsafe.Pointer // 底层数组的地址
len int
}
map的结构体
type hmap struct {
count int
...
buckets unsafe.Pointer // hash桶地址
...
}
解释一下下面的情况
在数组与切片章节,我们自习分析了数组与切片在内存形式上的区别。数组只有「体」,切片除了「体」之外,还有「头」部。切片的头部和内容体是分离的,使用指针关联起来。请读者尝试解释一下下面代码的输出结果
package main
import "fmt"
import "unsafe"
type ArrayStruct struct {
value [10]int
}
type SliceStruct struct {
value []int
}
func main() {
var as = ArrayStruct{[...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}
var ss = SliceStruct{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}
fmt.Println(unsafe.Sizeof(as), unsafe.Sizeof(ss))
}
结构体的参数传递
结构体是值传递
package main
import "fmt"
type Course struct {
price int
name string
url string
}
func changeCourse(c Course){
c.price = 200
}
func main() {
var c Course = Course {
price: 100,
name: "scrapy分布式爬虫",
url: "", // 注意这里的逗号不能少
}
changeCourse(c)
fmt.Println(c.price)
}