小白都学会了结构体,你还不会?

Posted @了凡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小白都学会了结构体,你还不会?相关的知识,希望对你有一定的参考价值。

前言

在这里插入图片描述

博主介绍:

– 本人是了凡,意思为希望本人任何时候以善良为先,以人品为重,喜欢了凡四训中的立命之学、改过之法、积善之方、谦德之效四训,更喜欢每日在简书上投稿每日的读书感悟笔名:三月_刘超。专注于 Go Web 后端,辅学Python、Java、算法、前端等领域。



保姆级系列

小白篇:https://blog.csdn.net/weixin_45765795/article/details/117278889

进阶篇:https://blog.csdn.net/weixin_45765795/article/details/117257325


引言

go语言的结构体和反射与C还是非常接近的,但是因为go是既面向对象又面向过程的语言也许会与C有一部分不同


语言官方引言

很久以前,有一个IT公司,这公司有个传统,允许员工拥有20%自由时间来开发实验性项目。在2007的某一天,公司的几个大牛,正在用c++开发一些比较繁琐但是核心的工作,主要包括庞大的分布式集群,大牛觉得很闹心,后来c++委员会来他们公司演讲,说c++将要添加大概35种新特性。这几个大牛的其中一个人,名为:Rob Pike,听后心中一万个xxx飘过,“c++特性还不够多吗?简化c++应该更有成就感吧”。于是乎,Rob Pike和其他几个大牛讨论了一下,怎么解决这个问题,过了一会,Rob Pike说要不我们自己搞个语言吧,名字叫“go”,非常简短,容易拼写。其他几位大牛就说好啊,然后他们找了块白板,在上面写下希望能有哪些功能(详见文尾)。接下来的时间里,大牛们开心的讨论设计这门语言的特性,经过漫长的岁月,他们决定,以c语言为原型,以及借鉴其他语言的一些特性,来解放程序员,解放自己,然后在2009年,go语言诞生。


结构体

引言

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。

结构体之自定义类型

自定义结构体类型其实根本在做的就是一个像继承一样,将string、整型、浮点型、布尔等数据类型重新定义了一个新的名字
我们可以基于内置的基本类型定义,也可以通过struct定义。例如:

 // 将 ParcelInt定义为 int类型
    type ParcelInt int

现在就可以看到通过 type关键字定义, int 类型的所有特性现在 ParcelInt 就都有了

结构体之类型别名

类型别名:简单点就是说每个人身份证上就相当于Int类型,然而每个人家里从小都有有另一个乳名或者小名、但是叫这个人的小名或者乳名就是喊得同一个人

定义格式:

type TypeAlias = Type

runebyte就是类型别名,他们的定义如下:

type byte = uint8
type rune = int32

但是大致一看结构体自定义类型和类型别名没什么区别,唯一不同就是一个是定义,一个是赋值符号=

类型别名和类型自定义有哪些区别?

来一段代码实验一下看看输出结果,你瞬间秒懂

代码实验:

package main

import "fmt"

type name1 int

type name2 = int

func main() {
	var n1 name1
	var n2 name2

	fmt.Printf("name1 of a:%T\\n", n1)
	fmt.Printf("name2 of a:%T\\n", n2)
}

输出结果:
在这里插入图片描述

总结:可以清楚的看到自定义类型打印的类型是name1类型,而类型别名打印还是原类型名字,说明你不管有多少个名字计算机只认你原本的名字

结构体详讲

Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。Go语言中通过struct来实现面向对象

结构体定义

使用type关键字定义struct结构体类型

定义格式:

type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}
  • 类型名:标识自定义结构体的名称,在同一个包内不能重复。
  • 字段名:表示结构体字段名。结构体中的字段名必须唯一。
  • 字段类型:表示结构体字段的具体类型。

假如我们定义一个学生结构体

第一种写法

type Student struct {
	name string    // 学生姓名
	age int		   // 学生年龄
	class string   // 学生班级
	number int     // 学生学号
}

第二种写法

