(Go语言)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法

0. 序言

从这章开始,在Go基础语法里难度就开始上来了
在学习函数与方法前,先弄明白指针是很重要的。

1. 指针

在没学指针前,相信很多人就已经大概知道指针是个什么东西了。因为它太有名了,当然是与 C和C++ 的出名有关。

1.1 指针的含义

Go保留的指针,在一定程度上保证了性能,同时为了更好的GC和安全考虑,又限制了指针的使用

  1. 指针属于基本数据类型,遍历存的就是值,也叫做值类型

  2. 获取遍历的地址,使用 &

    比如:var num int,想要获取num遍历的地址,使用 &num

  3. 获取指针类型所指向的值,使用:*

    比如:var *t int,使用 *p 获取 p 指向的值

    指针类型,指针变量存的是一个地址,这个地址指向的空间的才是值

    var p *int = &num

    这行代码的意思就是说,p变量指针,指向了num变量的地址的值

    1. p是一个指针变量
    2. p指针变量的类型是 *int 类型
    3. p本身的值 &i
var i int = 10
var p *int = &i // 指向的变量类型需与指针类型一致
fmt.Println(&i) // &i代表的就是i变量的内存地址:0xc00000a0e8
fmt.Println(p)  // 这里p变量的值就是&i:0xc00000a0e8

!指针必须指向某个变量的地址!

指针变量同样也是有地址的

// 输出 p 指针变量的内存地址
fmt.Println(&p) // 0xc000110048
  • 这是在内存中,指针指向的空间关系

在这里插入图片描述

指针变量会在内存中开辟一块空间(地址:0xc000110048),然后在这个空间中存放的是某变量的内存地址(0xc00010c098)通常这种写法就是:&变量名。

而想要获取指针变量的值,其实也就是指向的变量内容

// 获取 p 指针变量的值
fmt.Println(*p) // 10

它获取值的流程:

  1. 先获取到p指针变量的内存地址 0xc000110048
  2. 去这个0xc000110048内存地址中找存放的值0xc00010c098
  3. 然后由于它是指针变量,所以还需要根据这个值0xc00010c098 再来内存中查找对应的内存地址
  4. 根据值0xc00010c098找到对应的内容10。

1.2 用法

关于指针有两个常用的操作符,一个是取地址符&,另一个是解引用符*。对一个变量进行取地址,会返回对应类型的指针,例如:

func main() {
   num := 2
   p := &num
   fmt.Println(p) // 指针存储的是变量num的地址 0xc00001c088
}

解引用符则有两个用途,第一个是访问指针所指向的元素,也就是解引用,例如

func main() {
	num := 2
	p := &num
	rawNum := *p
	fmt.Println(rawNum)
}

p是一个指针,对指针类型解引用还有一个用途就是声明一个指针

func main() {
   var numPtr *int
   fmt.Println(numPtr) // <nil>
}

*int即代表该变量的类型是一个int类型的指针,不过指针不能光声明,还得初始化,需要为其分配内存,否则就是一个空指针,无法正常使用

要么使用取地址符将其他变量的地址赋值给该指针,要么就使用内置函数new手动分配,例如:

func main() {
   // var numPtr *int
   // numPtr = new(int)
   numPtr := new(int) // 更推荐写法
   fmt.Println(numPtr)
}

new函数只有一个参数那就是类型,并返回一个对应类型的指针,函数会为该指针分配内存,并且指针指向对应类型的零值,例如:

func main() {
   fmt.Println(*new(string)) // 
   fmt.Println(*new(int))// 0
   fmt.Println(*new([5]int))// [0 0 0 0 0]
   fmt.Println(*new([]float64))// []
}

1.3 禁止指针运算

在Go中是不支持指针运算的,也就是说指针无法偏移

func main() {
   arr := [5]int{0, 1, 2, 3, 4}
   p := &arr
   println(&arr[0])
   println(p)
   // 试图进行指针运算
   p++ // main.go:10:2: invalid operation: p++ (non-numeric type *[5]int)
   fmt.Println(p)
}

虽然正常时不支持指针运算的,但是你可以引入标准库unsafe来完成运算

标准库unsafe提供了许多用于低级编程的操作,其中就包括指针运算。

