Go中的结构体,结构体指针、空结构体和nil的区别。

1,364 阅读5分钟

日常_01 (1).jpg

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

Golang 中结构体 struct 定义,结构体指针,空结构体和 nil 区别学习

Golang 中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。 类似 Java,Python 中的 class。

一。结构体的定义和初始化

1. 语法

关键字 struct 表示创建一个结构体,语法如下

type struct_variable_name struct {
   member1 definition
   member2 definition
   ...
   member definition
}

我们可以定义一个Person结构体,这个结构体包含三个成员变量,name、age、hight。

type Person struct {
    name  string
    age   int
    hight float64
}

一旦定义了结构体类型,它就能用于变量的声明

2. 初始化

结构体初始化多种,可以灵活选择

(1). 方式一,简短声明初始化:

bob := Person{"Bob", 19, 1.85}
fmt.Printf("Bob 数据类型:%T,值为:%v\n", bob, bob)

输出为:

Bob 数据类型:main.Person,值为:{Bob 19 1.85}

(2). 方式二,var 定义:

var alan Person
fmt.Println("alan 结构体:", alan) 
alan.name = "Alan"
alan.age = 20
alan.hight = 1.78
fmt.Println("Alan 结构体内容:", alan)

输出为:

alan 结构体: { 0 0}   // {  0 0}  注意:第一个值是空字符串,控制台输出不明显,看不出来
Alan 结构体内容: {Alan 20 1.78}

通过 var 声明结构体 (未初始化) alan,alan 是一个只有默认值的结构体,不是 nil。

我们可以试验一下:

var jerry Person
fmt.Printf(jerry == nil)  // 这段代码编译会报错->无效操作:不匹配的类型Person和nil

// 报错内容:invalid operation: jerry == nil (mismatched types Person and nil)
我们定义了一个名为 jerry 的结构体,并未进行初始化,在判断是否为 nill 时,代码报错了

首先要明白,Golang 中 nil 表示什么,以下是我从源码中复制的:

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.

var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

可以看到, nil 的类型必须是一个指针,通道,函数,接口,字典,切片类型,他们都是引用类型

而结构体 struct 是值类型,jerry 结构体未初始化,其成员变量的值都会取默认值,所以也可以理解为是有值,只不过是默认值。

(3) 方式三:简单声明并初始化Person {}

jack := Person{
    name:  "Jack",
    age:   19,
    hight: 1.69,
}
fmt.Println("Jack 结构体内容:", jack)

输出:

Jack 结构体内容: {Jack 19 1.69}

二。结构体指针

它是一个指针,指向了一个结构体,同样有多种方式获得

1. 结构体前面加’*’

声明结构体时在结构体前面加一个 * 即可

var jerryPtr *Person  // 定义一个结构体指针,指针指向Person
jerryPtr = &bob       // 将bob的内存地址赋值给jerryPtr
fmt.Println("jerryPtr 结构体指针为:", jerryPtr)
fmt.Printf("jerryPtr 结构体指针地址为:%p,类型为:%T\n", &jerryPtr, jerryPtr)

输出:

jerryPtr 结构体指针为: &{Bob 19 1.85}
jerryPtr 结构体指针地址为:0xc000006030,类型为:*main.Person

2. 通过 new() 创建结构体

通过 new() 创建结构体,返回的也是一个指针

alan := new(Person)
fmt.Println("alan 为:", alan) // 不是nill
fmt.Printf("alan 的地址为:%p,数据类型为;%T\n", alan, alan)
alan.name = "Alan"
alan.age = 19
alan.hight = 1.79
fmt.Println("alan 结构体的内容为:", alan)

输出:

alan 为: &{ 0 }
alan 的地址为:0xc00006e540,数据类型为;*main.Person
alan 结构体的内容为: &{Alan 19 男}

观察输出,我们发现,alan 的数据类型 *main.Person,是一个指针。

3. 空结构体和 nil 区别

写到这里,在思考一个问题,那我定义一个没有任何成员变量的结构体,new 的时候,返回的是不是 nil 呢?

查看了下 new ()函数返回的是指针类型,源码如下:

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type

带着疑问,实际操作一下。 我们先定义一个空结构体:

type Student struct {
}

然后我们在 main 函数中声明一个空结构体,并判断是否为 nill

student := new(Student)
fmt.Printf("student 的数据类型为:%T,值为:%v\n", student, student)
fmt.Println("student == nill :", student == nil)

输出:

student 的数据类型为:*main.Student,值为:&{}
student == nill : false

可以看到,空结构体 student 并不是 nil,而且其的值为 &{}

写到这里,返回上文看了下 nil 的源码,疑惑瞬间解开了:

stuct 是一个值类型,即使加了 * 也只是变成了一个指针,指向结构体了。

nil 是一个 Type,根据源码 var nil Type,它其实也是 Golang 中的一中类型,nil 的类型必须是一个指针,通道,函数,接口,字典,切片类型

举个栗子,声明一个 slice,不做任何初始化,那么该 slice 就是一个nil

Talk is cheap, show me code:

var s []int64
fmt.Println("s :", s)
fmt.Println("s == nil:", s == nil)

输出:

s : []
s == nil: true

可以看到,s 确实一个 nil,和我们思考的一样。

总结一下,今天记录了 struct 的定义和声明方式,弄清楚了空结构体和 nil 的区别。

nil 在概念上和其它语言的 null、None、nil、NULL 一样,都指代零值或空值。nil 是预先说明的标识符,也即通常意义上的关键字。在 Golang 中,nil 只能赋值给指针、channel、func、interface、mapslice 类型的变量

另外,要注意的是,在 Golangstruct 是值类型,结构体作为参数时,是副本拷贝。如果想引用传值,加个 * 即可。