GoGo
1. 结构体的定义
typestruct
typestruct
结构体成员是由一系列的成员变量构成,这些成员变量也被称为“字段”。字段有以下特性:
- 字段拥有自己的类型和值。
- 字段名必须唯一。
- 字段的类型也可以是结构体,甚至是字段所在结构体的类型。
typestruct
结构体的标准格式如下:
type structName struct {
member type;
member type;
...
member type;
}
2. 结构体实例化
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。
package main
import (
"fmt"
)
type Student struct {
Id int
Name string
Age int
Mobile int
Major string
}
func main() {
// 创建Student指针类型变量
s1 := new(Student)
// 创建Student指针类型变量
s2 := &Student{}
// 创建Student类型变量
s3 := Student{}
// 创建Student指针类型变量,并根据字段名附初始值
s4 := &Student{
Id: 20210901,
Name: "wohu",
Age: 20,
Mobile: 123456,
Major: "move brick",
}
// 创建Student类型变量,并根据字段在结构体中的顺序附初始值
s5 := Student{
20170901,
"wohu",
20,
123456,
"move brick",
}
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
fmt.Println("s3:", s3)
fmt.Println("s4:", *s4)
fmt.Println("s5:", s5)
}
输出结果:
s1: &{0 0 0 }
s2: &{0 0 0 }
s3: {0 0 0 }
s4: {20210901 wohu 20 123456 move brick}
s5: {20170901 wohu 20 123456 move brick}
{}newnew{}
{}&new
.
s1.Name = "hi hzwy23"
s3.Name = "hello hzwy23"
fmt.Println(s3.Name)
fmt.Println(s1.Name)
.
2.1 普通实例化
var
var phone Phone
// 其中,Phone 为结构体类型,phone 为结构体的实例
用结构体表示的点结构(Point)的实例化过程请参见下面的代码:
type Point struct {
X int
Y int
}
var p Point
p.X = 10
p.Y = 20
.p.Xp.Y
nilPointPoint{}
2.2 new 实例化
Gonew
new
ins := new(T)
其中:
TinsTinsins*T
Go.
type Player struct{
Name string
HealthPoint int
MagicPoint int
}
tank := new(Player)
tank.Name = "Canon"
tank.HealthPoint = 300
new
new->
Go.Goins.Name(*ins).Name
2.3 取地址实例化
Go& new
ins := &T{}
其中:
Tins*T
下面使用结构体定义一个命令行指令(Command),指令中包含名称、变量关联和注释等,对 Command 进行指针地址的实例化,并完成赋值过程,代码如下:
type Command struct {
Name string // 指令名称
Var *int // 指令绑定的变量
Comment string // 指令的注释
}
var version int = 1
cmd := &Command{}
cmd.Name = "version"
cmd.Var = &version
cmd.Comment = "show version"
取地址实例化是最广泛的一种结构体实例化方式,可以使用函数封装上面的初始化过程,代码如下:
func newCommand(name string, varref *int, comment string) *Command {
return &Command{
Name: name,
Var: varref,
Comment: comment,
}
}
cmd = newCommand(
"version",
&version,
"show version",
)
3. 结构体初始化
结构体在实例化时可以直接对成员变量进行初始化,初始化有两种形式分别是以字段“键值对”形式和多个值的列表形式,键值对形式的初始化适合选择性填充字段较多的结构体,多个值的列表形式适合填充字段较少的结构体。
3.1 键值对初始化
结构体可以使用“键值对”(Key value pair)初始化字段,每个“键”(Key)对应结构体中的一个字段,键的“值”(Value)对应字段需要初始化的值。
键值对的填充是可选的,不需要初始化的字段可以不填入初始化列表中。
0falsenil
键值对初始化的格式如下:
varName := structName{key1: value1, key2: value2..., keyn: valuen}
// 键值之间以:分隔,键值对之间以,分隔。
下面示例中描述了家里的人物关联,正如儿歌里唱的:“爸爸的爸爸是爷爷”,人物之间可以使用多级的 child 来描述和建立关联,使用键值对形式填充结构体的代码如下:
type People struct {
name string
child *People
}
relation := &People{
name: "爷爷",
child: &People{
name: "爸爸",
child: &People{
name: "我",
},
},
}
注意:结构体成员中只能包含结构体的指针类型,包含非结构体指针类型会引起编译错误。
3.2 多值列表初始化
Go
varName := structName{value1, value2...valuen}
使用这种格式初始化时,需要注意:
- 必须初始化结构体的所有字段。
- 每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致。
- 键值对与值列表的初始化形式不能混用。
type Address struct {
Province string
City string
ZipCode int
PhoneNumber string
}
addr := Address{
"陕西",
"西安",
710000,
"12345",
}
fmt.Println(addr) // {陕西 西安 710000 12345}
3.3 初始化注意事项
初始化复合对象,必须使用类型标签,且左大括号必须在类型尾部。
package main
func main() {
var a struct { x int } = { 100 } // syntax error, syntax error: unexpected {, expecting expression
var b []int = { 1, 2, 3 } // syntax error
c := struct {x int; y string} // syntax error: unexpected semicolon or newline
{
}
var a = struct{ x int }{100}
var b = []int{1, 2, 3}
}
初始化值以 “,” 分隔。可以分多行,但最后一行必须以 “,” 或 “}” 结尾。
a := []int{
1,
2 // Error: need trailing comma before newline in composite literal
}
a := []int{
1,
2, // ok
}
b := []int{
1,
2 } // ok
3.4 空结构体
type Empty struct{} // Empty是一个不包含任何字段的空结构体类型
空结构体类型有什么用呢?
var s Empty
println(unsafe.Sizeof(s)) // 0
Goroutine
var c = make(chan Empty) // 声明一个元素类型为Empty的channel
c<-Empty{} // 向channel写入一个“事件”
channelGoroutine
4. 匿名结构体
type
4.1 匿名结构体定义和初始化
匿名结构体的初始化写法由结构体定义和键值对初始化两部分组成,结构体定义时没有结构体类型名,只有字段和类型定义,键值对初始化部分由可选的多个键值对组成,如下格式所示:
ins := struct {
// 匿名结构体字段定义
字段1 字段类型1
字段2 字段类型2
…
}{
// 字段值初始化
初始化字段1: 字段1的值,
初始化字段2: 字段2的值,
…
}
下面是对各个部分的说明:
- 字段1、字段2……:结构体定义的字段名。
- 初始化字段1、初始化字段2……:结构体初始化时的字段名,可选择性地对字段初始化。
- 字段类型1、字段类型2……:结构体定义字段的类型。
- 字段1的值、字段2的值……:结构体初始化字段的初始值。
键值对初始化部分是可选的,不初始化成员时,匿名结构体的格式变为:
ins := struct {
字段1 字段类型1
字段2 字段类型2
…
}
4.2 匿名结构体示例
在本示例中,使用匿名结构体的方式定义和初始化一个消息结构,这个消息结构具有消息标示部分(ID)和数据部分(data),打印消息内容的 printMsg() 函数在接收匿名结构体时需要在参数上重新定义匿名结构体,代码如下:
package main
import (
"fmt"
)
// 定义 printMsgType() 函数,参数为 msg,类型为 *struct{id int data string},
// 因为类型没有使用 type 定义,所以需要在每次用到的地方进行定义。
func printMsgType(msg *struct {
id int
data string
}) {
// 使用动词%T打印msg的类型
fmt.Printf("%T\n", msg) // *struct { id int; data string }
}
func main() {
// 实例化一个匿名结构体
msg := &struct { // 定义部分
id int
data string
}{ // 值初始化部分
1024,
"hello",
}
printMsgType(msg)
}
5. 结构体访问
.
结构体.成员名
struct
package main
import "fmt"
type Phone struct {
model string
color string
price int
}
func main() {
var phone1 Phone
var phone2 Phone
var phone4 Phone
fmt.Printf("phone1 init is %v\n", phone1)
// 创建一个新的结构体
phone1 = Phone{"华为", "black", 1000}
fmt.Printf("phone1 is %v\n", phone1)
// 也可以使用 key => value 格式
phone2 = Phone{model: "华为", color: "black", price: 1000}
fmt.Printf("phone2 is %v\n", phone2)
// 忽略的字段为 0 或 空
phone3 := Phone{model: "华为", price: 1000}
fmt.Printf("phone3 is %v\n", phone3)
// 访问结构体成员
phone4.price = 8000
phone4.color = "blue"
phone4.model = "苹果"
fmt.Printf("phone4 is %v\n", phone4)
}
输出:
phone1 init is { 0}
phone1 is {华为 black 1000}
phone2 is {华为 black 1000}
phone3 is {华为 1000}
phone4 is {苹果 blue 8000}
6. 结构体作为形参
可以将结构体类型作为参数传递给函数。
package main
import "fmt"
type Phone struct {
model string
color string
price int
}
func main() {
var phone Phone
// 访问结构体成员
phone.price = 8000
phone.color = "blue"
phone.model = "苹果"
printPhone(phone)
}
func printPhone(phone Phone) {
fmt.Printf("phone price : %d\n", phone.price)
fmt.Printf("phone color : %s\n", phone.color)
fmt.Printf("phone model : %s\n", phone.model)
}
输出:
phone price : 8000
phone color : blue
phone model : 苹果
指针结构体作为值传递。
package main
import "fmt"
type InnerData struct {
a int
}
type Data struct {
complax []int
instance InnerData
ptr *InnerData
}
func passByValue(inFunc Data) Data {
fmt.Printf("inFunc value: %+v\n", inFunc)
fmt.Printf("inFunc ptr: %p\n", &inFunc)
return inFunc
}
func main() {
in := Data{
complax: []int{1, 2, 3},
instance: InnerData{
5,
},
ptr: &InnerData{1},
}
fmt.Printf("in value: %+v\n", in)
fmt.Printf("in ptr: %p\n", &in)
out := passByValue(in)
fmt.Printf("out value: %+v\n", out)
fmt.Printf("out ptr: %p\n", &out)
}
输出结果:
in value: {complax:[1 2 3] instance:{a:5} ptr:0xc0000160e8}
in ptr: 0xc000082150
inFunc value: {complax:[1 2 3] instance:{a:5} ptr:0xc0000160e8}
inFunc ptr: 0xc0000821e0
out value: {complax:[1 2 3] instance:{a:5} ptr:0xc0000160e8}
out ptr: 0xc0000821b0
7. 结构体指针
指向结构体的指针类似于其它指针变量,格式如下:
var structPointer *Phone
&
structPointer = &phone1
.
structPointer.title
使用示例
package main
import "fmt"
type Phone struct {
model string
color string
price int
}
func main() {
var p *Phone
var phone Phone
p = &phone
// 访问结构体成员
p.price = 8000
p.color = "blue"
p.model = "苹果"
fmt.Printf("p is : %v\n", p)
fmt.Printf("*p is : %v\n", *p)
fmt.Printf("&p is : %v\n", &p)
fmt.Printf("phone is : %v\n", phone)
printPhone(p)
}
func printPhone(phone *Phone) {
fmt.Printf("phone price : %d\n", phone.price)
fmt.Printf("phone color : %s\n", phone.color)
fmt.Printf("phone model : %s\n", phone.model)
}
输出结果:
p is : &{苹果 blue 8000}
*p is : {苹果 blue 8000}
&p is : 0xc00000e028
phone is : {苹果 blue 8000}
phone price : 8000
phone color : blue
phone model : 苹果
var p *Phone // 就是说 p 这个指针是 Phone 类型的
p = &phone // phone 是 Phone 的一个实例化的结构,&phone 就是把这个结构体的内存地址赋给了 p,
*p // 那么在使用的时候,只要在 p 的前面加个*号,就可以把 p 这个内存地址对应的值给取出来了
&p // 就是取了 p 这个指针的内存地址,也就是 p 这个指针是放在内存空间的什么地方的。
phone // 就是 phone 这个结构体,打印出来就是它自己。也就是指针 p 前面带了 * 号的效果。
8. 结构体拥有自身类型的指针类型、以自身类型为元素类型的切片类型
type T struct {
t T // error
... ...
}
Goinvalid recursive type T
但是下面的是可以的
type T struct {
t *T // ok
st []T // ok
m map[string]T // ok
}
一个类型,它所占用的大小是固定的,因此一个结构体定义好的时候,其大小是固定的。但是,如果结构体里面套结构体,那么在计算该结构体占用大小的时候,就会成死循环。
mapintmap
valuemap
9. 零值初始化
GoGo
var book Book // book为零值结构体变量
GosyncMutex
GoCC
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
... ...
pthread_mutex_unlock(&mutex);
Cmutexpthread_mutex_initlockunlock
Go
var mu sync.Mutex
mu.Lock()
mu.Unlock()
Gosync.MutexMutexlockunlock
10. 结构体可比较性