前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go的面试笔试基础考察区别点

Go的面试笔试基础考察区别点

原创
作者头像
小诚信驿站
发布2019-10-04 20:43:58
1.5K0
发布2019-10-04 20:43:58
举报
想要坏坏的蓝猫,可以微信表情包搜索:墩墩猫
想要坏坏的蓝猫,可以微信表情包搜索:墩墩猫

面试:

1)go写的递归函数调用栈会溢出吗?:

代码语言:javascript
复制
大部分编程语言使用固定大小的函数调用栈,常见的大小从64KB到2MB不等。 
固定大小栈会限制 递归的深度,当你用递归处理大量数据时,需要避免栈溢出; 除此之外,还会导致安全性问题。 
与 相反,Go语言使用可变栈,栈的大小按需增加(初始时很小)。
这使得我们使用递归时不必考虑溢出 和安全问题

2) 函 数值可以比较么?

代码语言:javascript
复制
// squares返回一个匿名函数。
// 该匿名函数每次被调用时都会返回下一个数的平方。 
func squares() func() int {
var x int
return func() int {
   x++
   return x * x 
   }
}
func main() {
 f := squares() 
 fmt.Println(f())
 fmt.Println(f()) 
 fmt.Println(f()) 
 fmt.Println(f())
// "1"
// "4"
// "9"
// "16"
}
函数squares返回另一个类型为 func() int 的函数。对squares的一次调用会生成一个局部变量x并返 回一个匿名函数。
每次调用时匿名函数时,该函数都会先使x的值加1,再返回x的平方。第二次调 用squares时,会生成第二个x变量,
并返回一个新的匿名函数。新匿名函数操作的是第二个x变 量。

squares的例子证明,函数值不仅仅是一串代码,还记录了状态。在squares中定义的匿名内部函数 
可以访问和更新squares中的局部变量,这意味着匿名函数和squares中,存在变量引用。
这就是函 数值属于引用类型和函数值不可比较的原因。Go使用闭包(closures)技术实现函数值,
Go程序 员也把函数值叫做闭包。
通过这个例子,我们看到变量的生命周期不由它的作用域决定:squares返回后,变量x仍然隐式的 存在于f中。

3) 在Go的panic机制中,延迟函数的调用在释放堆栈信息之前 还是之后?

4)defer函数调用recover函数恢复 Panic造成的异常 ,不可以恢复的场景有哪些?

某些致命错误会导致Go在运行时终止程序,如内存不足 ,这种情况没法恢复。

5)函数与方法的区别

在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这 种类型上,即相当于为这种类型定义了一个独占的方法。

6)接口值是否可以比较

接口值可以使用==和!=来进行比较。两个接口值相等仅当它们都是nil值或者它们的动态类型相 同并且动态值也根据这个动态类型的==操作相等。因为接口值是可比较的,所以它们可以用在 map的键或者作为switch语句的操作数

然而,如果两个接口值的动态类型相同,但是这个动态类型是不可比较的(比如切片),将它们进 行比较就会失败并且panic:

⚠️:什么时候可以比较,什么时候不可以比较呢?

可比较类型(如基本类型和指 针),完全不可比较的类型(如切片,映射类型,和函数),但是在比较接口值或者包含了接 口值的聚合类型时,我们必须要意识到潜在的panic。

#############################分割线###############################################

笔试:

1)多返回值之 bare return

代码语言:javascript
复制
func CountWordsAndImages(url string) (words, images int, err error) {
resp, err := http.Get(url) if err != nil {
return
}
doc, err := html.Parse(resp.Body) resp.Body.Close()
if err != nil {
err = fmt.Errorf("parsing HTML: %s", err) return
}
words, images = countWordsAndImages(doc) return
}
func countWordsAndImages(n *html.Node) (words, images int) { /* ... */ }

Q:看上面的伪代码,会发现一个问题,明明是有返回值的函数,为什么直接return。这样表达是否有错误?

A:如果一个函数将所有的返回值都显示的变量名,那么该函数的return语句可以省略操作数。这称之 为bare return。 按照返回值列表的次序,返回所有的返回值,在上面的例子中,每一个return语句等价于:

return words, images, err

⚠️注意: 当一个函数有多处return语句以及许多返回值时,bare return 可以减少代码的重复,但是使得代码 难以被理解。举个例子,如果你没有仔细的审查代码,很难发现前2处return等价于 return 0,0,err(Go会将返回值 words和images在函数体的开始处,根据它们的类型,将其初始化为0), 最后一处return等价于 return words,image,nil。基于以上原因,不宜过度使用bare return。

2)循环变量的作用域问题

代码语言:javascript
复制
假设这个错误的伪代码:
var rmdirs []func()
for _, dir := range tempDirs() {
os.MkdirAll(dir, 0755)
rmdirs = append(rmdirs, func() {
os.RemoveAll(dir) // NOTE: incorrect! })
}
//上面的代码哪里错了呢?能找出来么?与下面的代码有那些区别呢
var rmdirs []func()
for _, d := range tempDirs() {
dir := d // NOTE: necessary!
os.MkdirAll(dir, 0755) // creates parent directories too 
rmdirs = append(rmdirs, func() {
os.RemoveAll(dir) })
}

Q:为什么要在循环体中用循环变量d赋值一个新的局部变量,而不是直接使用循环变量dir

A: 问题的原因在于循环变量的作用域。在上面的程序中,for循环语句引入了新的词法块,循环变量 dir在这个词法块中被声明。在该循环中生成的所有函数值都共享相同的循环变量。需要注意,函数 值中记录的是循环变量的内存地址,而不是循环变量某一时刻的值。以dir为例,后续的迭代会不断 更新dir的值,当删除操作执行时,for循环已完成,dir中存储的值等于最后一次迭代的值。这意味 着,每次对os.RemoveAll的调用删除的都是相同的目录。 通常,为了解决这个问题,我们会引入一个与循环变量同名的局部变量,作为循环变量的副本

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 面试:
    • 1)go写的递归函数调用栈会溢出吗?:
      • 2) 函 数值可以比较么?
        • 3) 在Go的panic机制中,延迟函数的调用在释放堆栈信息之前 还是之后?
          • 4)defer函数调用recover函数恢复 Panic造成的异常 ,不可以恢复的场景有哪些?
            • 5)函数与方法的区别
              • 6)接口值是否可以比较
              • 笔试:
                • 1)多返回值之 bare return
                  • 2)循环变量的作用域问题
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档