1.4 new 和 make

在前面已经很多次提到过内置函数newmake,两者有点类似,但也有不同,下面复习下

func new(Type) *Type
  • 返回值是类型指针
  • 接受参数是类型
  • 专用于给指针分配内存空间
func make(t Type, size ...IntegerType) Type
  • 返回值是值,不是指针
  • 接受的第一个参数是类型,不定长参数根据传入类型的不同而不同
  • 专用于给切片,映射表,通道分配内存

例子:

new(int) // int指针
new(string) // string指针
new([]int) // 整型切片指针
make([]int, 10, 100) // 长度为10,容量100的整型切片 
make(map[string]int, 10) // 容量为10的映射表
make(chan int, 10) // 缓冲区大小为10的通道

1.5 属于指针的使用陷阱

var a int = 300
var b int = 400
var p *int = &a
*p = 100
p = &b
*p = 200
fmt.Printf("a = %d, b = %d, *p = %d\n", a, b, *p) // a = 100, b = 200, *p = 200

这里*p的其实指向的就是变量a的内容,所以,给*p重新赋值,就意味着给a变量重新赋值

值类型,都有对应的指针类型,形式为 *数据类型,比如int的对应的指针就是*intf,loat32对应的指针类型就是 *float,以此类推

值类型包括:基本数据类型int系列、float系列、bool、string、数组和结构体struct

2. 结构体

2.1 介绍

结构体可以存储一组不同类型的数据,是一种符合类型。

Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。下面是一个简单的结构体的例子

OOP:面向对象编程

2.2 声明

type Result struct {
	code int
	msg  string
	data any
}

结构体本身以及内部字段都遵守大小写命名的暴露方式。

同时,相邻字段也可以不用重复声明数据类型

type Infomation struct {
	name, address     string
	age, schoolNumber string
}

在声明结构体字段时,字段名不能与方法名重复

2.3 实例化

Go不存在构造方法,大多数情况下采用如下的方式来实例化结构体,初始化的时候就像map一样指定字段名称再初始化字段值

result := Result{
    code: 1,
    msg:  "好人",
    data: true,
}
fmt.Println(result)

不过也可以省略字段名称,当省略字段名称时,就必须初始化所有字段。

通常不建议使用这种方式,因为可读性很糟糕

2.4 指针

对于结构体指针而言,不需要解引用就可以直接访问结构体的内容,例子如下:

p := &Person{
   name: "jack",
   age:  18,
}
fmt.Println(p.age,p.name)

在编译的时候会转换为(*p).name(*p).age,其实还是需要解引用,不过在编码的时候可以省去,算是一种语法糖。

2.5 标签

结构体标签是一种元编程的形式,结合反射可以做出很多奇妙的功能

`key1:"val1" key2:"val2"`
type Programmer struct {
    Name     string `json:"name"`
    Age      int `yaml:"age"`
    Job      string `toml:"job"`
    Language []string `properties:"language"`
}

2.6 空结构体

空结构体没有字段,不占用内存空间,我们可以通过unsafe.SizeOf函数来计算占用的字节大小

func main() {
   type Empty struct {}
   fmt.Println(unsafe.Sizeof(Empty{}))// 0
}

空结构体的使用场景有很多,比如之前提到过的,作为map的值类型,可以将map作为set来进行使用,又或者是作为通道的类型,表示仅做通知类型的通道

3. 函数

3.1 声明

func 函数名([参数列表]) [返回值]{
	函数体
}

函数签名由函数名称,参数列表,返回值组成

声明函数的有两种办法,一种是通过func关键字直接声明,另一种就是通过var关键字来声明

函数名称为Sum,有两个int类型的参数ab,返回值类型为int

func sum1(a int, b int) int {
	return a + b
}

var sum = func(a int, b int) int {
	return a + b
}

在Go中,方法是没有重载这一说的

Go的理念:如果签名不一样那就是两个完全不同的函数,那么就不应该取一样的名字,函数重载会让代码变得混淆和难以理解。

type Person struct {
	Name    string
	Age     int
	Address string
	Salary  float64
}

