Go语言可以通过自定义的方式形成新的类型,结构体就是这些类型中的一种复合类型,结构体是由零个或多个任意类型的值聚合成的实体,每个值都可以称为结构体的成员。
关键词
-
定义结构体
普通结构体
-
匿名结构体
- 提升字段
结构体嵌套
结构体外部访问
结构体比较
-
结构体和结构体指针
使用
方法调用
作为参数使用
make和new
|
定义结构体
普通结构体
//常规结构体定义 ,字段名称+ 类型
type Info struct {
name string
id int
}
匿名结构体
//匿名结构体定义
type Info2 struct {
id int
Info // Info结构体内部的字段又叫提升字段(我觉得叫啥无所谓)
}
//此处允许名称相同,并且数据结构不同的字段。
//访问时遇到名称相同的字段优先访问外层
var info_new = Info2{1,Info{"lisi",3}}
log.Println(info_new.id) // 1
log.Println(info_new.Info.id) // 3
log.Println(info_new.name) // lisi 可作为外层成员直接访问
//如果出现多层匿名嵌套情况,无论哪层的不重复的成员都可以作为最外层访问
//在结构体中属于匿名结构体的字段称为提升字段,因为它们可以被访问,就好像它们属于拥有匿名结构字段的结构一样。
type Info3 struct {
name3 string
id3 int
Info2
}
var info_new = Info3{"王二麻子", 3,Info2{3 , Info{"张三",3}}}
log.Println(info_new. name) // 张三 可作为外层成员直接访问
//强调 :如果出现多层匿名嵌套情况,无论哪层的不重复的成员都可以作为最外层访问 但不建议这么搞,因为你这么写了我肯定看不懂
结构体嵌套
//结构体嵌套
type Info2 struct {
id int
info Info
}
//就是给匿名的结构体加一个变量名称
info_new = Info2{3 , Info{"张三",3}}
var info_new = Info2{
id:3 ,
Info:Info{"张三",3},
}
结构体外部访问
//其实外部访问的问题,跟方法一样当你的结构体名称大写开头的时候可以被其他包访问
//当你的成员变量大写开头时可以被外部包访问
type Info struct { //-> y
Id int //-> y
Name string //-> y
name2 string //-> no
}
type info2 struct { //->no
Id int //->y
name string //->no
}
//Info 应该好理解,那么什么时候使用 info2 的id呢 ?
//我们看一个比较贱的例子
//在包里定义一个返回info2 的方法
func Create() info2 {
return info2{12,"张三"}
}
//然后在main函数里
package main
import (
"golangany/GoStruct"
"log"
)
func main() {
log.Println(GoStruct.Create().Id) // 12
}
//备注 但是这里如果是直接打印结构体
log.Println(GoStruct.Create()) // {12 张三}
结构体比较
//结构体比较
//1\. 比较的前提是结构体的成员都是值类型的
type Info struct {
id int
name string
}
var a = Info{1,"zhangsan"}
var b = Info{1,"zhangsan"}
log.Println(a==b) //true
//非值类型比较 --------------错误示范
image
当我想尝试另一个骚操作的时候
image
结构体和结构体指针
简单使用
//结构体和结构体指针
//简单使用
type Info struct {
id int
name string
}
var a = Info{12,"张三"}
a.id =13
log.Println(&a ,a)
(&a).id = 15
log.Println(&a ,a)
//当初始化完局部变量的结构体对象,和结构体指针的效果是一致的,都可以改变当前对象的属性
//参数传递
package main
import "log"
type Info struct {
id int
name string
}
func StructSet( i Info) {
i.id = 50
}
func StructSet2( i *Info) {
i.id = 50
}
func main() {
i := Info{id: 12,name: "张三"}
StructSet(i)
log.Println(i) // {12 张三}
StructSet2(&i)
log.Println(i) // {50 张三}
}
//所以当传递结构体指针作为变量的时候,是可以修改原结构体对象的值
//结构体方法调用
package main
import "log"
type Info struct {
id int
name string
}
func ( i Info)StructSet() { // == StructSet( i Info)
i.id = 50
}
func( i *Info) StructSet2() {//== StructSet2( i *Info)
i.id = 50
}
func main() {
i := Info{id: 12,name: "张三"}
i.StructSet()
log.Println(i) // {12 张三}
i.StructSet2() //== (&i).StructSet2()
log.Println(i) // {50 张三}
}
//注意这里 在这里是不是用 & 效果是一样的
func create() (*Info){
i := Info{id: 12,name: "张三"}
return &i
}
func main() {
i2 := create()
i2.StructSet()
log.Println(i2)
i2.StructSet2()
log.Println(i2)
}
//我们这里使用一个新的方式来调用看看
//效果其实是一样的 ,那么使用 结构体指针和 传值方式调用的区别是什么呢
//我觉得
//1\. StructSet2( i *Info) 调用方需要关注传入的是指针,还是结构体本身
//2\. i2.StructSet() 这种方式传值时,由方法来控制我使用的是指针还是结构体副本,调用方不感知
//3\. 个人觉得 如果你的方法中只有读取操作尽量使用 不带指针的函数写法,避免误操作导致修改数组,增加代码安全性
make和new
make和new都是golang用来分配内存的內建函数,且在堆上分配内存,make 即分配内存,也初始化内存。new只是将内存清零,并没有初始化内存。
make返回的还是引用类型本身;而new返回的是指向类型的指针。
make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。
没想到吧我先看下var
//开始之前先说下 var 关键字吧
package main
import (
"fmt"
"log"
"unsafe"
)
type Info struct {
id int
name string
}
func main() {
var a_int int
fmt.Printf("a_int的数据类型是%T,a的字节大小是%d 开始地址 %p \n",a_int,unsafe.Sizeof(a_int),&a_int)
var b_int []int
fmt.Printf("b_int的数据类型是%T,a的字节大小是%d 开始地址 %p \n",b_int,unsafe.Sizeof(b_int) ,&b_int)
b_int = append(b_int, 1,2,3,4,5)
fmt.Printf("b_int的数据类型是%T,a的字节大小是%d 开始地址 %p \n",b_int,unsafe.Sizeof(b_int),&b_int)
log.Println(b_int)
var c_int [4]int
fmt.Printf("c_int的数据类型是%T,a的字节大小是%d 开始地址 %p \n",c_int,unsafe.Sizeof(c_int),&c_int)
var d_int map[string]int
fmt.Printf("d_int的数据类型是%T,a的字节大小是%d 开始地址 %p \n",d_int,unsafe.Sizeof(d_int),&d_int)
var e_int Info
fmt.Printf("e_int的数据类型是%T,a的字节大小是%d 开始地址 %p \n",e_int,unsafe.Sizeof(e_int),&e_int)
}
//a_int的数据类型是int,a的字节大小是8 开始地址 0xc00001e0a8
//b_int的数据类型是[]int,a的字节大小是24 开始地址 0xc00000c030
//b_int的数据类型是[]int,a的字节大小是24 开始地址 0xc00000c030
//2022/03/05 16:26:30 [1 2 3 4 5]
//c_int的数据类型是[4]int,a的字节大小是32 开始地址 0xc0000160c0
//d_int的数据类型是map[string]int,a的字节大小是8 开始地址 0xc00000e030
//e_int的数据类型是main.Info,a的字节大小是24 开始地址 0xc00000c078
首先确定一个事情就是,var 声明变量后并给其分配了内存空间,无论最后你有没有赋值,当然我觉得这块跟make还是new没关系哈哈哈
Make和new
差异1 make用于内建类型(map、slice 和channel)的内存分配
差异2 返回数据还是返回指针
差异3 make可以给定长度并且赋初始值
package main
import "log"
func main() {
//make用于内建类型(map、slice 和channel)的内存分配
//本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针、长度和容量的三项描述符;
//不知道是不是说,这三种数据结构本质是个结构体,使用的前提是必须要将里面主要的值进行初始化,也就是说可能在内部实现的时候 new(channl) 然后给它赋值了
a := make(map[int]int)
log.Println(a) //2022/03/05 16:39:05 map[]
c := make([]int ,4)
log.Println(c) // [0 0 0 0] 可赋值 0 "" 以及 nil
b := new(map[int]int)
log.Println(b) //2022/03/05 16:39:05 &map[]
}
//可以看出 new返回了数据指针 ,而make返回了数据