面试题

= 和 := 的区别

前者赋值,后者声明+赋值。

指针的作用

指针用来保存变量的地址。

Go 有异常类型吗?

Go 没有异常类型,只有错误类型(Error),通常使用返回值来表示异常状态。

什么是 Goroutine

Goroutine 是与其他函数或方法同时运行的函数或方法。 Goroutines 可以被认为是轻量级的线程。 与线程相比,创建 Goroutine 的开销很小。 Go应用程序同时运行数千个 Goroutine 是非常常见的做法。

defer 的执行顺序

多个 defer 语句,遵从后进先出(Last In First Out,LIFO)的原则,最后声明的 defer 语句,最先得到执行。 defer 在 return 语句之后执行,但在函数退出之前,defer 可以修改返回值。

Go 语言 tag 的用处?

tag 可以理解为 struct 字段的注解,可以用来定义字段的一个或多个属性。框架/工具可以通过反射获取到某个字段定义的属性,采取相应的处理方式。tag 丰富了代码的语义,增强了灵活性。

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*)的一个初始化的(不是零值)的实例。

init函数的特性

  • init 函数在main函数前执行,不能被其他函数调用
  • init 函数没有输入参数、返回值
  • 每个包可以有一个或多个init函数
  • 同一个包的init执行顺序,golang没有明确定义,编程时注意不要依赖这个执行顺序
  • 不同包的init函数的执行顺序依照包导入的依赖关系决定执行顺序
  • import –> const –> var –> init() –> main()

通道特性

  • 对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的(并发安全)
  • 发送操作和接收操作中对元素值的处理都是不可分割的。
  • 发送操作在完全完成之前会被阻塞,接收操作也是一样。
  • 对于缓冲通道:如果通道已满,那么对它的所有发送操作都会被阻塞,直到通道中有元素值被接收走;如果通道已空,那么对它的所有接收操作都会被阻塞,直到通道中有新的元素值出现。
  • 对于非缓冲通道:无论是发送操作还是接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。

注意点:

关闭通道要在发送方关闭,关闭后如果channel内还有元素,并不会对接下来的接收产生影响 单向通道最主要的用途就是约束其他代码的行为 通过函数的参数类型或者返回值类型来限制(Go的语法糖)。

数组与切片的区别?

数组

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

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

数组是值传递;

数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从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)

解释以下命令的作用?

go env: #用于查看go的环境变量
go run: #用于编译并运行go源码文件
go build: #用于编译源码文件、代码包、依赖包
go get: #用于动态获取远程代码包
go install: #用于编译go文件,并将编译结构安装到bin、pkg目录
go clean: #用于清理工作目录,删除编译和安装遗留的目标文件
go version: #用于查看go的版本信息

Go 语言的局部变量分配在栈上还是堆上?

由编译器决定。Go 语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有超出函数范围,就可以在栈上,反之则必须分配在堆上。

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

切片(slice)、字典(map)、通道(channel)、接口(interface)

go语言中指针运算有哪些?

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

什么是协程泄露(Goroutine Leak)?

协程泄露是指协程创建后,长时间得不到释放,并且还在不断地创建新的协程,最终导致内存耗尽,程序崩溃。常见的导致协程泄露的场景有以下几种:

  • 缺少接收器,导致发送阻塞
  • 缺少发送器,导致接收阻塞
  • 死锁(dead lock)
  • 无限循环(infinite loops)

说说进程、线程、协程之间的区别

进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元;同一个进程中可以包括多个线程;进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束。

线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程;进程的创建调用fork或者vfork,而线程的创建调用pthread_create;线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源。

进程是资源分配的单位,而线程是操作系统调度的单位。

进程切换需要的资源很最大,效率很低;线程切换需要的资源一般,效率一般;协程切换任务资源很小,效率高。

多进程、多线程根据cpu核数不一样可能是并行的 也可能是并发的。协程的本质就是使用当前进程在不同的函数代码中切换执行,可以理解为并行。 协程是一个用户层面的概念,不同协程的模型实现可能是单线程,也可能是多线程。

进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。(全局变量保存在堆中,局部变量及函数保存在栈中)

线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是这样的)。

协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面可以开启协程,让程序在特定的时间内运行。

协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。

参考

关注和赞赏都是对小欧莫大的支持! 🤝 🤝 🤝
公众号