慕课go高级工程师实战营

薇__wxywd8 · · 2727 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

33. 【中级】 golang中的引用类型包括()

A. 数组切片

B. map

C. channel

D. interface

参考答案:ABCD

这道题已经过时了,在2013年4月3日的github提交中已经明确说了“Go has no 'reference types'”。而slice源码的说明也由“引用”改成了“描述符”。Go只有值传递,没有所谓的引用传递。上题应该改为哪些类型包含有指针。

34. 【中级】 golang中的指针运算包括()

A. 可以对指针进行自增或自减运算

B. 可以通过“&”取指针的地址

C. 可以通过“*”取指针指向的数据

D. 可以对指针进行下标运算

参考答案:BC

D选项应该也是对的,在Go13.8里指向数组的指针的下标运算是可以支持的:

package main

import "fmt"

func main() {

var a = [3]int{998, 999, 1000}

var ip *[3]int = &a

for i := 0; i < len(a); i++ {

fmt.Printf("ip[%d]=%d\n", i, ip[i])

}

}

输出:

ip[0]=998

ip[1]=999

ip[2]=1000


36. 【中级】下面赋值正确的是()

A. var x = nil

B. var x interface{} = nil

C. var x string = nil

D. var x error = nil

参考答案:BD

可以赋值成nil的变量类型有7种:1)任意类型的指针变量,2)函数变量,3)接口,4)error,5)map, 6)切片 7)通道。这些变量默认值就是nil,实际上都不用赋值成nil。

package main

import "fmt"

func main() {

var ptrI *int

var f func()

var iface interface{}

var err error

var m map[string]string

var sl []string

var ch chan int

if ptrI == nil {fmt.Printf("1) pointer is nil\n")}

if f == nil {fmt.Printf("2) func is nil\n")}

if iface == nil {fmt.Printf("3) interface is nil\n")}

if err == nil {fmt.Printf("4) error is nil\n")}

if m == nil {fmt.Printf("5) map is nil\n")}

if sl == nil {fmt.Printf("6) slice is nil\n")}

if ch == nil {fmt.Printf("7) ch is nil\n")}

}

输出: 

1) pointer is nil

2) func is nil

3) interface is nil

4) error is nil

5) map is nil

6) slice is nil

7) ch is nil

 不能赋值成nil的变量类型,会在编译时报错:

package main

import "fmt"

type Person struct {

name string

}

func main() {

var str string = nil

var person Person = nil

var i int = nil

var b bool = nil

var x = nil

fmt.Printf("str=%v person=%v i=%v b=%v x=%v", str, person, i, b, x)

}

 编译报错:

# command-line-arguments

./tryinit.go:10:6: cannot use nil as type string in assignment

./tryinit.go:11:6: cannot use nil as type Person in assignment

./tryinit.go:12:6: cannot use nil as type int in assignment

./tryinit.go:13:6: use of untyped nil

38. 【中级】从切片中删除一个元素,下面的算法实现正确的是()

A.

func (s *Slice)Remove(value interface{})error {

    for i, v := range *s {

       if isEqual(value, v) {

           if i== len(*s) - 1 {

               *s = (*s)[:i]

           }else {

               *s = append((*s)[:i],(*s)[i + 2:]...)

           }

           return nil

       }

    }

    return ERR_ELEM_NT_EXIST

}

B.

func (s*Slice)Remove(value interface{}) error {

    for i, v:= range *s {

        if isEqual(value, v) {

            *s =append((*s)[:i],(*s)[i + 1:])

            return nil

        }

    }

    returnERR_ELEM_NT_EXIST

}

C.

func (s*Slice)Remove(value interface{}) error {

     for i, v:= range *s {

         if isEqual(value, v) {

             delete(*s, v)

             return nil

         }

     }

     returnERR_ELEM_NT_EXIST

}

D.

func (s*Slice)Remove(value interface{}) error {

    for i, v:= range *s {

        if isEqual(value, v) {

            *s =append((*s)[:i],(*s)[i + 1:]...)

            return nil

        }

    }

    returnERR_ELEM_NT_EXIST

}

参考答案:D