func NewPerson(name string, age int, address string, salary float64) *Person {
	return &Person{Name: name, Age: age, Address: address, Salary: salary}
}
// 不可以重载
func NewPerson(name string) *Person {
	return &Person{Name: name}
}

3.2 参数

Go中的参数名可以不带名称,一般这种是在接口或函数类型声明时才会用到,不过为了可读性一般还是建议尽量给参数加上名称

type ExWriter func(io.Writer) error 

type Writer interface {
	ExWrite([]byte) (int, error)
}

对于类型相同的参数而言,可以只需要声明一次类型,不过条件是它们必须相邻

同时:变长参数可以接收0个或多个值,但必须声明在参数的末尾

func main() {
	a := test(2, 4, 6)
	fmt.Println(a)
    b, c := test1(3, 5, 1, 1, 5, 77, 111)
	fmt.Println(b, c) // 239 测试
}

func test(n1, n2, n3 int) int {
	return n1 + n2
}

/*
当存在边长参数,那么该参数会是一个切片
*/
func test1(n1 int, n3 ...int) (int, string) {
	a := 0
	for i := range n3 {
		n1++
		a += n3[i] + n1
	}
	return a, "测试"
}

Go中的函数参数是传值传递,即在传递参数时会拷贝实参的值。

因为这两个数据结构本质上都是指针,所以不用担心在传递时赋值大量内存

3.3 返回值

  • 一个简单的函数返回值的例子,Sum函数返回一个int类型的值。

  • 而当函数没有返回值时,不需要void,不带返回值即可。

  • Go允许函数有多个返回值,此时就需要用括号将返回值围起来

func main() {
	sum_1(1, 2)
	test2()
}

/*
相加函数,返回一个int类型
*/
func sum_1(a, b int) int {
	return a + b
}
/*
测试函数,不返回任何的值
*/
func test2() {
	fmt.Println("....")
}

/*
测试函数,返回两个变量
*/
func test1(n1 int, n3 ...int) (int, string) {
	a := 0
	for i := range n3 {
		n1++
		a += n3[i] + n1
	}
	return a, "测试"
}

Go也支持具名返回值,不能与参数名重复,使用具名返回值时,return关键字可以不需要指定返回哪些值。

/*
确认某个变量作为返回值
*/
func test3() (num int) {
	num = 10
	return
}

/*
确认多个变量作为返回值
*/
func test4() (num, age int) {
	num = 10
	age = 50
	return
}

其实,返回的那段代码(num, age int)就已经声明了返回的变量,所以无需指定使用return返回固定的参数了

但是不管具名返回值如何声明,永远都是以return关键字后的值为最高优先级

3.4 匿名函数

匿名函数就是没有签名的函数,例如下面的函数func(a, b int) int,它没有名称,所以我们只能在它的函数体后紧跟括号来进行调用。

a := func(a, b int) int {
    return a + b
}(1, 2)
fmt.Println(a)// 3

这种的话,还可以配合方法套方法,反正也是返回一个参数,那么哪里需要参数,哪里就能用这种方法

如果只函数只是使用一次,可以考虑使用匿名函数,不过匿名函数也可以实现多次调用

复杂例子:

package main

import "fmt"

func main() {
	var num1, num2 int

	fmt.Println("------第一个数字参数")
	fmt.Scanln(&num1)
	fmt.Println("------第二个数字参数")
	fmt.Scanln(&num2)

	var data = test5(num1, num2, func() (b string) {
		fmt.Println("------请输入填写字符")
		fmt.Scanln(&b)
		fmt.Println("------得到值:", b)
		return
	}())
	fmt.Println("最终生成对象结果:", data)

}

type Result struct {
	Code    int
	Message string
	Data    any
}
func test5(a, b int, s string) Result {
	result := Result{}

	num := a + b
	if num >= 0 {
		result.Code = 200
		result.Message = s + " 进入0大于等于判断"
		result.Data = num
	} else {
		result.Data = num
		result.Code = 500
		result.Message = s + " 相加值"
	}

	return result
}

这里复杂例子,就是使用了匿名函数,直接返回一个作为方法参数的变量

3.5 闭包

闭包(Closure)这一概念,在一些语言中又被称为Lamda表达式,与匿名函数一起使用,闭包 = 函数 + 环境引用

换言之:闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

