结构体说明
结构体(struct) 是用户自定义的类型,它代表若干字段(基本数据类型/引用数据类型)的集合。有些时候将多个数据看做一个整体要比单独使用这些数据更有意义,这种情况下就适合结构体。
比如将一个员工的name,department,salary三个属性打包在一起成为一个employee
结构就很有意义的。
结构体类型
结构体是复合类型(composite types),当声明一个结构体变量的时候,内存空间就已经被分配,每个字段(field)
为类型的0
值。
上面的内存示意图表明了,变量employee
声明为Employee
结构体类型的时候,会初始化该结构体中字段类型的0
值,然后直接指向声明后的结构体,也可以说明:结构体
是值
类型。
不同的结构体类型变量字段是独立的,互不影响,一个结构体变量字段的更改,不影响另外一个结构体类型变量。
package main
import "fmt"
type Employee struct {
Name,Department string
Salary float32
Task []string
}
func main() {
var employee1 = Employee{
Name: "易文杰",
Department: "系统开发部",
Salary: 5000,
Task: []string{
"支付",
"客户分配",
},
}
employee2 := employee1
employee2.Name = "张冲"
fmt.Println(employee1,employee2)
}
上面的代码,声明了一个employee1
变量后,将employee1
赋值给employee2
变量,当我们修改employee2
的字段Name
后,发现emloyee1
中的Name
并没有被更改。程序的输出结果:
{易文杰 系统开发部 5000 [支付 客户分配]} {张冲 系统开发部 5000 [支付 客户分配]}
结构体的声明
type Employee struct {
Name string
Department string
Salary float32
}
上面的代码声明了一个名为Employee
的结构体类型,它拥有Name
,Department
,Salary
三个字段。同一个类型的多个字段可以合并到一行(用逗号进行分隔),并将类型放到后面。上面的结构体中Name
和Department
都是string
的类型,因此可以将它们写到一起。
type Employee struct {
Name,Department string
Salary float32
}
上面结构体Employee
是一个具名结构体(named structure),因为它创建了一个具有名称的结构体类型:Employee
。我们可以定义具名结构体类型的变量。
我们也可以定义一个没有类型名称的结构体,这种结构体叫做匿名结构体(anonymous structures)
var employee struct{
Name,Department string
Salary float32
}
当结构体中的字段是map
,slice
,指针
的时候,默认的0
值是nil
(没有分配空间)的时候,需要分配空间后才能被使用
package main
import "fmt"
type Employee struct {
Name,Department string
Salary float32
Mapper map[string]string
Slicer []string
Ptr *int
}
func main() {
var employee Employee
employee.Name = "易文杰"
employee.Department = "系统开发部"
employee.Mapper = make(map[string]string)
employee.Mapper["smile"] = "易文杰"
employee.Slicer = make([]string,10)
employee.Slicer[0] = "切片"
age := 12
employee.Ptr = &age
fmt.Println(employee)
}
当未分配空间就直接使用,会导致代码异常
panic: assignment to entry in nil map
上面的程序会输出:
{易文杰 系统开发部 0 map[smile:易文杰] [切片 ] 0xc00000a098}
结构体的作用域
结构体采用首字母大小写来区分访问权限,当首字母是大写,可以别外包引用。反之,不能。
定义具名结构体变量
下面的程序说明了如何定义一个具名结构体Employee
的变量
package main
import "fmt"
//定义结构体
type Employee struct {
Name,Department string
Salary float32
}
func main() {
//使用结构体一
employee1 := Employee{
Name: "易文杰",
Department: "系统研发部",
Salary: 5000,
}
//使用结构体二
employee2 := Employee{"张冲","系统研发部",15000}
fmt.Println(employee1,employee2)
}
在上面的程序中,我们定义了一个名叫Employee
的结构体类型,我们可以通过指定字段的名称和对应的值来创建一个结构体变量。其中字段名和类型必须要和结构体中声明的保持一致。
采用第一种定义结构体变量:不需
要和声明结构体中的变量顺序保持一致,但最后一个数据需保留,
采用第二种定义结构体变量:需要
和声明结构体中的变量顺序保持一致,最后一个数据不需要保留,
上面的程序输出如下:
{易文杰 系统研发部 5000} {张冲 系统研发部 15000}
定义匿名结构体变量
package main
import "fmt"
func main() {
var employee = struct {
Name,Department string
Salary float32
}{"张冲","系统开发部",28000}
fmt.Println(employee)
}
在上面的程序中,我们定义了一个employee
匿名结构体变量。这种结构体称为匿名结构体,因为它只创建了一个新的结构体变量employee
,而没有定义新的结构体类型。
上面的程序输出为:
{张冲 系统开发部 28000}
访问结构体中的字段
使用.
操作符来访问结构体中的字段
package main
import "fmt"
func main() {
var employee = struct {
Name,Department string
Salary float32
}{"张冲","系统开发部",28000}
fmt.Printf("姓名%v\n部门%v\n工资%v\n",employee.Name,employee.Department,employee.Salary)
}
在上面的程序中,通过employee.Name
来访问employee
中的字段Name
。程序的输出为:
姓名张冲
部门系统开发部
工资28000
结构体指针
可以定义指向结构体的指针。
package main
import "fmt"
type Employee struct {
Name,Department string
Salary float32
}
func main() {
//方式一
var employee1 = &Employee{
Name: "张冲",
Department: "系统研发部",
Salary: 18000,
}
//方式二
var employee2 = new(Employee)
employee2.Name = "易文杰"
fmt.Println(employee1.Name)
fmt.Println((*employee1).Name)
fmt.Println(employee2.Name)
fmt.Println((*employee2).Name)
}
上面的程序中,employee1
和employee2
是一个指向Employee
结构体的指针。(*employee1).Name
是访问employee1
中的Name
字段。程序的输出为:
张冲
张冲
易文杰
易文杰
在Go中我们可以使用employee.Name
替代显示解引用(*employee).Name
来访问Name
字段。