结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体,每个值称为结构体的成员。下面分两方面介绍:一个是基础部分,一个是看一下结构体在实际内存中的内存模型。
1 基础
1.1 定义
- type structName typeName {}
1.2 声明
- var varName structName
1.3 初始化
- type Point struct{ X, Y int }
p := Point{1, 2} - anim := gif.GIF{LoopCount: nframes}
此创建方式结构体成员顺序不重要。
1.4 new
- new(structType)
- new出来的是结构体指针。
1.5 特性
- 结构体成员相同,顺序不同不是相同结构体类型。
- 结构体成员以大写字母可以导出,小写的不可以导出。
- 结构体类型不能再包含形同类型的成员:因为一个聚合的值不能包含它自身。但结构体可以包含相同类型的指针类型的成员,这可以让我们创建递归的数据结构,比如链表和树结构等。
- 结构体类型的零值对应的是每个成员相应类型的零值。
- 如果结构体没有任何成员的话就是空结构体,写作struct{}。它的大小为0,也不包含任何信息,但是有时候依然是有价值的。比如在管道中传递的数据如果只紧起到信号作用那完全可以用struct{}以便节省空间。
1.6 函数传参
- 值传递,如果结构体比较大的话最好指针传递。
1.7 结构体比较
- 如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体 将可以使用==或!=运算符进行比较。
1.8 匿名成员
- 只声明一个成员对应的数据类型而不指名成员的名字,这类成员就叫匿名成员。
- 因为匿名成员也有一个隐式的名字,因此不能同时包含两个类型相同的匿名成员,这会导致 名字冲突。
1.9 嵌套
结构体可以嵌套另一个结构体。
结构体可以包含结构体类型的匿名成员,这样可以通过p.b就可以访问匿名成员结构体中的b。
1.10 标签
1.10.1 标签主要应用于json字符串与结构体成员的相互转换的映射关系,见下方代码实例:
type User struct {
UserId int `json:"user_id"`
UserName string `json:"user_name"`
}
u := &User{UserId: 1, UserName: "xitehip"}
j, _ := json.Marshal(u)
fmt.Println(string(j))
// 输出内容:{"user_id":1,"user_name":"tony"}
1.10.2 struct成员变量标签获取
t := reflect.TypeOf(u)
field := t.Elem().Field(0)
fmt.Println(field.Tag.Get("json"))
关于映射后续会有专门章节讲解。
2 内存布局
代码实例:
type Person struct {
age int
class int
name string
}
var pp *Person
func main() {
p := Person{age:18, class:8, name:"xitehip"}
pp = &p
fmt.Println(*pp)
}
编译生成的可执行文件,然后用gdb去分析内存布局见下图1
由图1可以发现,结构体中的元素确实是连续排布的,只不过成员name存储的是指针。打印name指针的值发现确实是xitehip(可查看ascii表)这个字符串。
3 总结
结构体是一种存储复合数据,主要应用于一个实体有多个数据类型的场景。在golang面向对象思想中,结构体也应用非常广泛,在后续的文章会详细讲解golang中的面向对象。
参考go语言圣经