【基础】go基础学习笔记

基础及关键字
  • if for switch都支持使用隐形声明(:=)来快速声明一个变量,无需在上面一行额外声明,这可以增加代码简洁性,但不太符合其他常规语言的写法,需要习惯一下

  • if for switch都不需要使用()包裹表达式,只使用空格隔开就行

  • 大写的函数或变量才算是对外声明(用开头大写来代替其他语言中的public private\export的概念)

  • 基础类型转换是显式的,使用类型本身加(),值写在括号中,即可完成类型转换

  • 导包和声明变量都支持使用()批量声明,这可以增加代码简洁性,也可以像其他语言一样一条条声明,也没问题

  • switch的case匹配到后就直接停止,不会像其他语言一样一路向后继续匹配,除非以 fallthrough 语句结束

  • for关键字支持省略声明、条件、表达式三个部分,这直接代替别的语言中的while了

  • 声明变量、方法参数、方法返回值时当多个参数类型重复时都支持省略类型,这可以有效减少重复代码,但也容易出bug,写法: x, y, z int

  • go中各种表达式都使用分号;隔开

  • 方法return支持多值return,只要方法的return类型定义时多定义几个就好了,小括号括起来,用逗号隔开

  • 尽量避免使用隐式return(方法需要返回值时,直接一个return将函数中的所有变量都返回掉),因为隐式return容易出现问题(太依赖编译器,函数后续不好修改)

  • go中的返回值也支持声明类型的同时给其命名,这就更能说明不要使用隐式return了,容易出现问题

  • 循环中使用break跳出

  • defer关键字是defer语句所在的函数运行完成后再调用被defer修饰的代码,defer调用的函数会被压栈,会遵从后进先出的顺序依次调用

  • 以下函数很违反直觉:

package mainimport "fmt"func main() {fmt.Println("counting")defer fmt.Println("done")i := 0;for ; i < 10; i++ {defer fmt.Println(i)}
}

结果:

counting
9
8
7
6
5
4
3
2
1
0
done
指针
  • 指针直接指向内存地址,操作指针等于操作该值本身。
  • 使用&对某个变量产生一个指针:&i,使用*修饰T(类型)来声明一个指针:p *int,使用*p的方式来解引用指针(使用指针)。
  • &表示取地址,A变量被取地址后赋值给新的变量B,这个变量B就成为了变量A的指针,直接打印变量B时只会打印出内存地址
  • *表示解引用,变量B目前是指针,使用*对变量B进行解引用,即*B就可以打印原来的变量A的值,解引用后的*B和原来的A的值是完全一样的,即内存上存储使用的是同一份。
  • 列表指针写法: *List[T]指针的指针写法: **List[T],指针的指针是要修改的变量的指针的指针,不常用但是在一些特殊场景很有用,通常用在替换要修改的变量的指针本身时使用,口述比较绕,但是看一个关于链表的代码例子就明白了:
// 在 main 函数中,我们的链表头是一个指针
var listHead *List[T] = nil // 一开始是空链表// 我们希望 Push 函数能修改 listHead 这个指针,让它指向一个新的节点。
func (l **List[T]) Push(v T) {// 如果我们只接收 *List[T],那么 l 只是 listHead 的一个副本。// 我们需要修改 listHead 本身,所以必须接收它的地址,即 **List[T]。// *l 的意思就是:“通过这把钥匙,拿到 main 函数里的那张原始藏宝图(listHead 指针)”// 然后我们修改这张原始藏宝图,让它指向一个新的宝藏(新节点)。*l = &List[T]{next: *l, val: v}
}
  • 黄金法则:想修改什么,就向函数传递它的指针,不论是变量还是指针本身
  • 直接给指针本身赋值时,必须也要赋值指针,不能赋值值本身
结构体
  • go中声明结构体(类似于类)使用type 结构体名称 struct加大括号:
type Vertex struct {X intY int
}
  • 实例化结构体:
