go 语言常见问题(2)

简介: go 语言常见问题(2)

11. recover的执行时机

无,recover 必须在 defer 函数中运行。recover 捕获的是祖父级调用时的异常,直接调用时无效。

func main() {
    recover()
    panic(1)
}

直接 defer 调用也是无效。

func main() {
    defer recover()
    panic(1)
}

defer 调用时多层嵌套依然无效。

func main() {
    defer func() {
        func() { recover() }()
    }()
    panic(1)
}

必须在 defer 函数中直接调用才有效。

func main() {
    defer func() {
        recover()
    }()
    panic(1)
}

12. 闭包错误引用同一个变量问题怎么处理

在每轮迭代中生成一个局部变量 i 。如果没有 i := i 这行,将会打印同一个变量。

func main() {
    for i := 0; i < 5; i++ {
        i := i
        defer func() {
            println(i)
        }()
    }
}

或者是通过函数参数传入 i 。

func main() {
    for i := 0; i < 5; i++ {
        defer func(i int) {
            println(i)
        }(i)
    }
}

13. 在循环内部执行defer语句会发生啥

defer 在函数退出时才能执行,在 for 执行 defer 会导致资源延迟释放。

并且会使调用栈剧增(如果循环次数多的话),比较好的方式是使用局部函数处理。

func main() {
    for i := 0; i < 5; i++ {
        func() {
            f, err := os.Open("/path/to/file")
            if err != nil {
                log.Fatal(err)
            }
            defer f.Close()
        }()
    }
}

func 是一个局部函数,在局部函数里面执行 defer 将不会有问题。

14. 说出一个避免Goroutine泄露的措施

可以通过 context 包来避免内存泄漏。

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    ch := func(ctx context.Context) <-chan int {
        ch := make(chan int)
        go func() {
            for i := 0; ; i++ {
                select {
                case <- ctx.Done():
                    return
                case ch <- i:
                }
            }
        } ()
        return ch
    }(ctx)
    for v := range ch {
        fmt.Println(v)
        if v == 5 {
            cancel()
            break
        }
    }
}

下面的 for 循环停止取数据时,就用 cancel 函数,让另一个协程停止写数据。如果下面 for 已停止读取数据,上面 for 循环还在写入,就会造成内存泄漏。

15. 如何跳出for select 循环

通常在for循环中,使用break可以跳出循环,但是注意在go语言中,for select配合时,break 并不能跳出循环。

func testSelectFor2(chExit chan bool){
 EXIT:
    for  {
        select {
        case v, ok := <-chExit:
            if !ok {
                fmt.Println("close channel 2", v)
                break EXIT//goto EXIT2
            }
            fmt.Println("ch2 val =", v)
        }
    }
//EXIT2:
    fmt.Println("exit testSelectFor2")
}

16. 如何在切片中查找

go中使用 sort.searchXXX 方法,在排序好的切片中查找指定的方法,但是其返回是对应的查找元素不存在时,待插入的位置下标(元素插入在返回下标前)。可以通过封装如下函数,达到目的。

func IsExist(s []string, t string) (int, bool) {
    iIndex := sort.SearchStrings(s, t)
    bExist := iIndex!=len(s) && s[iIndex]==t
    return iIndex, bExist
}

17. 如何初始化带嵌套结构的结构体

go 的哲学是组合优于继承,使用 struct 嵌套即可完成组合,内嵌的结构体属性就像外层结构的属性即可,可以直接调用。注意初始化外层结构体时,必须指定内嵌结构体名称的结构体初始化,如下看到 s1方式报错,s2 方式正确。

type stPeople struct {
    Gender bool
    Name string
}
type stStudent struct {
    stPeople
    Class int
}
//尝试4 嵌套结构的初始化表达式
//var s1 = stStudent{false, "JimWen", 3}
var s2 = stStudent{stPeople{false, "JimWen"}, 3}
fmt.Println(s2.Gender, s2.Name, s2.Class)

18. 切片和数组的区别

数组是具有固定长度,且拥有零个或者多个,相同数据类型元素的序列。数组的长度是数组类型的一部分,所以[3]int 和 [4]int 是两种不同的数组类型。


数组需要指定大小,不指定也会根据初始化的自动推算出大小,不可改变;数组是值传递。


数组是内置类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。


数组定义:

var array [10]int
var array =[5]int{1,2,3,4,5}

切片表示一个拥有相同类型元素的可变长度的序列。切片是一种轻量级的数据结构,它有三个属性:指针、长度和容量。


切片不需要指定大小;切片是地址传递;切片可以通过数组来初始化,也可以通过内置函数make()初始化 。初始化时len=cap,在追加元素时如果容量cap不足时将按len的2倍扩容。


切片定义:

var slice []type = make([]type, len)

19. new和make的区别

new 的作用是初始化一个指向类型的指针 (*T) 。


new 函数是内建函数,函数定义:func new(Type) *Type。


使用 new 函数来分配空间。传递给 new 函数的是一个类型,不是一个值。返回值是指向这个新分配的零值的指针。

make 的作用是为 slice,map 或 chan 初始化并返回引用 (T)。


make 函数是内建函数,函数定义:func make(Type, size IntegerType) Type;第一个参数是一个类型,第二个参数是长度;返回值是一个类型。


make(T, args) 函数的目的与 new(T) 不同。它仅仅用于创建 Slice, Map 和 Channel,并且返回类型是 T(不是T*)的一个初始化的(不是零值)的实例。

20. Printf()、Sprintf()、Fprintf()函数的区别用法是什么