同样类型的字段可以写在一行,例如:

type Student struct {
	name, class string  // 学生姓名和班级
	number, age int     // 学生学号和年龄
}

这样看是不是简洁许多呢?

总结:结构体在go语言中是一个比例很重的基础部分,以后在项目中会经常用到,后序繁衍也会比较多
功能:目前来看内置的基础数据类型是用来描述一个值的,而结构体是用来描述一组值的,这样使用起来更加方便

结构体实例化

结构体实例化时,是为了真正的分配内存,也是实例化后才可以使用你定义结构体的字段,当然结构体本身也是一种类型

var 结构体实例 结构体类型

基本实例化

两种实例化方法

package main

import "fmt"

type Student struct {
	name, class string
	age, number int
}

func main() {

	// 第一种
	var instantiation1 Student
	instantiation1.name = "张三"
	instantiation1.class = "计科4班"
	
	// 第二种
	instantiation2 := new(Student)
	instantiation2.age = 18
	instantiation2.number = 20191515101

	fmt.Println("第一种:",instantiation1)
	fmt.Println("第二种:",instantiation2)
}

输出结果:
在这里插入图片描述
我们通过.来访问结构体的字段(成员变量),例如:instantiation1.name

但是这两种实例化有哪些不同呢?

仔细看第一种和第二种的返回结果
在这里插入图片描述

总结:我们可以看到第一种返回的是一个结构体
而第二种返回的是实例化的对象的指针

当然还有第三种实例化方法,返回的也是对象的指针
代码实验:

package main

import "fmt"

type Student struct {
	name, class string
	age, number int
}

func main() {
	var instantiation1 Student
	instantiation1.name = "张三"
	instantiation1.class = "计科4班"

	instantiation2 := new(Student)
	instantiation2.age = 18
	instantiation2.number = 20191515101

	instantiation3 := &Student{}
	instantiation3.name = "李四"
	instantiation3.class = "计科4班"
	instantiation3.age = 18
	instantiation3.number = 20191515101

	fmt.Println("第一种:",instantiation1)
	fmt.Println("第二种:",instantiation2)
	fmt.Println("第三种:",instantiation3)
}

输出结果:
在这里插入图片描述

匿名结构体

匿名数据结构:简单讲就是随用随声明,而且声明的结构体是没有名字的,具体接下来详细介绍吧

例如:

package main

import "fmt"

func main() {
	var user struct{name string; age, number int}
	user.name = "张三"
	user.age = 20
	user.number = 20191515101
	fmt.Printf("%v\\n", user)
}

输入结果:
在这里插入图片描述

创建指针结构体

指针结构体就是上述描述的那是那三种方法其中的两种

第一种就是创建指针结构体:

package main

import "fmt"

type Student struct {
	name string
	age int
}

// 创建指针类型结构体通过new关键字对结构体进行实例化,得到的是结构体的地址。
func main() {
	inflection1 := new(Student)
	inflection1.name = "张三"
	inflection1.age = 20
	fmt.Printf("创建指针类型实例化:%v\\n", inflection1)
}


输出结果:
在这里插入图片描述

第二种就是取结构体的地址实例化:

package main

import "fmt"

type Student struct {
	name string
	age int
}

// 创建指针类型结构体通过new关键字对结构体进行实例化,得到的是结构体的地址。
func main() {
	inflection2 := &Student{}
	inflection2.name = "张三"
	inflection2.age = 20
	fmt.Printf("取结构体的地址实例化:%v\\n", inflection2)
}

输出结果:
在这里插入图片描述

两种结构体初始化

没有初始化的结构体,其成员变量都是对应其类型的零值。

案例:

package main

import "fmt"

type Student struct {
	name string
	age int
}

// 创建指针类型结构体通过new关键字对结构体进行实例化,得到的是结构体的地址。
func main() {
	var initialize Student
	fmt.Printf("初始化结构体:%v\\n", initialize)
}

输出结果:
在这里插入图片描述