v := Vertex{1, 2}//允许如下方式实例化结构
v := Vertex{X: 1} //对X赋值使用冒号而不是等号,这里Y就是0,println(v)打印结果是 {1 0}
  • 配合结构体使用:
p := &v
等价于
var p *Vertex = &v

go允许隐式解引用,即直接省略和星号和大括号对结构体引用进行使用:

p.X = 1e9
等价于
(*p).X = 1e9
数组和切片
  • 数组声明方式:var a [2]string,与其他语言不同的是数量和中括号放在了类型前边
  • 初始化数组的方式:var a [2]string,带初始值的方式: a := [2]string{"hello", "world"} 或者 var c [2]string = [2]string{"hello", "world"}
  • 数组和切片的区别是数组声明时必须指定长度,而切片则由编译器推导。数组是实际存储值的,而切片不存储值(只是相当于数组中某段元素的引用),切片更常用
  • 切片遵从要头不要尾,a[1:4]表示取数组的1,2,3下标的元素,不包含4
  • 切片类似于数组的引用,即它描述了数组中的某段元素,修改切片等于直接修改数组本身
  • 构建数组时中括号[]里不写数字,就相当于构建了一个数组+切片,注意,这样的话长度其实仍然固定,如果直接给超过声明长度的下标赋值或取值就会报越界错误
  • 切片可以忽略上界和下界:
//切片下界的默认值为 0,上界则是该切片的长度。//对于数组 var a [10]int 来说,以下切片表达式和它是等价的:a[0:10]
a[:10]
a[0:]
a[:]
  • make(内置函数)用来创建动态数组,比较常用:
//第三个参数cap可以省略,也可以手动指定
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
  • 数组的len可以理解为数组长度,cap可以理解为容量。容量总是大于等于长度的。
  • 切片的类型可以是任意类型包括结构体或切片等,例如[][]string[]struct
  • append(内置函数)用来为切片追加值:
v := []int{0,1} //声明切片
v = append(v, 2, 3, 4) //追加多个值
fmt.Println(v) //结果:[0 1 2 3 4] 
  • 使用range关键字遍历切片或数组
  • 在go中利用切片结合uint8的对应灰度值来渲染一张图片,关键点就是对切片本身的使用:
