go基础面试题汇总第一弹
init函数是什么时候执行的?
init的函数的作用是什么?
通常作为程序执行前包的初始化,例如mysql redis 等中间件的初始化
init函数的执行顺序是怎样的?
分不同情况来回答:
- 在同一个go文件里面如果有多个init方法,它们会按照代码依次执行。
- 同一个package文件里面,它们会按照文件名的顺序执行。
- 不同的package且不是相互依赖的情况下,按照Import导入的顺序执行。
- 不同的package是相互依赖的情况,会优先执行最后被依赖的init函数。
go文件的初始化顺序是怎样的?
- 执行引入的包。
- 当前包里面的常量变量。
- 执行init函数。
- 执行main函数。
注意点:
- 包相互依赖避免循环导入问题。
- 所有文件的init函数都是在一个goroutine内执行的。
- 如果一个包在不同的地方被引入多次,但是它的Init函数只会执行一次。
go如何获取项目根目录
可以通过os go内置函数库来获取 os.Getwd()
os.Args[0]
os.Executable
也可以通过环境变量自定义根路径
go中new和make有什么区别?
go和new的区别
meke分配内存的同时会初始化,new只会分配零值填充。
make主要用来给slice,map,channel来初始化,new万能没有限制。
make返回的是原始类型,new返回的是类型的指针*T。
new 申请的值均为零值,对创建映射和切片没有意义。
new可以为任何类型的值开辟内存并返回此值的指针。
数组和切片的区别?
数组和切片的相同点
数组和切片 全部元素类型都必须相同。
数组和切片 所有元素都是连续存储在一块内存中,并且是紧挨着的。
数组和切片的不同点
数组的零值是每个元素类型的零值。
切片的零值为nil。
指针类型的数组和切片直接用类型声明后是nil,不能直接使用。
slice和map有什么区别
map中的元素所在内存不一定是连续的
访问元素的时间复杂度都是O1,但相对于slice来说map更慢。
map的优势
map的key值的类型是任何可以比较的类型。
对于大多数元素为零值的情况,map可以节省大量内存。
切片的底层数据结构是什么?有什么特性?
它的底层主要有三个部分
- array 是一个指向底层数组的指针
- len 切片的长度,是指当前切片包含元素数量
- cap 切片的容量,是指底层数组能够容纳的元素数量
切片的动态特性
当切片的len到达cap时,切片需要扩容,在这个扩容的过程中,go内存机制会分配一个新的更大的底层数组,并且将原数组的内容复制到新数组里面,这个过程是runtime包的growlice函数实现的。
切片是如何扩容的?
1.7和1.8后截然不同
切片是否并发安全
需要手动管理并发安全可以用sync.Mutex来确保切片在追加元素的时候避免并发问题。
如何判断两个切片是否相等
在Go语言中,判断两个切片是否相等,需要考虑切片的元素值是否相等以及顺序是否一致。可以使用reflect.DeepEqual函数来进行比较,它能够深度比较两个值是否相等
package mainimport ("reflect""fmt"
)func main() {slice1 := []int{1, 2, 3}slice2 := []int{1, 2, 3}slice3 := []int{3, 2, 1}// 使用reflect.DeepEqual比较两个切片if reflect.DeepEqual(slice1, slice2) {fmt.Println("slice1 and slice2 are equal")} else {fmt.Println("slice1 and slice2 are not equal")}// 使用reflect.DeepEqual比较两个切片if reflect.DeepEqual(slice1, slice3) {fmt.Println("slice1 and slice3 are equal")} else {fmt.Println("slice1 and slice3 are not equal")}
}
package mainimport ("fmt"
)func main() {slice1 := []int{1, 2, 3}slice2 := []int{1, 2, 3}slice3 := []int{3, 2, 1}// 比较元素值是否相等equal := len(slice1) == len(slice2) && len(slice1) == len(slice3)if equal {for i := range slice1 {if slice1[i] != slice2[i] {equal = falsebreak}}}if equal {fmt.Println("slice1 and slice2 have the same elements")} else {fmt.Println("slice1 and slice2 do not have the same elements")}equal = len(slice1) == len(slice3) && len(slice1) == len(slice3)if equal {for i := range slice1 {if slice1[i] != slice3[i] {equal = falsebreak}}}if equal {fmt.Println("slice1 and slice3 have the same elements")} else {fmt.Println("slice1 and slice3 do not have the same elements")}
}
slice作为参数传递是传值还是传指针
,切片(slice)作为参数传递时,实际上是传递了切片的副本,而不是原始切片的指针。
切片作为参数传递时,传递的是切片的副本。
对切片副本的修改不会影响原始切片,除非通过函数返回值获取新的切片。
切片副本和原始切片共享底层数组,直到副本进行了导致底层数组扩容的操作。
package mainimport "fmt"func modifySlice(s []int) {s[0] = 100 // 修改切片副本的第一个元素
}func modifyAndAppend(s []int) []int {s = append(s, 4) // 追加元素,如果容量不够,会重新分配底层数组return s
}func main() {original := []int{1, 2, 3}modifySlice(original)fmt.Println("After modifySlice:", original) // 输出: After modifySlice: [1 2 3]newSlice := modifyAndAppend(original)fmt.Println("After modifyAndAppend:", original, newSlice) // 输出: After modifyAndAppend: [1 2 3] [1 2 3 4]
}
切片的优化技巧
- 预先分配足够的容量
- 避免在循环中使用append
- 使用copy来复制切片
- 注意切片的并发还用 用锁机制保证它的并发安全
strings.TrimRight和strings.TrimSuffix有什么区别
strings.TrimRight(s string, cutset string) string
s := "!!!Hello, World!!!"
result := strings.TrimRight(s, "!")
fmt.Println(result) // 输出 "!!!Hello, World"
strings.TrimSuffix(s string, suffix string) string
s := "Hello, World!"
result := strings.TrimSuffix(s, "!")
fmt.Println(result) // 输出 "Hello, World"
区别:
TrimRight关注的是去除尾部的特定字符集,而TrimSuffix关注的是去除尾部的特定后缀字符串。
TrimRight需要两个参数:原始字符串和要去除的字符集;TrimSuffix只需要两个参数:原始字符串和要去除的后缀。
TrimRight会去除尾部所有指定的字符,直到遇到不在cutset中的字符为止;TrimSuffix则只会去除尾部的特定后缀字符串,如果字符串不是以这个后缀结尾的,那么原始字符串不会被修改。
go语言值溢出会发生什么?
在 Go 语言中,数值类型溢出是指当数值超出类型所能表示的范围时,结果会被截断,从而产生不正确的值。Go 语言中的整型(如 int, int8, int16, int32, int64, uint8, uint16, uint32, uint64)都会受到这种限制。
var x uint8 = 255 // uint8 的最大值是 255
x += 1
fmt.Println(x) // 输出 0
避免值溢出
使用适当的数据类型:确保选择的数值类型可以容纳你所需的数值范围。例如,如果你需要存储大整数,应使用 int64 或者 big.Int 而不是较小的整型。
手动检查值是否溢出:在执行操作前检查数值是否接近类型的最大值或最小值。如果快要溢出,可以采取相应的措施。
var x uint8 = 255
if x == math.MaxUint8 {fmt.Println("溢出警告")
} else {x += 1
}
发生溢出后解决方案
检测到错误并抛出异常或警告:通过条件语句来捕捉溢出场景,并采取相应措施。
选择更大范围的类型:在代码中可以考虑将类型更改为 int64、uint64,或者对特别大的数值使用 math/big 包中的 big.Int 类型,这个类型没有固定的大小限制。
import "math/big"
a := big.NewInt(1)
b := big.NewInt(1)
result := new(big.Int).Mul(a, b) // 执行大数乘法
fmt.Println(result)
go语言中每个值 在内存中只分布在一个内存块上的类型有哪些?
总结来说,Go 语言中每个值只分布在一个内存块上的类型主要包括基本类型(如整型、浮点型、布尔型等)、数组和结构体。
有一些类型的值在内存中并不只占据一个连续的内存块,它们涉及到指向其他内存区域的指针或引用:
切片(slice):切片本身是一个描述符,包含指向底层数组的指针、长度和容量,切片描述符存储在一块内存中,但底层数组的元素可能分布在不同的内存块上。
映射(map):映射是一种引用类型,键值对并不会存储在一个连续的内存块中。
字符串(string):字符串是一个包含指向底层字节数组的指针和长度的结构,字符串的底层数据可能不在连续的内存块中。
接口(interface):接口是一个复杂的类型,包含两个部分:类型信息和数据指针。这两部分的内存布局通常不是连续的。
go语言中哪些类型可以使用cap和和len?
len 可用于:array、slice、string、map、channel。
cap 可用于:slice、array、channel。
go语言的指针有哪些限制?
- go指针不支持直接进行算术运算。
- 一个指针类型的值不能随意转换为另一个指针类型。
- 一个指针的值不能随意跟其他类型指针的值进行比较的。
- 一个指针的值不能随意被赋值给其它任意类型的指针值。
指针比较需要满足两个条件
- 这两个指针类型相同。
- 这两个指针之间可以隐式转换。
go语言中哪些类型的零值可以用nil表示
指针、切片、映射、通道、接口类型、函数都可以用nil表示
不同数据类型的nil值的尺寸是不同的。
nil值不一定是可以相互比较的,主要取决于该类型是否可以比较。
可以比较的两个Nil值不一定相等。
go调用函数传入结构体时,是传值还是传指针?
函数的传递参数只有值传递,且传递的实参都是原始数据的一份拷贝
在go语言中赋值操作和函数调用传参都是将原始值的直接部分赋值给了目标值
如何判断两个对象是否完全相同
- 基本类型的比较
对于基本类型(如整型、布尔型、浮点型、字符、字符串等),你可以直接使用 == 运算符比较两个对象的值是否相同。
a := 10
b := 10
fmt.Println(a == b) // true
str1 := "hello"
str2 := "hello"
fmt.Println(str1 == str2) // true
- 数组的比较
Go 语言中的数组支持使用 == 直接比较,前提是数组的元素类型必须是可比较的(如基本类型)。
arr1 := [3]int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
fmt.Println(arr1 == arr2) // true
- 结构体的比较
结构体可以使用 == 进行比较,但前提是结构体的所有字段都是可比较的类型。如果结构体包含了不可比较的字段(如切片、映射、函数等),直接比较会引发编译错误。
type Person struct {Name stringAge int
}
p1 := Person{Name: "Alice", Age: 25}
p2 := Person{Name: "Alice", Age: 25}
fmt.Println(p1 == p2) // true
- 切片(slice)、映射(map)和通道(channel)的比较
切片、映射、通道 不能直接使用 == 进行比较,除非与 nil 进行比较。要比较两个切片或映射是否完全相同,可以手动遍历每个元素或使用第三方库(如 reflect.DeepEqual)。
var s1 []int = nil
var s2 []int = nil
fmt.Println(s1 == s2) // true, 因为都为 nils1 = []int{1, 2, 3}
s2 = []int{1, 2, 3}
// fmt.Println(s1 == s2) // 编译错误,切片不能直接比较
- 接口的比较
接口可以用 == 进行比较,只有当两个接口的动态类型和动态值都相同的时候,才会被认为是相同的。
var i1 interface{} = 123
var i2 interface{} = 123
fmt.Println(i1 == i2) // true
- 使用 reflect.DeepEqual 进行深度比较
对于无法直接用 == 比较的复杂类型(如切片、映射等),你可以使用 Go 标准库中的 reflect.DeepEqual 进行深度比较。它会递归地比较对象的每个字段和元素,适用于结构体、切片、映射等复杂数据结构。
import ("fmt""reflect"
)s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
fmt.Println(reflect.DeepEqual(s1, s2)) // truem1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 1, "b": 2}
fmt.Println(reflect.DeepEqual(m1, m2)) // true
总结
基本类型、数组、结构体(可比较字段)可以使用 == 直接比较。
切片、映射、通道等引用类型不能直接比较,需要使用 reflect.DeepEqual 或手动比较。
接口类型可以使用 == 比较,前提是其动态类型和动态值相同
用两种方法判断一个对象是否拥有某个方法
- 利用类型断言(Type Assertion)判断对象是否实现某个接口
Go 是静态类型语言,但它通过接口提供了动态的特性。通过定义一个接口,并使用类型断言,能够判断某个对象是否实现了该接口(即是否拥有某个方法)。
示例代码:
假设要判断某个对象是否有 Speak() 方法:
type Speaker interface {Speak()
}type Person struct{}func (p Person) Speak() {fmt.Println("I can speak!")
}func main() {var obj interface{} = Person{}if speaker, ok := obj.(Speaker); ok {fmt.Println("This object has the Speak method.")speaker.Speak() // 调用该方法} else {fmt.Println("This object does not have the Speak method.")}
}
- 使用 reflect 包进行反射判断
Go 的 reflect 包允许在运行时检查和操作对象的类型和方法。通过反射可以检查一个对象是否拥有某个方法。
import ("fmt""reflect"
)type Person struct{}func (p Person) Speak() {fmt.Println("I can speak!")
}func main() {var obj = Person{}method := reflect.ValueOf(obj).MethodByName("Speak")if method.IsValid() {fmt.Println("This object has the Speak method.")method.Call(nil) // 调用该方法} else {fmt.Println("This object does not have the Speak method.")}
}
总结:
类型断言:通过接口类型断言来判断对象是否实现某个接口(从而拥有对应的方法)。
反射(reflect):利用反射在运行时动态检查对象是否具有指定名称的方法。
相关文章:

go基础面试题汇总第一弹
init函数是什么时候执行的? init的函数的作用是什么? 通常作为程序执行前包的初始化,例如mysql redis 等中间件的初始化 init函数的执行顺序是怎样的? 分不同情况来回答: 在同一个go文件里面如果有多个init方法,它们…...

Redis 实现分布式锁时需要考虑的问题
引言 分布式系统中的多个节点经常需要对共享资源进行并发访问,若没有有效的协调机制,可能会导致数据竞争、资源冲突等问题。分布式锁应运而生,它是一种保证在分布式环境中多个节点可以安全地访问共享资源的机制。而在Redis中,使用…...

百年极限论一直存在百年糊涂话:有正数小于所有正数
百年极限论一直存在百年糊涂话:有正数小于所有(任何、任意)正数。 “对于每个大于0的ε[ε>0],都有非0距离数小于ε”显然是病句:有正数小于每个(所有)正数ε。其中任意(任何&am…...

红日靶场1学习笔记
一、准备工作 1、靶场搭建 靶场地址 靶场描述 靶场拓扑图 其他相关靶场搭建详情见靶场地址相关说明 2、靶场相关主机信息 后续打靶场的过程中,如果不是短时间内完成,可能ip会有变化 主机ip密码角色win7192.168.122.131hongrisec2019!边界服务器win…...

【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
文章目录 从零实现 list 容器:细粒度剖析与代码实现前言1. list 的核心数据结构1.1节点结构分析: 2. 迭代器设计与实现2.1 为什么 list 需要迭代器?2.2 实现一个简单的迭代器2.2.1 迭代器代码实现:2.2.2 解释: 2.3 测试…...

【C#生态园】打造现代化跨平台应用:深度解析.NET桌面应用工具
选择最适合你的.NET UI框架:全面解析六种热门选择 前言 在现代软件开发中,选择合适的桌面应用框架和UI库对于开发人员来说至关重要。本文将介绍几种流行的.NET桌面应用框架和UI库,包括Eto.Forms、Avalonia、ReactiveUI、MahApps.Metro、Mat…...

第二十一章 (动态内存管理)
1. 为什么要有动态内存分配 2. malloc和free 3. calloc和realloc 4. 常⻅的动态内存的错误 5. 动态内存经典笔试题分析 6. 总结C/C中程序内存区域划分 1.为什么要有动态内存管理 我们目前已经掌握的内存开辟方式有 int main() {int num 0; //开辟4个字节int arr[10] …...

机器学习框架总结
机器学习框架是用于构建、训练、评估和部署机器学习模型的工具和库的集合。它们简化了模型开发过程,并提供了预构建的功能、优化的计算性能和对深度学习、监督学习、无监督学习等技术的支持。下面是一些主要的机器学习框架的详细介绍: 1. TensorFlow 1…...

docker pull 超时的问题如何解决
docker不能使用,使用之前的阿里云镜像失败。。。 搜了各种解决方法,感谢B站UP主 <iframe src"//player.bilibili.com/player.html?isOutsidetrue&aid113173361331402&bvidBV1KstBeEEQR&cid25942297878&p1" scrolling"…...

【数学分析笔记】第4章第3节 导数四则运算和反函数求导法则(2)
4. 微分 4.3 导数四则运算与反函数求导法则 双曲正弦函数 sh x e x − e − x 2 \sh x\frac{e^x-e^{-x}}{2} shx2ex−e−x 双曲余弦函数 ch x e x e − x 2 \ch x\frac{e^xe^{-x}}{2} chx2exe−x ch 2 x − sh 2 x 1 \ch^2 x-\sh^2 x1 ch2x−sh2x1 ( e…...

【2024】基于mysqldump的数据备份与恢复
基于mysqldump备份与恢复 mysqldump是一个用于备份 MySQL 数据库的实用工具。 它可以将数据库的结构(如数据库、表、视图、存储过程等的定义)和数据(表中的记录)导出为文本文件,这些文本文件可以包含 SQL 语句&#…...

家用无线路由器配置
一.首先进行线路连接。如下图:"光猫LAN口"—网线—"路由器WAN口"。 注意:家用光纤宽带一般选择使用200兆宽带到1000兆,如果网速不达标请查看路由器是否是千兆路由器。千兆路由器通常是双频的,支持两个信号一个…...

模拟算法(4)_外观数列
个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 模拟算法(4)_外观数列 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌 目录 1. 题目链…...

vsomeip用到的socket
概述: vsomeip用到的socket的代码全部都在implementation\endpoints目录下面,主要分布在下面六个endpoint类中: local_client_endpoint_impl // 本地客户端socket(UDS Socket或者127.0.0.1的socket)local_server…...

MFC有三个选项:MFC ActiveX控件、MFC应用程序、MFC DLL,如何选择?
深耕AI:互联网行业 算法研发工程师 目录 MFC ActiveX 控件 控件的类型 标准控件 自定义控件 ActiveX控件 MFC ActiveX控件 标准/自定义控件 MFC ActiveX控件分类 3种MFC如何选择? MFC ActiveX控件 MFC 应用程序 MFC DLL 总结 举例说明…...

边缘概率 | 条件概率
关于什么是边缘概率分布和条件概率分布,在理论上,我自己也还没有理解,那么现在就根据我学习到的理解方式来记录一下,有错误指出,请大家指正!!! 例如,一个箱子里有十个乒乓…...

深入浅出:现代JavaScript开发者必知必会的Web性能优化技巧
亲爱的读者们,欢迎来到本期博客。今天,我们将深入探讨JavaScript开发者在日常工作中如何提升Web性能。在快节奏的Web开发世界中,性能优化至关重要。本文将分享一些实用技巧,帮助你构建快速、高效的Web应用。 1. 使用CDN加速资源加…...

【S32K3 RTD LLD篇5】K344 ADC SW+HW trigger
【S32K3 RTD LLD篇5】K344 ADC SWHW trigger 一,文档简介二,ADC SW HW 触发2.1 软硬件平台2.2 SWADC 软件触发2.3 SWBCTUADC 软件BCTU触发2.4 PITTRIGMUXADC 硬件PIT TRIGUMX触发2.5 EMIOSBCTUHWADC硬件EMIOS BCTU触发2.6 EMIOSBCTUHW LISTADC硬件EMIOS …...

TransFormer 视频笔记
TransFormer BasicsAttention单头注意力 single head attentionQ: query 查寻矩阵 128*12288K key matrix 128*12288SoftMax 归一 Value matrix 12288*12288 MLP Bas…...

前端的混合全栈之路Meteor篇(三):发布订阅示例代码及如何将Meteor的响应数据映射到vue3的reactive系统
Meteor 3.0 是一个功能强大的全栈 JavaScript 框架,特别适合实时应用程序的开发。它的核心机制之一就包括发布-订阅(Publish-Subscribe)模型,它允许服务器端发布数据,客户端订阅并实时更新。本文将介绍如何在 Meteor 3…...

自动驾驶系列—颠覆未来驾驶:深入解析自动驾驶线控转向系统技术
🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…...

Webstorm 中对 Node.js 后端项目进行断点调试
首先,肯定需要有一个启动服务器的命令脚本。 然后,写一个 debug 的配置: 然后,debug 模式 启动项目和 启动调试服务: 最后,发送请求,即可调试: 这几个关键按钮含义: 重启…...

VUE前后端分离毕业设计题目项目有哪些,VUE程序开发常见毕业论文设计推荐
目录 0 为什么选择Vue.js 1 Vue.js 的主要特点 2 前后端分离毕业设计项目推荐 3 后端推荐 4 总结 0 为什么选择Vue.js 使用Vue.js开发计算机毕业设计是一个很好的选择,因为它不仅具有现代前端框架的所有优点,还能让你专注于构建高性能、高可用性的W…...

一、Spring Boot集成Spring Security之自动装配
Spring Boot集成Spring Security之自动装配介绍 一、实现功能及软件版本说明二、创建Spring Boot项目三、查看自动装配配置类四、自动装配配置类之SecurityAutoConfiguration1、SecurityAutoConfiguration部分源码2、主要作用3、SpringBootWebSecurityConfiguration3.1、Spring…...

计数相关的题 Python 力扣
2284. 最多单词数的发件人 给你一个聊天记录,共包含 n 条信息。给你两个字符串数组 messages 和 senders ,其中 messages[i] 是 senders[i] 发出的一条 信息 。 一条 信息 是若干用单个空格连接的 单词 ,信息开头和结尾不会有多余空格。发件…...

Express内置的中间件(express.json和express.urlencoded)格式的请求体数据
目录 Express内置的中间件 express.json 中间件的使用 express.urlencoded 中间件的使用 express.urlencoded([options]) 解析req.body的兼容写法 Express内置的中间件 自 Express 4.16.0 版本开始,Express 内置了 3 个常用的中间件,极大的提高了 …...

cmakelist加载Qt模块
Qt编程中,cmakelist会自动添加Core,Gui,Widgets模块,有时需要添加新的Qt的模块。在命令find_package中搜索要新增的模块,在命令target_link_libraries中添加要新增的模块。 比如要使用QUiLoader类,要增加对…...

8-2.Android 任务之 CountDownTimer 编码模板(开启计时器、取消计时器)
一、CountDownTimer 1、概述 CountDownTimer 是 Android 中一个用于执行定时操作的类 CountDownTimer 主要应用于在指定时间段内完成某项任务,或者每隔一段时间触发某项任务 2、使用步骤 创建 CountDownTimer:创建 CountDownTimer 就是创建它的匿名…...

Servlet的生命周期及用户提交表单页面的实现(实验报告)
一、实验目的、要求 1. 掌握Servlet的定义,即Servlet是运行在服务器端的Java程序,用于扩展服务器的功能。 2. 学习和掌握在开发环境中搭建Servlet应用所需的工具,如Tomcat服务器、IDEA等。 二、实验内容 根据本章所学知识,实验…...

【Router】路由功能之IP过滤(IP Filter)功能(基于端口)介绍及实现
IP过滤(IP Filter) IP Filter是一种通过对网络数据包中的 IP 地址进行分析和筛选,以实现对网络流量的控制和管理的技术。 IP过滤(IP Filter)作用 安全防护 可以阻止来自特定 IP 地址或 IP 地址范围的恶意攻击、非法访问等,增强网络的安全性。 流量管理 根据不同的 IP …...