文章转载地址:https://juejin.im/entry/5971bed66fb9a06bb21adf15

1、写出下面代码的输出

package main

import "fmt"

func main() {
defer_all()
panic("触发异常")
} func defer_all() {
defer func() {
fmt.Println("打印前")
}()
defer func() {
fmt.Println("打印中")
}()
defer func() {
fmt.Println("打印后")
}()
}

  解析:这道题主要考察的是对 defer 的理解,defer 主要是延迟函数,延迟到调用者函数执行 return 命令之前,

多个 defer 之前按照先进后出的顺序执行,所以,这道题中,在 panic 触发时结束函数运行,在 return 之前依次打

印:打印后、打印中、打印前。最后 runtime 运行时抛出打印 panic 异常信息,panic 需要 defer 结束后才会向上传

需要注意的是,函数的 return value 不是原子操作,而是在编译器中被分解成两部分:返回值和return,而我们

知道 defer 是在 return 之前执行的,所以可以在 defer 函数中修改返回值,如下示例:

package main

import (
"fmt"
) func main() {
fmt.Println(doubleScore(0)) //0
fmt.Println(doubleScore(20.0)) //40
fmt.Println(doubleScore(50.0)) //50
}
func doubleScore(source float32) (score float32) {
defer func() {
if score < 1 || score >= 100 {
//将影响返回值
score = source
}
}()
score = source * 2
return //或者
//return source * 2
}

2. 下面的代码输出什么?

package main

import "fmt"

func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
} func main() {
a := 1
b := 2
defer calc("1", a, calc("10", a, b))
a = 0
defer calc("2", a, calc("20", a, b))
b = 1
}

 解析:

程序在执行到第三行的时候,会先执行 calc 函数的 b 参数,即:calc("10",a,b),输出:10,1,2,3 得到值 3,然后因为

defer 定义的函数是延迟函数故 calc("1",1,3) 会被延迟执行

程序执行到第五行的时候,同样先执行 calc("20",a,b) 输出:20,0,2,2 得到值 2,同样将 calc("2",0,2) 延迟执行

程序执行到末尾的时候,按照栈先进后出的方式依次执行:calc("2",0,2),calc("1",1,3),则就依次输出:2,0,2,2、

1,1,3,4

3.请写出以下输出内容

func main() {
s := make([]int, 5)
s = append(s,1,2,3)
fmt.Println(s)
}

  解析:

使用 make 初始化 slice,第二个参数代表的是 slice 的长度,slice 还有第三个参数表示容量,这里没有指定容量表示创建一个

满容的切片,使用 len()、cap() 函数获取切片的 长度,初始化后切片的长度和容量都是 5,使用 append 追加三个元素使得切片的

长度大于原有的容量,此时切片的容量扩大一倍,变成 10,因此输出的结果为:

[0 0 0 0 0 1 2 3]

3. 下面的代码能正常编译吗?

package main

import (
"fmt"
) type People interface {
Speak(string) string
} type Stduent struct{} func (stu *Stduent) Speak(think string) (talk string) {
if think == "bitch" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
} func main() {
var peo People = Stduent{}
think := "bitch"
fmt.Println(peo.Speak(think))
}

  运行打印结果:

# command-line-arguments
.\main.go:23:6: cannot use Stduent literal (type Stduent) as type People in assignment:
Stduent does not implement People (Speak method has pointer receiver)

  从上面的输出信息可以看出 Student 没有实现 People 这个接口

解析:

我们来看一下语言规范里面定义的规则,这些规则用来说明一个类型的值或指针是否实现了该接口:

1.类型 *T 的可调用方法集包含接收者为 *T 或 T 的所有方法集

这条规则说的是如果我们用来调用接口方法的变量是一个指针类型,那么方法的接收者可以是值类型也可以是指针类型,

现在看一下我们的例子显然是不符合规则的,var peo People = Student{} 是一个值类型

2. 类型 T 的可调用方法集包含接收者为 T 的所有方法集

这条规则说的是如果我们用来调用接口方法的变量是一个值类型,那么方法的接收者必须要是值类型才可以被调用,看一下

我们的例子,方法的接收者是指针类型

上面的代码可以这样修改:

1.var peo People = &Student{}

2.将方法的接收者改成值类型

可以参考这篇文章:https://github.com/Unknwon/gcblog/blob/master/content/26-methods-interfaces-and-embedded-types-in-golang.md

4. 下面的代码是死循环吗?

func main() {
v := []int{1, 2, 3}
for i := range v {
v = append(v, i)
}
}

  解析:

直接运行上面的代码我们会发现上面的程序不会出现死循环,能够正常结束。

我们来看一下切片的 for range ,它的底层代码是:

//   for_temp := range
// len_temp := len(for_temp)
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = for_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }

  从底层代码中我们可以看到在遍历 slice 之前会先计算 slice 的长度作为循环次数,循环体中,每次循环会先获取

元素值,如果 for-range 中接收 index,value 则会对 index,value 进行一次赋值

由于循环开始前循环次数已经确定了,所以循环过程中新添加的元素没办法遍历到

参考文章:https://my.oschina.net/renhc/blog/2396058

 5.下面的代码有什么问题吗?

slice := []int{0, 1, 2, 3}
myMap := make(map[int]*int) for index, value := range slice {
myMap[index] = &value
}
fmt.Println("=====new map=====")
for k, v := range myMap {
fmt.Printf("%d => %d\n", k, *v)
}

  运行打印输出结果:

=====new map=====
3 => 3
0 => 3
1 => 3
2 => 3

  结果完全一样,都是最后一次遍历的值。通过第 4 道题目对切片 for-range 的底层代码可知,遍历后

的值赋值给 value,在我们的例子中,会把 value 的地址保存到 myMap 中,这里的 value 是一个全局变量,