这里 addAndPrint() 返回的参数是一个函数( func(string) int ),

func main() {
    // 由于 addAndPrint() 是个方法,所以这里使用变量来接受
	f1 := addAndPrint() // 这里用f1变量来接收返回的函数,所以那个匿名函数的名称就可以说是f1
	num1 := f1("第一次第一次")// 这里调用 f1 变量匿名函数
	fmt.Println(num1)
}

func addAndPrint() func(string) int {
	var n int = 10
    // 返回一个匿名函数
	return func(str string) int {
		n++
		fmt.Println("打印字符串:", str)
		return n
	}
}

3.6 init函数

每个源文件都可以包含一个init函数,该函数会在main函数执行前被Go调用

package main

import "fmt"

func init() {
	fmt.Println("init") // init
}

func main() {
}
  1. 如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程:全局变量定义》init函数》main函数
  2. ini函数最主要的作用,就是完成一些初始化的工作
  3. init函数执行的流程是怎么样的?或者说,当源文件存在互相的调用时,执行流程是怎样的?
    首先执行调用文件(调用关系中最上级的文件)中的执行流程,然后再执行被调用文件的执行流程

3.7 延迟调用

defer关键字可以使得一个函数延迟一段时间调用,在函数返回之前这些defer描述的函数最后都会被逐个执行

func main() {
	test6()
}
func test6() {
	// defer 关键字 延迟函数的触发时间
	defer func() {
		fmt.Println("1")
	}()
	fmt.Println("2")
}

//2
//1

虽然没有明令禁止,一般建议不要在for循环中使用defer

func main() {
	n := 5
	for i := range n {
		defer fmt.Println(i)
	}
}

这段代码结果是正确的,但过程也许不对。在Go中,每创建一个defer,就需要在当前协程申请一片内存空间。假设在上面例子中不是简单的for n循环,而是一个较为复杂的数据处理流程,当外部请求数突然激增时,那么在短时间内就会创建大量的defer,在循环次数很大或次数不确定时,就可能会导致内存占用突然暴涨,这种我们一般称之为内存泄漏。

对于defer直接作用的函数而言,它的参数是会被预计算的,这也就导致了上述这种奇怪现象,对于这种情况,尤其是在延迟调用中将函数返回值作为参数的情况尤其需要注意。

4. 方法

方法与函数的区别在于,方法是有接收者的。

type M map[int]string

func (arr M) SetThis(i int, v string) {
    arr[i] = v
}
func (arr M) Get(i int) string {
	return arr[i]
}

func main() {
	var a M = make(map[int]string)
	M.SetThis(a, 1, "张三")
	M.SetThis(a, 3, "李四")
	M.SetThis(a, 4, "王五")

	a.SetThis(5, "赵六") // 推荐
	fmt.Println(a)
	fmt.Println(a.Get(3))
}

上部分代码其实可以看成是(Java角度):

  1. 新建了一个M类
  2. 为M类创建了一个静态的SetThis()方法
  3. 创建的a对象继承自M类
  4. 所以a对象可以调用方法,使用M类也可以调用方法

5. 😍前篇知识回顾

  1. Go的环境安装与开发工具配置
  2. Go的运行流程步骤与包的概念
  3. (Go)变量与常量?字面量与变量的较量!
  4. 初上手Go?本篇文章帮拿捏Go的数据类型!
  5. (Go语言)条件判断与循环?切片和数组的关系?映射表与Map?三组关系傻傻分不清?本文带你了解基本的复杂类型与执行判断语句

6. 💕👉 其他好文推荐

  • 还不了解Git分布式版本控制器?本文将带你全面了解并掌握
  • 带你认识Maven的依赖、继承和聚合都是什么!有什么用?
  • 2-3树思想与红黑树的实现与基本原理
  • !全网最全! ElasticSearch8.7 搭配 SpringDataElasticSearch5.1 的使用
  • 全面深入Java GC!!带你完全了解 GC垃圾回收机制!!
  • 全面了解Java的内存模型(JMM)!详细清晰!
  • 在JVM中,类是如何被加载的呢?本篇文章就带你认识类加载的一套流程!

全文资料学习全部参考于:Golang中文学习文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/912115.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于redis实现API接口访问次数限制

