o语言面试题汇总[通俗易懂]正在准备golang面试的小伙伴,很荣幸,你能看到这篇文章,希望每一个看到这篇分享的小伙伴都能找到自己称心如意的工作!

1、说说go语言的main函数
(1)、main函数不能带参数。

(2)、main函数不能定义返回值。

(3)、main函数所在的包必须为main包。

(4)、main函数中可以使用flag包来获取和解析命令行参数。

2、在go语言中,new和make的区别?

new函数是内建函数,函数定义为

func new(Type) *Type 

3.指针的作用

一个指针可以指向任意变量的地址,它所指向的地址在32位或64位机器上分别固定占4或8个字节。指针的作用有:

  • 获取变量的值
 import fmt
 ​
 func main(){
  a := 1
  p := &a//取址&
  fmt.Printf("%d\n", *p);//取值*
 }
  • 改变变量的值
 // 交换函数
 func swap(a, b *int) {
     *a, *b = *b, *a
 }
  • 用指针替代值传入函数,比如类的接收器就是这样的。
 type A struct{}
 ​
 func (a *A) fun(){}

4.协程,线程,进程的区别?

进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

线程

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

协程

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

5.Go 有异常类型吗?

有。Go用error类型代替try...catch语句,这样可以节省资源。同时增加代码可读性:

 _, err := funcDemo()
if err != nil {
    fmt.Println(err)
    return
}

也可以用errors.New()来定义自己的异常。errors.Error()会返回异常的字符串表示。只要实现error接口就可以定义自己的异常,

 type errorString struct {
  s string
 }
 ​
 func (e *errorString) Error() string {
  return e.s
 }
 ​
 // 多一个函数当作构造函数
 func New(text string) error {
  return &errorString{text}
 }

6、slice,len,cap,共享,扩容

append函数,因为slice底层数据结构是,由数组、len、cap组成,所以,在使用append扩容时,会查看数组后面有没有连续内存快,有就在后面添加,没有就重新生成一个大的素组。

7.context包的用途

Context通常被译作上下文,它是一个比较抽象的概念,其本质,是【上下上下】存在上下层的传递,上会把内容传递给下。在Go语言中,程序单元也就指的是Goroutine。

strings.join

strings.joinstrings.builder

性能比较

strings.Join ≈ strings.Builder > bytes.Buffer > "+" > fmt.Sprintf

5种拼接方法的实例代码

func main(){
	a := []string{"a", "b", "c"}
	//方式1:+
	ret := a[0] + a[1] + a[2]
	//方式2:fmt.Sprintf
	ret := fmt.Sprintf("%s%s%s", a[0],a[1],a[2])
	//方式3:strings.Builder
	var sb strings.Builder
	sb.WriteString(a[0])
	sb.WriteString(a[1])
	sb.WriteString(a[2])
	ret := sb.String()
	//方式4:bytes.Buffer
	buf := new(bytes.Buffer)
	buf.Write(a[0])
	buf.Write(a[1])
	buf.Write(a[2])
	ret := buf.String()
	//方式5:strings.Join
	ret := strings.Join(a,"")
}

8.Log包线程安全吗?

Golang的标准库提供了log的机制,但是该模块的功能较为简单(看似简单,其实他有他的设计思路)。在输出的位置做了线程安全的保护。

9.defer 的执行顺序

defer执行顺序和调用顺序相反,类似于栈后进先出(LIFO)。

defer在return之后执行,但在函数退出之前,defer可以修改返回值。下面是一个例子:

func test() int {
	i := 0
	defer func() {
		fmt.Println("defer1")
	}()
	defer func() {
		i += 1
		fmt.Println("defer2")
	}()
	return i
}

func main() {
	fmt.Println("return", test())
}
// defer2
// defer1
// return 0
func test() (i int)
func test() (i int) {
	i = 0
	defer func() {
		i += 1
		fmt.Println("defer2")
	}()
	return i
}

func main() {
	fmt.Println("return", test())
}
// defer2
// return 1

这个例子中,返回值被修改了。对于有名返回值的函数,执行 return 语句时,并不会再创建临时变量保存,因此,defer 语句修改了 i,即对返回值产生了影响。

10.并发编程概念是什么?

并行是指两个或者多个事件在同一时刻发生;并发是指两个或多个事件在同一时间间隔发生。

发偏重于多个任务交替执行,而多个任务之间有可能还是串行的。而并行是真正意义上的“同时执行

并发编程是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。