第一种:值的列表初始化

例如:

package main

import "fmt"

type Student struct {
	name string
	age int
}

// 列表初始化结构体就是可以不写键,只用写值
func main() {
	initialize := &Student{
		"张三",
		20,
	}
	fmt.Printf("列表初始化:%v\\n", initialize)
}

输出结果:
在这里插入图片描述
总结:这种初始化有三个弊端:
第一:必须在结构体中把所有键对应的值全部填上
第二:必须与原结构体中的键值对的顺序一致
第三:该方法不可以键值对初始化方法一起混用

第二种:键值队初始化的三种方法

例如:

package main

import "fmt"

type Student struct {
	name string
	age int
}

// 键值对初始化方法
func main() {
	// 第一种
	initialize1 := Student{
		name:"张三",
		age: 20,
	}
	fmt.Printf("键值对初始化方法1:%v\\n", initialize1)
	// 第二种
	initialize2 := &Student{
		name:"张三",
		age: 20,
	}
	fmt.Printf("键值对初始化方法2:%v\\n", initialize2)
	// 第三种
	initialize3 := &Student{
		age: 20,
	}
	fmt.Printf("键值对初始化方法3:%v\\n", initialize3)
}

输出结果:
在这里插入图片描述

总结:可以看到与列表正好相反,列表初始化方法那三个限制咋键值对里完全没有,也可以说更加灵活了。

结构体内存布局

在结构体中是占据一块连续的内存空间,同样空的结构体不占用****内存空间

连续的内存空间例如:

package main

import "fmt"

type Student struct {
	one int8
	two int8
	three int8
	four int8
}

// 查看空间是否连续
func main() {
	initialize := Student{
		1,2,3,4,
	}
	fmt.Printf("one:%p\\n", &initialize.one)
	fmt.Printf("two:%p\\n", &initialize.two)
	fmt.Printf("three:%p\\n", &initialize.three)
	fmt.Printf("four:%p\\n", &initialize.four)
}

输出结果:
在这里插入图片描述
空结构体例如:

package main

import (
	"fmt"
	"unsafe"
)

type Student struct {
}

// 查看空结构体内存大小
func main() {
	initialize := Student{}
	fmt.Println("结构体长度:", unsafe.Sizeof(initialize))
}

输出结果:
在这里插入图片描述

构造函数

Go语言的结构体没有构造函数,我们可以自己实现。 例如,下方的代码就实现了一个person的构造函数。 因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。

例如:

package main

import "fmt"

type student struct {
	name string
	age  int
}

func main() {
	p9 := newPerson("张三", 20)
	fmt.Printf("%#v\\n", p9)
}
func newPerson(name string, age int) *student {
	return &student{
		name: name,
		age:  age,
	}
}

输出结果:
在这里插入图片描述

面试题

请问下面代码执行输出多少?

type student struct {
	name string
	age  int
}

func main() {
	m := make(map[string]*student)
	stus := []student{
		{name: "小王子", age: 18},
		{name: "娜扎", age: 23},
		{name: "大王八", age: 9000},
	}

	for _, stu := range stus {
		m[stu.name] = &stu
	}
	for k, v := range m {
		fmt.Println(k, "=>", v.name)
	}
}

输出结果:
在这里插入图片描述


后序问题

结构体可以实现继承么?

结构体的泛型该如何实现?

欢迎各位大佬评论区中留言哦


这次就先讲到这里,如果想要了解更多的golang语言内容一键三连后序每周持续更新!

以上是关于小白都学会了结构体,你还不会?的主要内容,如果未能解决你的问题,请参考以下文章

在wincc中,我已经定义了结构体变量(有浮点型,整型,字符,文本类),c脚本中取值时需要再重新定

C语言中定义了结构体一定要先进行初始化么?如果不初始化直接使用会有啥后果?

go——结构

不会用github?全中文CSDN代码托管平台值得你拥有!手把手教你学会使用!!

结对编程代码互评

PacBio的异构体测序(Iso-Seq)