A,切片截取是前闭后开;B,append追加另一个切片时需要加"...";C,delete是map的删除方式

51. 【初级】对于局部变量整型切片x的赋值,下面定义正确的是()

A. x := []int{

                 1, 2, 3,

                 4, 5, 6,

                 }

B. x :=[]int{

           1, 2, 3,

           4, 5, 6

          }

C. x :=[]int{

                1, 2, 3,

                4, 5, 6}

D. x :=[]int{1, 2, 3, 4, 5, 6,}

参考答案:ACD

切片赋值时,最后一个元素后面如果有逗号则“}”可以换行或者不换行,否则”}“必须和最后一个元素保持在同一行。

55. 【初级】关于变量的自增和自减操作,下面语句正确的是()

A. i := 1;i++

B. i := 1;j = i++

C. i := 1;++i

D. i := 1;i--

参考答案:AD

Go目前只支持后置自增自减运算符,而且不能用于赋值或者作为参数传递给函数。

package main

import "fmt"

func main() {

i:=1;i--

j := i++  //错误

if i++ > 0 { //错误

fmt.Printf("i=%v\n", i++) //错误

}

++i //错误

fmt.Printf("i=%v j=%v\n", i, j)

}

编译报错:

# command-line-arguments

./example.go:7:8: syntax error: unexpected ++ at end of statement

./example.go:8:9: syntax error: unexpected >, expecting {

./example.go:9:25: syntax error: unexpected ++, expecting comma or )

./example.go:11:2: syntax error: unexpected ++, expecting }

57. 【中级】关于函数声明,下面语法错误的是()

A. func f(a, b int) (value int, err error)

B. func f(a int, b int) (value int, err error)

C. func f(a, b int) (value int, error)

D. func f(a int, b int) (int, int, error)

参考答案:C

Go语言的函数返回值,要么全是非命名的返回值,要么全是命名的返回值,不能即有非命名的又有命名的返回值,否则编译会报错:

syntax error: mixed named and unnamed function parameters

58. 【中级】如果Add函数的调用代码为:

func main() {

var a Integer = 1

var b Integer = 2

var i interface{} = &a

sum := i.(*Integer).Add(b)

fmt.Println(sum)

}

则Add函数定义正确的是()

A.

type Integer int

func (a Integer) Add(b Integer) Integer {

 return a + b

}

B

type Integer int

func (a Integer) Add(b *Integer) Integer {

 return a + *b

}

C

type Integer int

func (a *Integer) Add(b Integer) Integer {

 return *a + b

}

D

type Integer int

func (a *Integer) Add(b *Integer) Integer {

 return *a + *b

}

参考答案:AC

对于方法的参数而言,指针类型的参数不能接受值类型的参数,反之亦然,所以BD是错的。而对于结构体的方法而言,不论定义成值方法或者指针方法,都可以同时支持用指针或者非指针实例来调用。(可能是在编译期完成的转换,后续还需要确认)有两种特殊情况只能定义成值方法:

一是接口断言成非指针实例,例如把main函数改为:

func main() {

var a Integer = 1

var b Integer = 2

var c interface{} = a

sum := c.(Integer).Add(b)

fmt.Println(sum)

}

则答案是 A

运行C则编译报错:

./example.go:9:20: cannot call pointer method on c.(Integer)

./example.go:9:20: cannot take the address of c.(Integer)

二是类型强制转换,例如把main函数改为:

func main() {

var a int = 1

var b Integer = 2

sum := Integer(a).Add(b)

fmt.Println(sum)

}

则答案是 A

运行C则同样是编译报错:

./example.go:8:19: cannot call pointer method on Integer(a)

./example.go:8:19: cannot take the address of Integer(a)

72. 【中级】关于GetPodAction定义,下面赋值正确的是()

type Fragment interface {

    Exec(transInfo *TransInfo) error

}

type GetPodAction struct {

}

func (g GetPodAction) Exec(transInfo *TransInfo) error {

    return nil

}

A. var fragment Fragment =new(GetPodAction)

B. var fragment Fragment = GetPodAction

C. var fragment Fragment = &GetPodAction{}

D. var fragment Fragment = GetPodAction{}

参考答案:ACD