都是把格式好的字符串输出,只是输出的目标不一样。


Printf(),是把格式字符串输出到标准输出(一般是屏幕,可以重定向)。Printf() 是和标准输出文件 (stdout) 关联的,Fprintf  则没有这个限制。


Sprintf(),是把格式字符串输出到指定字符串中,所以参数比printf多一个char*。那就是目标字符串地址。


Fprintf(),是把格式字符串输出到指定文件设备中,所以参数比 printf 多一个文件指针 FILE*。主要用于文件操作。


Fprintf() 是格式化输出到一个stream,通常是到文件。

21. 说说go语言中的for循环

for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的break,可以选择中断哪一个循环 for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量。

22. Array 类型的值作为函数参数

在 C/C++ 中,数组(名)是指针。将数组作为参数传进函数时,相当于传递了数组内存地址的引用,在函数内部会改变该数组的值。在 Go 中,数组是值。作为参数传进函数时,传递的是数组的原始值拷贝,此时在函数内部是无法更新该数组的。

// 数组使用值拷贝传参
func main() {
 x := [3]int{1,2,3}
 func(arr [3]int) {
  arr[0] = 7
  fmt.Println(arr)// [7 2 3]
 }(x)
 fmt.Println(x)// [1 2 3] // 并不是你以为的 [7 2 3]
}

想改变数组,直接传递指向这个数组的指针类型。

// 传址会修改原数据
func main() {
 x := [3]int{1,2,3}
 func(arr *[3]int) {
  (*arr)[0] = 7
  fmt.Println(arr)// &[7 2 3]
 }(&x)
 fmt.Println(x)// [7 2 3]
}

直接使用 slice:即使函数内部得到的是 slice 的值拷贝,但依旧会更新 slice 的原始数据(底层 array)

// 错误示例
func main() {
 x := []string{"a", "b", "c"}
 for v := range x {
  fmt.Println(v)// 1 2 3
 }
}
// 正确示例
func main() {
 x := []string{"a", "b", "c"}
 for _, v := range x {// 使用 _ 丢弃索引
  fmt.Println(v)
 }
}

说。go语言中的for循

23. 说说go语言中的switch语句

单个 case 中,可以出现多个结果选项。只有在 case 中明确添加 fallthrough关键字,才会继续执行紧跟的下一个 case。

24. 说说go语言中有没有隐藏的this指针

方法施加的对象显式传递,没有被隐藏起来。


golang 的面向对象表达更直观,对于面向过程只是换了一种语法形式来表达

方法施加的对象不需要非得是指针,也不用非得叫 this。

25. go语言中的引用类型包含哪些

数组切片、字典(map)、通道(channel)、接口(interface)。

26. go语言中指针运算有哪些

可以通过“&”取指针的地址;可以通过“*”取指针指向的数据。

26. 说说go语言的main函数

main 函数不能带参数;main 函数不能定义返回值。

main 函数所在的包必须为 main 包;main 函数中可以使用 flag 包来获取和解析命令行参数。

27. go语言触发异常的场景有哪些

  • 空指针解析
  • 下标越界
  • 除数为0
  • 调用 panic 函数

28. 说说go语言的beego框架

  • beego 是一个 golang 实现的轻量级HTTP框架
  • beego 可以通过注释路由、正则路由等多种方式完成 url 路由注入
  • 可以使用 bee new 工具生成空工程,然后使用 bee run 命令自动热编译

29. 说说go语言的goconvey框架

  • goconvey 是一个支持 golang 的单元测试框架
  • goconvey 能够自动监控文件修改并启动测试,并可以将测试结果实时输出到web界面
  • goconvey 提供了丰富的断言简化测试用例的编写

30. GoStub的作用是什么

  • GoStub 可以对全局变量打桩
  • GoStub 可以对函数打桩
  • GoStub 不可以对类的成员方法打桩
  • GoStub 可以打动态桩,比如对一个函数打桩后,多次调用该函数会有不同的行为
目录
打赏
0
1
1
0
132
分享
相关文章
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
71 10
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
89 3
阿里双十一背后的Go语言实践:百万QPS网关的设计与实现
解析阿里核心网关如何利用Go协程池、RingBuffer、零拷贝技术支撑亿级流量。 重点分享: ① 如何用gRPC拦截器实现熔断限流; ② Sync.Map在高并发读写中的取舍。
221 0
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
86 0
eino — 基于go语言的大模型应用开发框架(二)
本文介绍了如何使用Eino框架实现一个基本的LLM(大语言模型)应用。Eino中的`ChatModel`接口提供了与不同大模型服务(如OpenAI、Ollama等)交互的统一方式,支持生成完整响应、流式响应和绑定工具等功能。`Generate`方法用于生成完整的模型响应,`Stream`方法以流式方式返回结果,`BindTools`方法为模型绑定工具。此外,还介绍了通过`Option`模式配置模型参数及模板功能,支持基于前端和用户自定义的角色及Prompt。目前主要聚焦于`ChatModel`的`Generate`方法,后续将继续深入学习。
803 7
eino — 基于go语言的大模型应用开发框架(一)
Eino 是一个受开源社区优秀LLM应用开发框架(如LangChain和LlamaIndex)启发的Go语言框架,强调简洁性、可扩展性和可靠性。它提供了易于复用的组件、强大的编排框架、简洁明了的API、最佳实践集合及实用的DevOps工具,支持快速构建和部署LLM应用。Eino不仅兼容多种模型库(如OpenAI、Ollama、Ark),还提供详细的官方文档和活跃的社区支持,便于开发者上手使用。
1254 8
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问