&value 取得是这个全局变量的地址,所以最后输出的结果都是一样的并且是最后一个值,相当于如下代码:

        // for_temp := range
// len_temp := len(for_temp)
// for index_temp = 0;index_temp < len_temp;index_temp++ {
// value_temp = for_temp[index_temp]
// index = index_temp
// value = value_temp
// myMap[index] = &value
// original body
// }

注意:这里必须是保存指针才会有问题,如果直接保存的是 value,不会有问题

总结:通过 for-range 遍历切片,首先,计算遍历的次数(切片的长度);每次遍历,都会把当前遍历到的值

存放到一个全局变量中

参考文章:https://juejin.im/entry/5bd7d1ac51882541b558f0b8

       go range 内部实现(Dave 大神):https://garbagecollected.org/2017/02/22/go-range-loop-internals/

Go-常见的面试题(一)的更多相关文章

  1. 部分常见ORACLE面试题以及SQL注意事项

    部分常见ORACLE面试题以及SQL注意事项 一.表的创建: 一个通过单列外键联系起父表和子表的简单例子如下: CREATE TABLE parent(id INT NOT NULL, PRIMARY ...

  2. 【面试必备】常见Java面试题大综合

    一.Java基础 1.Arrays.sort实现原理和Collections.sort实现原理答:Collections.sort方法底层会调用Arrays.sort方法,底层实现都是TimeSort ...

  3. PHPer常见的面试题总结

    1.平时喜欢哪些php书籍及博客?CSDN.虎嗅.猎云 2.js闭包是什么? 3.for与foreach哪个更快? 4.php鸟哥是谁?能不能讲一下php执行原理? 5.php加速器有哪些?apc.z ...

  4. SQLServer 常见SQL笔试题之语句操作题详解

    SqlServer 常见SQL笔试题之语句操作题详解 by:授客 QQ:1033553122 测试数据库 CREATE DATABASE handWriting ON PRIMARY ( name = ...

  5. (转)C/C++ 程序设计员应聘常见 面试笔试 试题深入剖析

    C/C++ 程序设计员应聘常见 面试笔试 试题深入剖析 http://www.nowcoder.com/discuss/1826?type=2&order=0&pos=23&p ...

  6. 关于JavaScript 常见的面试题

    关于JavaScript常见的面试题总结 一.JavaScript基本数据类型 null:空.无.表示不存在,当为对象的属性赋值为null,表示删除该属性 undefined:未定义.当声明变量却没有 ...

  7. HTML5常见的面试题,基础知识点

                                                                                    HTML5常见的面试题 一.HTML 常 ...

  8. HTTP和HTTPS的区别和常见的面试题

    本篇会着重介绍http和https的区别和常见的面试题 常见的http和https面试题: Http与Https的基本概念和他们的区别 HTTPS工作原理 常用的HTTP方法有哪些 GET方法与POS ...

  9. 分享一些JVM常见的面试题(转)

    出处:  分享一些JVM常见的面试题 前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 ...

  10. 【转】常见Java面试题 – 第四部分:迭代(iteration)和递归(recursion)

    ImportNew注: 本文是ImportNew编译整理的Java面试题系列文章之一.你可以从这里查看全部的Java面试系列. Q.请写一段代码来计算给定文本内字符“A”的个数.分别用迭代和递归两种方 ...

随机推荐

  1. es5原型式继承间解

    1. 原型式继承方法 js 继承使用不难,要说清楚,需要自己一定总结,才能说清楚. es5 的继承方式有很多种,这个是 js 语言本身造成,但是类实现继承之后的功能,有如下 3 条: 子类继承父类,主 ...

  2. SpringCloud第六步:搭建分布式配置中心

    什么是配置中心 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件.在Spring Cloud中,有分布式配置中心组件spring cloud con ...

  3. 8、jeecg 笔记之 自定义word 模板导出(一)

    1.前言 jeecg 中已经自带 word 的导出导出功能,其所使用的也是 easypoi,尽管所导出的 word 能满足大部分需求, 但总是有需要用到自定义 word导出模板,下文所用到的皆是 ea ...

  4. 运维面试题之linux编程

    吐槽: linux下的编程基本上都很简单包括shell 三剑客和vim的使用,也可能写ansible的playbook,有基础都是一两天可以学会的,正则表达式都是试出来的不知道有些面试官让我们在纸上写 ...

  5. selenium处理元素定位到了点击无效问题

    在WEB自动化测试过程中,经常会遇到这样的问题: 元素定位到了,但是点击无效?有人可能会问了,怎么判断元素定位到了,这个问题很好判断 1.给元素加高亮显示 self.driver.execute_sc ...

  6. 【备忘】mybatis的条件判断用<choose>

    mybatis并没有if..else,在mybatis的sql mapper文件中,条件判断要用choose..when..otherwise.   <choose> <when t ...

  7. python查询数据库返回数据

    python查询数据库返回数据主要运用到flask框架,pymysql 和 json‘插件’ #!/usr/bin/python # -*- coding: UTF-8 -*- import pymy ...

  8. Aliyun OSS Nginx proxy module(阿里云OSS Nginx 签名代理模块)

    1.此文章主要介绍内容 本文主要介绍如何利用Nginx lua 实现将阿里云OSS存储空间做到同本地磁盘一样使用.核心是利用Nginx lua 对OSS请求进行签名并利用内部跳转将所有访问本地Ngin ...

  9. ORACLE 数据库管理

    [故障处理]ORA-12162: TNS:net service name is incorrectly specified 本文将给大家阐述一个因未设置系统环境变量ORACLE_SID导致ORA-1 ...

  10. Mysql安装、设置密码、编码

    一.MySQL介绍 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司.MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是 ...