Fragment是接口所以赋值成指针或者非指针,AC是指针,D是非指针。B编译报错“type GetPodAction is not an expression”

结构体创建实例有两种等价的形式:

1) var g GetPodAction; 2) g :=  GetPodAction{}

82. 【中级】关于接口,下面说法正确的是()

A. 只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等价的,可以相互赋值

B. 如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A

C. 接口查询是否成功,要在运行期才能够确定

D. 接口赋值是否可行,要在运行期才能够确定

参考答案:ABC

接口赋值时编译期就可以报错。

验证代码:

package main

import "fmt"

type A interface {

f1()

f2()

f3()

}

type B interface {

f2()

f1()

}

type AImpl struct {

}

func (a AImpl) f1() {

fmt.Printf("call AImpl f1()\n")

}

func (a AImpl) f2() {

fmt.Printf("call AImpl f2()\n")

}

func (a AImpl) f3() {

fmt.Printf("call AImpl f3()\n")

}

func main() {

var a A = AImpl{}

var b B = a

b.f1()

//接口查询,判断b是否实现了接口A

if c, ok := b.(A); ok {

c.f3()

}

}

输出:

call AImpl f1()

call AImpl f3()

83. 【初级】关于channel,下面语法正确的是()

A. var ch chan int

B. ch := make(chan int)

C. <- ch

D. ch <-

参考答案:ABC

85. 【中级】 golang中大多数数据类型都可以转化为有效的JSON文本,下面几种类型除外()

A. 指针

B. channel

C. complex

D. 函数

参考答案:BCD

87. 【初级】 flag是bool型变量,下面if表达式符合编码规范的是()

A. if flag == 1

B. if flag

C. if flag == false

D. if !flag

参考答案:BD

这道题C也是符合规范的,Go的源代码有很多这样的写法,例如:runtime/pprof/internal/profile:

func encodeBoolOpt(b *buffer, tag int, x bool) {

if x == false {

return

}

encodeBool(b, tag, x)

}

88. 【初级】 value是整型变量,下面if表达式符合编码规范的是()

A. if value == 0

B. if value

C. if value != 0

D. if !value

参考答案:AC

BD两项只能是bool变量,否则编译报错:“non-bool value (type int) used as if condition”,“invalid operation: ! int”

91. 【中级】关于slice或map操作,下面正确的是()

A.

var s []int

s = append(s,1)

B.

var m map[string]int

m["one"] = 1

C.

var s []int

s = make([]int, 0)

s = append(s,1)

D.

var m map[string]int

m = make(map[string]int)

m["one"] = 1

参考答案:ACD

slice和map在插入数据之前都需要分配内存,而append函数内置有内存分配操作,所以在调用append之前可以不用make切片。


93. 【中级】关于channel的特性,下面说法正确的是()

A. 给一个 nil channel 发送数据,造成永远阻塞

B. 从一个 nil channel 接收数据,造成永远阻塞

C. 给一个已经关闭的 channel 发送数据,引起 panic

D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值

参考答案:ABCD

这道题AB两个选项应该是适用于Go的早期版本,在Go1.13.8里面,AB直接报fatal error然后退出。C,D是正确的。

下面是一段能正常运行的代码:

func main() {

ch := make(chan bool)

go func(ch chan bool) {

time.Sleep(1 * time.Second)

b := <-ch

fmt.Printf("b=%v\n", b)

}(ch)

ch <- true

fmt.Printf("exit\n")

}

b=true

exit

按照AB说法,把"ch := make(chan bool)"改成:

func main() {

var ch chan bool

go func(ch chan bool) {

time.Sleep(1 * time.Second)

b := <-ch

fmt.Printf("b=%v\n", b)

}(ch)

ch <- true

fmt.Printf("exit\n")

}

报错退出:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:

main.main()

        /home/zhoupeng/go-test/src/tutorial/example.go:15 +0x5b

goroutine 6 [chan receive (nil chan)]:

main.main.func1(0x0)

        /home/zhoupeng/go-test/src/tutorial/example.go:12 +0x4b

created by main.main

        /home/zhoupeng/go-test/src/tutorial/example.go:10 +0x42

exit status 2