一&#xff0c;概述 日常开发中会有一个常见的需求&#xff0c;需要限制接口在单位时间内的访问次数&#xff0c;比如说某个免费的接口限制单个IP一分钟内只能访问5次。该怎么实现呢&#xff0c;通常大家都会想到用redis&#xff0c;确实通过redis可以实现这个功能&#xff0c…

实在智能受邀出席柳州市智能终端及机器人产业发展合作大会

10 月 27 日至 28 日&#xff0c;由中共柳州市委员会与柳州市人民政府主办的2024柳州市智能终端及机器人产业发展合作大会在柳州莲花山庄隆重举行。大会充分整合各方资源&#xff0c;持续深化与柳州在重大战略规划、重大平台建设、重点产业培育等领域的合作。作为智能体行业的知…

JDBC-PreparedStatement

在前面使用的Statement中&#xff0c;编写sql语句使用的是拼接的形式&#xff0c;这样不仅可读性差&#xff0c;还非常容易导致出错&#xff0c;最大的问题是安全问题。 sql注入 在需要用户输入的地方&#xff0c;用户输入的是SQL语句的片段&#xff0c;最终用户输入的SQL片段…

如何创建备份设备以简化 SQL Server 备份过程?

SQL Server 中的备份设备是什么&#xff1f; 在 SQL Server 中&#xff0c;备份设备是用于存储备份数据的物理或逻辑介质。备份设备可以是文件、设备或其他存储介质。主要类型包括&#xff1a; 文件备份设备&#xff1a;通常是本地文件系统中的一个或多个文件。可以是 .bak 文…

非计算机背景但是想从事医学AI研究,需要掌握的编程语言|个人观点·24-11-08

小罗碎碎念 目前&#xff0c;我们从事医学AI研究的&#xff0c;接触的最多的两种编程语言应该就是R和Python了。那么初学者很容易提出一个疑问&#xff0c;**我想从事医学AI相关的研究的话&#xff0c;应该学哪些编程语言呢&#xff1f;**在文章的开头&#xff0c;我可以先给出…

Jmeter基础篇(21)教你手动修改Jmeter测试报告和压测结果

哈喽呀各位小伙伴!今天给大家带来一期关于Jmeter黑科技的教学! 在日常性能测试过程中,我们经常使用JMeter这个强大的工具来执行压力测试,并通过JMeter的报告生成命令,从CSV或JTL文件中读取数据,生成HTML格式的测试报告。然而,测试报告生成之后,数据就是固定的了,很多…

AHB Matrix 四星级 验证笔记(2.4) Tt3.3AHB总线协议测试时的 并行数据

文章目录 前言一、代码二、错误1.地址范围2. 并行执行线程中变量覆盖的情况3.有关incr的beat 前言 来源路科验证本节搞定 T3.3 AHB总线协议的覆盖&#xff1a;AHB_PROTOCOL_COVER 即测试ahb slave接口和master接口支持&#xff08;尽可能&#xff09;全部的ahb协议传输场景&am…

IDA*算法 Power Calculus————poj 3134

目录 闲聊 前言 DFS算法的无效搜索 BFS算法的空间浪费 IDDFS A*算法 IDA* Power Calculus 问题描述 输入 输出 问题分析 代码 闲聊 前几周在忙着数学竞赛&#xff0c;所以就没时间更新&#xff0c;高等数学&#xff0c;一生之敌&#xff0c;真不知道报名的时候我是怎么想…

权限管理简单练习

1.修改/tmp的权限改为 rwxrwxrwx 2.添加SUID权限到/tmp 3.添加SBIT权限到/tmp目录 4. 使用rhel创建 /tmp/123.txt 5.使用其他非root账号删除 /tmp/123/txt 能否执行成功 6.普通用户除了使用sudo可以执行poweroff以外&#xff0c;还有什么别的办法可以执行poweroff

uni-app 图标库整合最佳实践:使用 iconfont 构建属于自己的图标库

一. 前言 在前端开发中&#xff0c;图标已经成为页面设计中不可或缺的一部分。图标可以使界面更加美观、清晰&#xff0c;并且能够提升用户体验。而使用图标库来管理和引用图标资源&#xff0c;可以带来更多的便利和效率。 而在众多的图标库中&#xff0c;iconfont 独树一帜。…