package mainimport (
"golang.org/x/tour/pic"
)func Pic(dx, dy int) [][]uint8 {outerSlice := make([][]uint8 ,dy)for y := range outerSlice {innerSlice := make([]uint8, dx)for x := range innerSlice {v := x*x+y*y //改变这里可以改变结果图片的效果,例如:(x+y)/2、x*y、x^y、x*log(y) 、x%(y+1)、x*x、y*y 、x * math.Log(float64(y))等innerSlice[x] = uint8(v)}outerSlice[y] = innerSlice}return outerSlice
}func main() {pic.Show(Pic)
}
map(映射)
  • 创建map可以使用make函数,声明map类型时的格式如:map[string]string,第一个中括号中的为键类型可以是任意类型,通常是string,后面紧跟着的是值类型,可以是任意类型
  • map创建时后面的大括号类似于kotlin中的mapOf,不过把to换成了冒号
  • 通过双赋值表达式检测键是否存在:
v, exsit := m["答案"]
fmt.Println("值:", v, "是否存在?", exsit)
函数(方法)
  • 函数可以作为参数传递,参数类型为func,函数可以赋值给一个变量,该变量即成为函数本身,跟js中的函数特点有点像,函数类型也可以作为另一个函数的返回值
  • 函数的闭包通常可以用于统计和累计,这很有用,可以省去一些不必要的外部全局变量
  • 一段不正确的斐波那契数列计算代码,这段代码跳过了开头的0和1的输出:
package mainimport "fmt"// fibonacci 是返回一个「返回一个 int 的函数」的函数
func fibonacci() func() int {r := []int{0,1}return func () int {sum := r[len(r)-1] + r[len(r)-2]r = append(r,sum)//fmt.Println(r)//fmt.Println( r[len(r)-1:len(r)][0] )return sum}
}func main() {f := fibonacci()for i := 0; i < 10; i++ {fmt.Print(f()," ")}
}//会打印 :1 2 3 5 8 13 21 34 55 89

修正思路为每次调用时,第一个值正是要返回的结果,而后只使用最新的两个数字来保持状态值最新,下面是修正后的函数:

func fibonacci() func() int {a,b := 0,1return func () int {ret := aa,b = b,a+breturn ret}
}//打印:0 1 1 2 3 5 8 13 21 34
  • 为结构体(或基础类型)定义方法的方式就是使用括号包含具体的带名称的类型,类似于kotlin的扩展方法,但是不能定义在结构体内,也必须得声明一个名称来引用而不是this
//定义
type Vertex struct {X, Y float64
}func (v Vertex) Abs() float64 {return math.Sqrt(v.X*v.X + v.Y*v.Y)
}//使用
v := Vertex{3, 4}
fmt.Println(v.Abs())

为类型定义方法的方式是使用type转一下类型:

//定义
type MyFloat float64func (f MyFloat) Abs() float64 {if f < 0 {return float64(-f)}return float64(f)
}//使用
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
  • 不能跨包定义方法
  • 为指针类型接收者定义方法通常更常用,因为有时需要通过该方法来修改结构体的数据并使其在后续生效:
package mainimport ("fmt"
)type Vertex struct {A,B int
}//没有对v(指针型类型接收者)进行修改的能力
func (v Vertex) Sum() int {return v.A+v.B
}//有对v进行修改的能力
func (v *Vertex) Scale(l int) {v.A = v.A * lv.B = v.B * l
}//使用
func main() {v := Vertex{3, 4}v.Scale(10)fmt.Println(v.Sum())
}//打印70
  • 如果不确定,或者想修改接收者,用指针接收者通常是更安全、更通用的选择,定义为指针接收者通常对性能也有很大的帮助,可以避免复制开销
  • go的语法糖:1.定义为指针型接收者的方法,在调用时也直接允许值类型的进行调用 2.定义为值型接受者的方法,在调用时也允许指针类型进行调用
  • 当一个方法实现了接口或者作为结构体的方法,有入参,有多个响应值,且响应值中有方法类型时,这个方法看起来会很奇怪,有多达4个或更多的括号,但是却有效合法,如下复杂示例:
package mainimport "fmt"type Vertex struct{X,Y int
}//Vertex类型的专属异常
type VertexError struct {V Vertex
}//Vertex类型的专属异常处理
func (e *VertexError) Error() string {return fmt.Sprintf("vertex error on value: %+v", e.V)
}//复杂函数
func (v *Vertex) Sum(scale func(int) int) (int,(func(int) int), error){if v.X == -1 {return 0, nil, &VertexError{*v}}return scale(v.X) + scale(v.Y),scale,nil
}func Scale(s int) int {return s * s
}func main() {v := Vertex{-1,2} //-1时会触发异常s,f,err := v.Sum(Scale)if err != nil {fmt.Printf("error: %v",err)return}fmt.Printf("s: %v f: %v",s,f)
}

接口

  • go中的接口使用interface关键字,没有显示的类似于其他语言中的"implements"或冒号等定义,某个类型的方法只要包含目标接口的全部方法,就可以说该类型实现了目标接口,没有代码上显式的引用和定义关系
  • 弊端是无法一眼看出某个类型到底实现了某个接口没有,好处就是代码灵活
  • 接口也是值,可以在方法参数、返回值、变量等进行传递
  • 空接口是一个特殊定义: interface {},可以保存任何类型的值内容,类似于kotlin中的Any?或java中的Object+null类型
  • 类型断言,格式和写法: i.(string),具体用法如下,可以检查出该接口底层保存的值以及是否使用的是对应的类型:
   var i interface{} = "hello"s := i.(string)fmt.Println(s) //hellos, ok := i.(string)fmt.Println(s, ok) //hello truef, ok := i.(float64)fmt.Println(f, ok) //0 falsef = i.(float64) // panicfmt.Println(f) //panic: interface conversion: interface {} is string, not float64

用来检查并获取值确实不错,看起来挺好用的

  • 类型选择,格式和写法: i.(type){ case ... }, 类型选择的写法如下,可以通过类似switch的方式检查对应接口值是否是该类型:
package mainimport "fmt"func do(i interface{}) {switch v := i.(type) {case int:fmt.Println("是int类型")case string:fmt.Println("是string类型")default:fmt.Println("未知类型", v)}
}func main() {do(21) //是int类型do("hello") //是string类型do(true) //未知类型 true
}
  • 方法调用的返回值可以主动的响应error,通过判断error是否为空:if err != nil来进行错误处理:
package mainimport ("errors""fmt"
)func Div(a, b int) (int, error) {if b == 0 {return 0, errors.New("b is 0!") //抛出一个错误}return a / b, nil
}func main() {div, err := Div(3, 0)if err != nil {fmt.Println(err) //b is 0!return //提前返回}fmt.Println(div)
}
  • error是一个内置接口,可以轻松使用方法对某个类型定义特定的错误处理,实现: Error() string方法即可
  • 错误处理完后应当立即返回,通常使用return或者log.Fatal
  • 实现接口可以做很多事情,一种常见的好处就是解耦且不用关注额外细节就能实现强大的功能,因为方法参数如果是接口类型的,那么传入的实例只要能实现对应接口的所有方法,就可以正确作为参数,至于方法内部如何,应该根据方法实际情况来完成实现
  • 实现了图片接口的结构体,可以借助pic库完成图片绘制,耦合性很低,表现力很高,示例:
package mainimport ("golang.org/x/tour/pic""image""image/color"
)// Image 是我们将要定义的自定义图片类型。
// 它是一个空结构体,因为我们不需要存储任何数据。
// 图像的像素是动态计算出来的。
type Image struct{}// Bounds 方法返回图像的尺寸。
// 我们定义一个 256x256 像素的图像。
func (i Image) Bounds() image.Rectangle {return image.Rect(0, 0, 256, 256)
}// ColorModel 返回图像的颜色模型。
// 我们使用标准的 RGBA 模型。
func (i Image) ColorModel() color.Model {return color.RGBAModel
}// At 方法是核心。它在给定的 (x, y) 坐标处返回一个颜色。
// 渲染程序会为图像中的每一个像素调用一次此方法。
func (i Image) At(x, y int) color.Color {// 使用 x 和 y 坐标通过一个简单的函数生成一个值。// 这里的 x^y 是按位异或(XOR)操作,能产生有趣的图案。v := uint8(x ^ y)// 返回一个 RGBA 颜色。// R(红)= v, G(绿)= v, B(蓝)= 255, A(透明度)= 255(不透明)// 这会产生一个蓝色的、带有渐变纹理的图像。return color.RGBA{v, v, 255, 255}
}func main() {// 创建我们自定义 Image 类型的一个实例。m := Image{}// pic.ShowImage 会接收任何实现了 image.Image 接口的类型,// 并将其渲染成图片(在 Go Tour 中是 base64 编码的 PNG)。pic.ShowImage(m)
}
类(泛)型参数
  • 类似于其他语言中的泛型
  • 声明方式:func Index[T comparable](s []T, x T) int,这表示s的元素类型可以是满足了 comparable接口的所有类型,x 的类型也是满足了 comparable接口的所有类型

杂项

  • 遍历链表:
//常见的需要取值的标准写法:
for n := l; n != nil; n = n.next//将链表拨动到最后位置的写法:
current := *l
for current.next != nil {current = current.next
}
  • 构建字符串使用strings.Builder,方法是WriteString,获取结果是string()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI驱动的企业知识管理革新

Baklib&#xff1a;人工智能引领的知识管理平台Baklib 是领先的 AI 驱动知识管理系统&#xff0c;专为企业打造智能化、模块化的知识共享平台。功能覆盖在线帮助中心、内联网、CMS 网站、客户支持系统、视频中心、活动教学平台和客户社区&#xff0c;全面提升组织在知识管理、员…

使用 FFmpeg 实现 RTP 音频传输与播放

&#x1f50a; 使用 FFmpeg 实现 RTP 音频传输与播放&#xff08;Ubuntu&#xff09; 在音视频开发或远程通信场景中&#xff0c;RTP&#xff08;Real-time Transport Protocol&#xff09; 是一种常用的实时音频传输协议。本文将介绍如何使用 FFmpeg 在 Ubuntu 上实现从一台主…

Android 导出联系人为 vCard(.vcf)

有时需要将联系人数据导出为可共享的标准格式&#xff1a;vCard&#xff08;.vcf&#xff09;格式&#xff0c;可被系统直接导入通讯录一、导出联系人为 vCard&#xff08;.vcf&#xff09;✅ 支持字段我们支持导出的字段包括&#xff1a;姓名个人电话家庭电话工作电话邮箱备注…

大模型-bacth之chunked prefills

一、chunked prefills 1.1 chunked prefills核心思想 ORCA虽然很优秀&#xff0c;但是依然存在两个问题&#xff1a;GPU利用率不高&#xff0c;流水线依然可能导致气泡问题。 我们来看sarathi-serve做的一个实验。左右两图分别刻画了在不同的batch size下&#xff0c;prefil…

multiprocessing模块使用方法(一)

结合Python代码示例说明multiprocessing模块的使用方法、Linux启动命令、服务部署及真实应用场景&#xff1a;一、multiprocessing基础用法 Python的multiprocessing模块通过创建子进程实现并行计算&#xff0c;核心步骤如下&#xff08;引用&#xff09;&#xff1a;创建进程对…

vector如何判断应该扩容?

std::vector 的扩容机制是 C 容器性能优化的一个核心点。它的判断标准、行为特征和实现细节如下所示&#xff1a;✅ 一、vector 的核心结构 std::vector 内部维护三个指针&#xff1a; begin -> 指向数据起始位置 end -> 指向当前已使用的元素末尾&#xff08;size…

ICMP考点

在网络工程师、运维工程师、安全工程师等岗位的面试中&#xff0c;​​ICMP&#xff08;Internet Control Message Protocol&#xff0c;互联网控制报文协议&#xff09;​​ 是高频必考知识点。ICMP作为IP协议的“辅助协议”&#xff0c;虽然不直接传输用户数据&#xff0c;但…

服务器带宽具体是指什么意思?

企业在选择服务器租用或托管服务时&#xff0c;需要根据自身的业务情况来选择合适的服务器带宽&#xff0c;以便于能够让其业务持续稳定的运行工作&#xff0c;做到及时响应用户的访问请求&#xff0c;但是很多用户对于带宽一词没有过多的概念&#xff0c;本文就来探讨一下服务…

CANape之ASAP2 Studio介绍

提到ECU的测量标定工具&#xff0c;很多小伙伴第一个想到的就是CANape。但其实&#xff0c;除了强大的测量标定功能之外&#xff0c;CANape还有很多其他功能。例如&#xff0c;CANape中集成的小工具——ASAP2 Studio&#xff0c;其支持对A2L文件&#xff08;由ASAM组织所定义&a…

【debug日记】MONAI SwinUNETR 目标检测项目调试总结(AI自动总结)

MONAI SwinUNETR 目标检测项目调试总结 日期: 2025年7月25日 项目: 使用 MONAI&#xff0c;以预训练的 SwinUNETR 为骨干网络&#xff0c;微调 RetinaNet 进行3D肺结节检测。 本文档旨在记录在项目配置、数据处理和模型训练过程中遇到的一系列问题及其解决方案&#xff0c;作为…

AI同传领域,字节跳动与科大讯飞激战进行时

在AI同声传译市场&#xff0c;行业巨头科大讯飞长期占据主导地位&#xff0c;但新晋玩家字节跳动正以迅猛姿态发起挑战。7月24日&#xff0c;字节旗下火山引擎正式发布豆包同声传译模型 Seed LiveInterpret 2.0&#xff0c;主打“人类级延迟”和“0样本声音复刻”&#xff0c;试…

[C++]string::substr

string substr (size_t pos 0, size_t len npos) const;substr() 主要功能是复制&#xff08;截取更准确&#xff09;子字符串&#xff0c;要求从指定位置 pos 开始&#xff0c;并具有指定的长度 len 。如果没有指定长度或者超出了源字符串的长度&#xff0c;则子字符串将延续…

KNN算法:从原理到实战全解析

一 算法介绍 K近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;是一种基于实例的监督学习算法&#xff0c;适用于分类和回归任务。其核心思想是通过计算待预测样本与训练集中样本的距离&#xff0c;选取距离最近的K个邻居&#xff0c;根据这些邻居的标签进行投票&#xf…

医疗器械:DFEMA和PFEMA

在医疗器械行业&#xff0c;DFMEA&#xff08;Design FMEA&#xff0c;设计失效模式及影响分析&#xff09;和 PFMEA&#xff08;Process FMEA&#xff0c;过程失效模式及影响分析&#xff09;是核心的风险管理工具&#xff0c;旨在通过系统性识别潜在风险、分析影响并采取预防…

Qt 与 SQLite 嵌入式数据库开发

Qt 与 SQLite 的结合是开发轻量级、跨平台嵌入式数据库应用的理想选择。SQLite 作为一种零配置、文件型数据库&#xff0c;无需独立的服务器进程&#xff0c;非常适合集成到 Qt 应用中。本文将深入探讨 Qt 与 SQLite 的嵌入式数据库开发&#xff0c;包括基础操作、高级特性、性…

Oracle OMF 非OMF 文件 转化 不需要重建 file#.incarnation#

不需要重建就要重启&#xff0c; alter database datafile move 就可以在线 file#.incarnation# 是 incarnation 不是dbid Goal How to convert non OMF files to OMF files with ASM storage Datafiles are not deleted at dropping tablespace if files are non-OMF and …

大型微服务项目:听书——11 Redisson分布式布隆过滤器+Redisson分布式锁改造专辑详情接口

11 Redisson分布式布隆过滤器Redisson分布式锁改造专辑详情接口 11.1 缓存穿透解决方案&布隆过滤器 缓存穿透解决方案&#xff1a; 布隆过滤器&#xff1a; 布隆过滤器的使用&#xff1a; 11.2 远程调用查询所有的专辑id集合 修改&#xff1a; /*** 查询所有的专辑…

STM32与ADS1220实现多通道数据采集的完整分析和源程序

以下是基于STM32与ADS1220实现多通道数据采集的完整分析和源程序,结合硬件设计、通信协议及软件优化,提供高精度采集解决方案: 一、系统设计关键要点 ADS1220特性 24位高精度ΔΣ ADC,支持4路单端或2路差分输入 集成PGA(增益1~128)、基准电压和可编程电流源 多通道限制:…

百特搭AI低代码平台助力企业国际化业务敏捷拓展

在全球化浪潮下&#xff0c;企业扬帆出海或服务全球客户已成为重要战略。然而&#xff0c;开拓国际市场面临多重挑战&#xff1a;语言文化差异显著、本地化需求复杂多变、智能化应用需求激增、各国IT基础设施与合规要求各异。企业亟需一个能够快速响应、灵活适应&#xff0c;并…

epoll_event数据结构及使用案例详解

epoll_event 数据结构详解 在 Linux 的 I/O 多路复用机制 epoll 中&#xff0c;epoll_event 是关键的数据结构&#xff0c;用于描述文件描述符&#xff08;fd&#xff09;上的事件和关联数据。其定义在头文件 <sys/epoll.h> 中&#xff1a; struct epoll_event {uint32_t…