验证C,在“ch <- true”之前加入“close(ch)”,则报错:“panic: send on closed channel”

func main() {

ch := make(chan bool)

go func(ch chan bool) {

time.Sleep(1 * time.Second)

b := <-ch

fmt.Printf("b=%v\n", b)

}(ch)

close(ch)

ch <- true

fmt.Printf("exit\n")

}

验证D,通道写入true之后关闭通道,等待3秒,协程等待1秒后连读2次通道,第一次读到缓冲区里的“true”,第二次缓冲区为空则读取到布尔变量的零值false。注,数值型变量的零值是0,字符串的零值是“”。

func main() {

ch := make(chan bool, 1)

go func(ch chan bool) {

time.Sleep(1 * time.Second)

b1 := <-ch

fmt.Printf("b1=%v\n", b1)

b2 := <-ch

fmt.Printf("b2=%v\n", b2)

}(ch)

ch <- true

close(ch)

time.Sleep(3 * time.Second)

fmt.Printf("exit\n")

}

b1=true

b2=false

exit

94. 【中级】关于无缓冲和有冲突的channel,下面说法正确的是()

A. 无缓冲的channel是默认的缓冲为1的channel

B. 无缓冲的channel和有缓冲的channel都是同步的

C. 无缓冲的channel和有缓冲的channel都是非同步的

D. 无缓冲的channel是同步的,而有缓冲的channel是非同步的

参考答案:D

无缓冲的channel是默认的缓冲为0的channel,对应代码如“make(chan int, 0)”,0是默认值可以省略。有缓冲的channel是非同步的指的是发送/接收操作只在缓冲区满了才阻塞,否则继续执行后面的代码。

缓存为1的时序验证:

func main() {

ch := make(chan int, 1)

go func(ch chan int) {

time.Sleep(2 * time.Second)

i1 := <-ch

log.Printf("i1=%v\n", i1)

time.Sleep(2 * time.Second)

i2 := <-ch

log.Printf("i2=%v\n", i2)

}(ch)

log.Printf("send 1\n")

ch <- 1

log.Printf("send 2\n")

ch <- 2

log.Printf("main sleep\n")

time.Sleep(3 * time.Second)

log.Printf("exit\n")

}

2020/07/13 14:19:26 send 1

2020/07/13 14:19:26 send 2

2020/07/13 14:19:28 i1=1

2020/07/13 14:19:28 main sleep

2020/07/13 14:19:30 i2=2

2020/07/13 14:19:31 exit

从输出可以看出,缓冲区是一个先进先出的队列,发送的顺序是1,2,接收的顺序也是1,2。运行到send2时缓冲区已满,main函数所在协程阻塞,2秒之后另一个协程从通道读取到第一个值1,缓冲区不再是已满的状态,main函数停止阻塞继续执行后面的代码打印“main sleep”,再过2秒之后另一个协程从通道读取到第二个值2。

如果把上述代码改为非缓冲通道,则输出时序为:

2020/07/13 14:37:15 send 1

2020/07/13 14:37:17 i1=1

2020/07/13 14:37:17 send 2

2020/07/13 14:37:19 i2=2

2020/07/13 14:37:19 main sleep

2020/07/13 14:37:22 exit

send1之后就会阻塞,2秒之后等另一个协程读取到第一个值1之后,main再发送send 2,发了之后再阻塞 ,2秒之后等另一个协程读取到第二个值2之后,main解除阻塞。

95. 【中级】关于异常的触发,下面说法正确的是()

A. 空指针解析

B. 下标越界

C. 除数为0

D. 调用panic函数

参考答案:ABCD

96. 【中级】关于cap函数的适用类型,下面说法正确的是()

A. array

B. slice

C. map

D. channel

参考答案:ABD

func main(){

var a [3]int

var sl = make([]int, 1, 5)

var m = make(map[int]int, 100)

var ch = make(chan int, 10)

log.Printf("%d\n",cap(a))

log.Printf("%d\n",cap(sl))

log.Printf("%d\n",cap(m))

log.Printf("%d\n",cap(ch))

}


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:薇__wxywd8

查看原文:慕课go高级工程师实战营

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2727 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传