课程讲解--深入探究二分算法

一、二分查找算法的基本概念 定义与原理 二分查找&#xff0c;也被称为折半查找&#xff0c;是一种在有序数据集合中查找特定元素的高效算法。其原理基于分治思想&#xff0c;每次查找都将查找区间缩小一半。例如&#xff0c;在一个有序数组中查找一个特定的数字&#xff0c;我…

达梦数据库DM Exception字符串截断错误,略坑~

前言 我之前在使用达梦数据库的时候&#xff0c;遇到了很多很多的问题&#xff0c;主要对达梦数据库也不是很熟悉&#xff0c;它的语法和我所熟悉的mysql和postgresql有很大的区别。 今天&#xff0c;讲一下我之前遇到的一个问题。这个问题的起因是用达梦数据库迁移工具&…

Java版工程行业管理系统-提升工程项目的综合管理能力

工程项目管理涉及众多环节和角色&#xff0c;如何实现高效协同和信息共享是关键。本文将介绍一个采用先进技术框架的Java版工程项目管理系统&#xff0c;该系统支持前后端分离&#xff0c;功能全面&#xff0c;可满足不同角色的需求。从项目进度图表到施工地图&#xff0c;再到…

高考:心态、时间、知识,多维度攻略让你脱颖而出

高考&#xff0c;宛如一场无声的激战&#xff0c;承载着无数莘莘学子的梦想与热望。在这激烈的竞争中&#xff0c;充分且周全的准备显得尤为关键。那么&#xff0c;高考备考究竟应从哪些方面入手&#xff1f;又有哪些行之有效的备考策略能为我们保驾护航呢&#xff1f; 一、高考…

信息安全工程师(82)操作系统安全概述

一、操作系统安全的概念 操作系统安全是指操作系统在基本功能的基础上增加了安全机制与措施&#xff0c;从而满足安全策略要求&#xff0c;具有相应的安全功能&#xff0c;并符合特定的安全标准。在一定约束条件下&#xff0c;操作系统安全能够抵御常见的网络安全威胁&#xff…

SQL server 中 CROSS APPLY的使用

CROSS APPLY 是 SQL Server 中的一个操作符&#xff0c;用于将一个表表达式&#xff08;如子查询、函数等&#xff09;与外部表进行连接。CROSS APPLY 类似于 INNER JOIN&#xff0c;但它允许你在一个查询中多次引用外部表的行&#xff0c;并且可以动态地生成结果集。 基本语法…

硬件---3电容---电容特性、上电和断电延时、稳压功能、容抗计算

一电容是什么 1概念 电容就是两块不连接的导体加上中间的绝缘材料。其本身能够存储电荷&#xff0c;当在这两个互相导体两端增加电压的时候&#xff0c;就会形成电场&#xff0c;从而存储电能。 2注意 <1>电解电容正负极一定不能接反&#xff0c;如果接反轻则烧坏&am…

行车记录打不开?原因分析与数据恢复全攻略

行车记录遭遇困境 行车记录仪&#xff0c;作为现代驾驶中的重要设备&#xff0c;不仅能够帮助我们记录行车过程&#xff0c;还能在关键时刻提供有力的证据。然而&#xff0c;当行车记录突然打不开时&#xff0c;这无疑给车主们带来了不小的困扰。行车记录打不开&#xff0c;可…

考研要求掌握C语言(归并排序)

归并排序考啥&#xff1f; 在考研中归并排序只出在选择题&#xff0c;理解原理很重要 且在考研中考靓靓归并&#xff0c;还是比较简单的 归并排序原理 就是每次分一半&#xff0c;直到每一半只含有一个或不能再分时&#xff0c;一半一半的进行排序&#xff0c;最终合并两个…

编译工具与文件学习(一)-YAML、repos、vcstoolcolcon

YAML YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种人类可读的数据序列化格式&#xff0c;常用于配置文件、数据交换和存储结构化数据。YAML 的设计目标是简洁、易读&#xff0c;并且能够表示复杂的数据结构。 YAML 文件的基本语法 基本结构&#xff1a; Y…