Golang

Go语言struct结构体详解

11-04 09:38

声明结构体

type 标识符 strcut{

字段 类型

field1 type

field2 type

...

}


结构体快速入门案

//定义一个结构体

type User struct{

Name string

Age int

Gender string

}

func main(){

//创建一个User变量,此时内存中已经分配空间,默认使用各类型的零值

var user User

fmt.Println("user=", user) //user=(0),string默认值为空串,int默认值为0,所以只打印一个0

user.Name = "张飞"  //赋值

user.Age = 30

user.Gender = "男"

fmt.Println("user=", user)

 

//使用类型推导,直接赋值

user2 := User{Name: "Alice", Age: 30, Gender: "女"}

fmt.Println("user2=", user2)

 

//使用类型推导,直接赋值,并省略字段

user3 := User{"Tom", 20, "男"}

fmt.Println("user2=", user2)

}

通过上面案例可以看出,结构体和结构体变量的区别和联系:

1、结构体是自定义的数据类型,代表一类事物

2、结构体变量(实例)是具体的、实际的,代表一个具体变量


结构体在内存中的布局


结构体字段基本介绍

从概念或叫法上看:结构体字段 = 结构体属性 = 结构体field

字段是结构体的一个组成部分,一般是基本数据类型、数组,也可以是引用类型


结构体字段注意事项和细节说明

1、声明字段的语法类似声明变量,比如:字段名 字段类型

2、字段的类型可以为:基本类型、数组或引用类型

3、在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),比如:布尔类型是false,数值是0,字符串是"",数组类型的默认值和它的元素类型相关(如score [3]int,零值为[0,0,0]),指针、slice、map的零值都是nil,即还没有分配空间(虽然输出不一样,实际都为nil)

//如果字段类型是指针、slice、map,它们的零值都是nil,即还没有分配空间(虽然输出不一样,实际都为nil)

type Person struct {

Name string

Age int

Email string

Hobby map[string]string

Score [3]float64

Slice []int

Ptr *int

}

//使用时,指针、slice、map的零值都是nil

person := Person{}

if person.Hobby == nil { fmt.Println("Map使用了零值nil") }

if person.Slice == nil { fmt.Println("Slice使用了零值nil") }

if person.ptr == nil { fmt.Println("指针使用了零值nil") }

4、不同结构体变量的字段是独立的,互不影响,一个结构体变量的字段发生变化,不影响另一个,结构体是值类型,比如:

注意:在Go语言中,结构体是值类型,直接指向数据空间(并没有先指向一个地址,再通过地址指向数据空间)

var stu1 = Stu{"Jack", 18}     //结构体:str1 => 结构体数据空间[xxx, xxx]

var stu2 = &Stu{"Tom", 18}  //结构体指针:str2 => 地址 => 结构体数据空间[xxx, xxx]


struct类型的内存分配机制


Golang严格区分大小写

结构体名称首字母如果是大写的,证明这个结构体可以被其它包使用

如果字段名字首字母是大写的,证明这个字段可以被其它包使用,小写的表示私有,只能在本包使用


创建结构体常用的方式

type User struct {

Name string

Age int

}

func main() {

//方式1 - 直接声明

var u1 User

u1.Name = "Tom"

u1.Age = 18

fmt.Println("u1 =", u1)

 

//方式2 - {}

var u2 = User{}  //或者 u2 := User{}

u2.Name = "Tom"

u2.Age = 18

fmt.Println("u2 =", u2)

 

//方式3

var u3 = User{Name:"Tom", Age:18}

fmt.Println("u3 =", u3)

 

//方式4:省略了字段名,但需要每次把所有字段都声明了,缺失字段会报错

var u4 User = User{"Tom", 18}

fmt.Println("u4 =", u4)

 

//方式5:比上面的方式4省去变量类型

var u5 = User{"Tom", 18}

fmt.Println("u5 =", u5)

 

//方式6

u6 := User{"Tom", 18}

fmt.Println("u6 =", u6)

 

//方式7 - *指针

var u7 *User = new(User)  //new(结构体)会得到一个结构体指针

//因为u7是一个指针,因此标准的给字段赋值方式

(*u7).Name = "Tom"

//有种简化写法,可以去掉指针变量前面的*,Go的设计者认为这样做,更符合程序员的使用习惯

//Go的编译器底层会对 u7.Age = 18 进行处理,给 u7 加上取值运算,转化为 (*u7).Age = 18

//所以,(*u7).Age 等价于 u7.Age,前者为标准写法,后者为简化写法

u7.Age = 18

//因为是指针,所以取出时会带一个&符号,告诉你这是指针,其它都一样

//如果要拿掉地址符号&,在前面加一个取值符号*,这样在输出时,就把结构体本身的数据空间拿到了

fmt.Println("u7 =", *u7)

 

//方式8 - &指针

var u8 *User = &User{} //得到一个结构体指针

(*u8).Age = 18 //标准写法

//结构体指针访问字段的标准方式应该是:(*结构体指针).字段名 ,比如 (*person).Name = "tom" 3) ,但 go 做了一个简化,也支持 “结构体指针.字段名”,比如:person.Name = "tom"。

u8.Name = "Tom" //简化写法

fmt.Println("u8 =", *u8)

 

//方式9 - &指针

//可以直接给字段赋值

var u9 *User = &User{"Tom", 18}  //u9 => 地址 => 结构体数据空间[xxx, xxx]

//因为是指针,所以取出时会带一个&符号,告诉你这是指针,其它都一样

//如果要拿掉地址符号&,在前面加一个取值符号*,这样在输出时,就把结构体本身的数据空间拿到了

fmt.Println("u9 =", *u9)

}


结构体中使用切片,两种方式

type Person struct{

Str string

Slice []int

}

func main(){

//方式一:直接赋值会报错,因为仅初始化了,并没有指向一个空间,需要先make

p1 := Person{}

p1.Slice = make([]int, 3)

p1.Slice[0] = 100

//方式二:省去slice的make,直接赋值

var p2 = Person{}

p2.Slice = []int{1, 2, 3}

}


结构体中使用map,两种方式

type Person struct{

Name string

Hobby map[string]string 

}

func main(){

//方式一:先make,再赋值

var p3 Person

p3.Hobby = make(map[string]string)

p3.Hobby["Bob"] = "bob"

//方式二:直接赋值

var p4 Person

p4.Hobby = map[string]string{"Alice": "alice", "Bob": "bob"}

}



结构体的注意事项和使用细节

1、结构体的所有字段在内存中是连续的,比如下面的分析图:


2、结构体是用户单独定义的类型,和其它类型(结构体)进行转换时,需要有完全相同的字段(名字、个数、类型)


3、当我们定义好一个结构体后,再次使用type进行重新定义,Golang认为这是新的数据类型,新旧两种类型的变量之间可以相互强转

正确的使用方法:使用类型强转

再举个例子,比如把int类型重新定义为integer后,Go认为这是两种类型

 正确的做法是:


4、struct的每个字段后面,可以加上一个tag,这个tag可以通过反射机制获取,常见的使用场景是序列化和反序列化(json)

下面是没有使用tag时的案例,struct转json后,字段首字母依然大写:

下是使用tag后,结构体的字段在转json时,可以被自定义(任意名称):


微信小程序
大潇博客 版权所有 Copyright ©2016~2026
京ICP备17004217号-6  合作QQ:284710375
天玺科技