package main
import "fmt"
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := slice[2:5]
s2 := s1[2:6:7]
s2 = append(s2, 100)
s2 = append(s2, 200)
s1[2] = 20
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(slice)
}
答案:
[2 3 20]
[4 5 6 7 100 200]
[0 1 2 3 20 5 6 7 100 9]
题目2:
package main
func main() {
s := []int{1, 1, 1}
f(s)
fmt.Println(s)
}
func f(s []int) {
for i := range s {
s[i] += 1
}
}
答案:[2 2 2]
题目3:
package main
import "fmt"
func myAppend(s []int) []int {
s = append(s, 100)
return s
}
func myAppendPtr(s *[]int) {
*s = append(*s, 100)
return
}
func main() {
s := []int{1, 1, 1}
newS := myAppend(s)
fmt.Println(s)
fmt.Println(newS)
s = newS
myAppendPtr(&s)
fmt.Println(s)
}
结果:[1 1 1] [1 1 1 100] [1 1 1 100 100]
题目4:
func test() int {
x := 1
defer func() {
x = 2
}()
return x
}
func main() {
fmt.Println(test())
}
/*
return x 先计算 x 的值,即 1,但还未真正返回。
defer 执行,将 x 赋值为 2,但不会影响已计算的返回值。
最终返回 1。
*/
题目5:
func test() (x int) {
x = 1
defer func() {
x = 2
}()
return x
}
func main() {
fmt.Println(test())
}
/*
x 是命名返回值,return x 计算时
x=1,但未立即返回。
defer 执行,把 x 改为 2。
return 返回 x=2,最终返回值是 2。
*/
题目6:
func deferFunc() int {
fmt.Println("defer func called")
return 0
}
func returnFunc() int {
fmt.Println("return func called")
return 0
}
func returnAndDefer() int {
defer deferFunc()
return returnFunc()
}
func main() {
returnAndDefer()
}
/*
结果:return func called
defer func called
结论为:return之后的语句先执行,defer后的语句后执行*/
题目7:
func main() {
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
panic("发生 panic 了!") // 触发 panic
defer fmt.Println("defer 3") // 这行不会执行
}
/*
结果:
defer 2
defer 1
panic: 发生 panic 了!
*/
func main() {
defer_call()
fmt.Println("main 正常结束")
}
func defer_call() {
defer func() {
fmt.Println("defer: panic 之前1, 捕获异常")
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer func() { fmt.Println("defer: panic 之前2, 不捕获") }()
panic("异常内容") //触发defer出栈
defer func() { fmt.Println("defer: panic 之后, 永远执行不到") }()
}
/*
结果:
defer: panic 之前2, 不捕获
defer: panic 之前1, 捕获异常
异常内容
main 正常结束
*/
func main() {
defer fmt.Println("defer 1")
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获 panic:", r)
}
}()
defer fmt.Println("defer 2")
panic("触发 panic")
defer fmt.Println("defer 3") // 不会执行
}
/*
结果:
defer 2
捕获 panic: 触发 panic
defer 1
*/
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
} else {
fmt.Println("fatal")
}
}()
defer func() {
panic("defer panic")
}()
panic("panic")
}
/*
结果:
defer panic
recover() 只能捕获最近的 panic,不会捕获被后续 panic 覆盖的 panic。
后进先出(LIFO)执行 defer 语句,所以 defer panic 发生后,原来的 panic("panic") 就被丢弃了。
如果 recover() 能捕获 panic,则程序不会崩溃。*/
题目8:
func function(index int, value int) int {
fmt.Println(index)
return index
}
func main() {
defer function(1, function(3, 0))
defer function(2, function(4, 0))
}
/*
结果:
3
4
2
1
这里面有两个defer, 所以defer一共会压栈两次,先进栈1,后进栈2。
那么在压栈function1的时候,需要连同函数地址、函数形参一同进栈,
那么为了得到function1的第二个参数的结果,所以就需要先执行function3将第二个参数算出,
那么function3就被第一个执行。同理压栈function2,就需要执行function4算出function2第二个参数的值。
然后函数结束,先出栈fuction2、再出栈function1.
*/
题目9:
package main
import "fmt"
func DeferFunc1(i int) (t int) {
t = i // t = 1
defer func() {
t += 3 // t = t + 3 = 1 + 3 = 4
}()
return t // 先计算返回值 t=1,然后执行 defer 修改 t=4
}
func DeferFunc2(i int) int {
t := i // t = 1 (局部变量)
defer func() {
t += 3 // 由于 t 不是命名返回值,对返回值无影响
}()
return t // 直接返回 t=1,defer 修改的是局部变量,不影响返回值
}
func DeferFunc3(i int) (t int) {
defer func() {
t += i // t = t + i = 2 + 1 = 3
}()
return 2 // 先计算返回值 t=2,再执行 defer 修改 t=3
}
func DeferFunc4() (t int) {
defer func(i int) {
fmt.Println(i) // 0 (因为 defer 传递的是 t 的拷贝)
fmt.Println(t) // 2 (defer 运行时 t 已被 return 修改)
}(t)
t = 1
return 2 // 先计算返回值 t=2,再执行 defer
}
func main() {
fmt.Println(DeferFunc1(1)) // 1 -> 4
fmt.Println(DeferFunc2(1)) // 1
fmt.Println(DeferFunc3(1)) // 3
DeferFunc4() // 0, 2
}
/*
结果:
4
1
3
0
2
*/
题目10:
package main
import (
"fmt"
)
type student struct {
Name string
Age int
}
func main() {
//定义map
m := make(map[string]*student)
//定义student数组
stus := []student{
{Name: "zhou", Age: 24},
{Name: "li", Age: 23},
{Name: "wang", Age: 22},
}
//将数组依次添加到map中
for _, stu := range stus {
m[stu.Name] = &stu
}
//打印map
for k,v := range m {
fmt.Println(k ,"=>", v.Name)
}
}
/*
结果:
遍历结果出现错误,输出结果为
zhou => wang
li => wang
wang => wang
map 中的 3 个 key 均指向数组中最后一个结构体。
*/
题目11:
func main() {
s := make(map[string]int)
delete(s, "h")
fmt.Println(s["h"])
}
A. runtime panic
B. 0
C. compilation error
/*
删除 map 不存在的键值对时,不会报错,相当于没有任何作用;
获取不存在的减值对时,返回值类型对应的零值,所以返回 0。
*/
题目12:
func main() {
i := -5
j := +5
fmt.Printf("%+d %+d", i, j)
}
A. -5 +5
B. +5 +5
C. 0 0
/*
A,%d表示输出十进制数字,+表示输出数值的符号。
*/
题目13:
type People struct{}
func (p *People) ShowA() {
fmt.Println("showA")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("showB")
}
type Teacher struct {
People
}
func (t *Teacher) ShowB() {
fmt.Println("teacher showB")
}
func main() {
t := Teacher{}
t.ShowB()
}
/*
teacher showB。
*/
题目14:
定义一个包内全局字符串变量,下面语法正确的是(多选):
A. var str string
B. str := ""
C. str = ""
D. var str = ""
/*
AD。
B 只支持局部变量声明;C 是赋值,str 必须在这之前已经声明。
*/
题目15:
func hello(i int) {
fmt.Println(i)
}
func main() {
i := 5
defer hello(i)
i = i + 10
}
/*
5。即使 hello 函数被延迟执行,它所获取的 i 参数的值会在 defer 语句被执行时已经确定
*/
题目16:
func incr(p *int) int {
*p++
return *p
}
func main() {
p :=1
incr(&p)
fmt.Println(p)
}
A. 1
B. 2
C. 3
/*
B。指针,incr() 函数里的 p 是 *int 类型的指针,指向的是 main() 函数的变量 p 的地址。
第 2 行代码是将该地址的值执行一个自增操作,incr() 返回自增后的结果。
*/
题目17:
对add()函数调用正确的是?
func add(args ...int) int {
sum := 0
for _, arg := range args {
sum += arg
}
return sum
}
A. add(1, 2)
B. add(1, 3, 7)
C. add([]int{1, 2})
D. add([]int{1, 3, 7}...)
/*
ABD。可变函数,因为它要求传递若干 int,A、B 显然正确;
C 是数组或切片,不对;D 把切片展开,其实就是若干 int,也对
*/
题目18:
func main() {
ch1 := make(chan int)
go fmt.Println(<-ch1)
ch1 <- 5
time.Sleep(1 * time.Second)
}
A:5、
B:不能编译;
C:运行时死锁
/*
C。go 语句后面的函数调用,其参数会先求值,这和普通的函数调用求值一样。go fmt.Println(<-ch1) 语句中的 <-ch1 是在 main goroutine 中求值的。这相当于一个无缓冲的 chan,发送和接收操作都在一个 goroutine 中(main goroutine)进行,因此造成死锁。
defer 语句也要注意。比如下面的做法是不对的:defer recover()
而应该使用这样的方式:
defer func() {
recover()
}()
*/
题目19:
func main() {
ch1 := make(chan int)
go func(){
fmt.Println(<-ch1)
}()
ch1 <- 5
time.Sleep(1 * time.Second)
}
A:5、
B:不能编译;
C:运行时死锁
/*
A。当 ch1 <- 5 执行时,主 goroutine 会阻塞在这一行,
直到另一个 goroutine 开始执行并准备好接收数据为止。
因为在这个例子中,接收操作是由匿名 goroutine 执行的,
所以发送和接收操作是在不同的 goroutine 中执行的,不会发生死锁
*/
题目20:
func main() {
// 创建一个 map
m := map[string]int{"a": 1, "b": 2, "c": 3}
// 保存要删除的键
keysToDelete := []string{}
// 遍历 map
for key, value := range m {
fmt.Println(key, value)
// 满足某个条件时,保存要删除的键
if value == 2 {
keysToDelete = append(keysToDelete, key)
}
}
// 在遍历完成后删除元素
for _, key := range keysToDelete {
delete(m, key)
}
/*
A 21
B 22
C 23
counter is 3
或
B 22
C 23
counter is 2
取决于 map 的遍历顺序。
*/
题目21:
关于switch语句,下面说法正确的有?
A. 条件表达式必须为常量或者整数;
B. 单个case中,可以出现多个结果选项;
C. 需要用break来明确退出一个case;
D. 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case;
/*
参考答案及解析:BD。
switch 的条件表达式可以是任意类型(不限于常量或整数)
switch x {
case 1, 2, 3: // 匹配 1、2 或 3
fmt.Println("x is 1, 2, or 3")
case 4, 5:
fmt.Println("x is 4 or 5")
}
Go 的 switch 默认自动 break,不需要手动写 break。
如果想继续执行下一个 case,必须显式使用 fallthrough:
switch x {
case 1:
fmt.Println("1") // 默认 break,不会进入 case 2
case 2:
fmt.Println("2")
}
switch x {
case 1:
fmt.Println("1")
fallthrough // 强制进入 case 2
case 2:
fmt.Println("2") // 会执行
default:
fmt.Println("default")
}
*/
题目22:
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
panic("触发异常")
defer func() { fmt.Println("打印后") }()
}
/*
打印中
打印前
panic: 触发异常
*/
题目23:
func main() {
var i interface{}
if i == nil {
fmt.Println("nil")
return
}
fmt.Println("not nil")
}
A. nil B. not nil C. compilation error
/*
B. not nil
*/
题目24:
func main() {
var b *int = nil
var i interface{} = b
if i == (*int)(nil){
fmt.Println("nil")
return
}
fmt.Println("not nil")
}
A. nil B. not nil C. compilation error
/*
A. nil
*/
题目25:
// 1.
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
}
// 2.
func main() {
s := make([]int,0)
s = append(s,1,2,3,4)
fmt.Println(s)
}
/*
[0 0 0 0 0 1 2 3]
[1 2 3 4]
*/
题目26:
通过指针变量 p 访问其成员变量 name,有哪几种方式?
A.p.name
B.(&p).name
C.(*p).name
D.p->name
/*
AC。& 取址运算符,* 指针解引用
*/
题目27:
下面这段代码能否编译通过?如果可以,输出什么?
const (
x = iota
_
y
z = "zz"
k
p = iota
)
func main() {
fmt.Println(x,y,z,k,p)
}
/*
0 2 zz zz 5
_: 是一个空白标识符,用于占位,它会使 iota 增加,但不会创建一个命名常量。
k: 没有显式赋值,因此会重复上一个非空白标识符的值,即 "zz"
p: 被赋值为当前 iota 的值。由于 iota 在上一行定义时未被使用(显式赋值中断了 iota 的递增),
所以 iota 会继续递增
*/
题目28:
下面这段代码能否编译通过?如果可以,输出什么?
func GetValue() int {
return 1
}
func main() {
i := GetValue()
switch i.(type) {
case int:
println("int")
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}
}
/*
编译错误,正确写法如下
func GetValue() interface{} {
return 1
}
func main() {
i := GetValue()
switch v := i.(type) {
case int:
println("int")
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}
}
*/
题目29:
type person struct {
name string
}
func main() {
var m map[person]int
p := person{"mike"}
fmt.Println(m[p])
}
A.0
B.1
C.Compilation error
/*
A。打印一个 map 中不存在的值时,返回元素类型的零值。
这个例子中,m 的类型是 map[person]int,因为 m 中不存在 p,所以打印 int 类型的零值,即 0。
*/
题目30:
func main() {
a := 5
b := 8.1
fmt.Println(a + b)
}
A.13.1
B.13
C.compilation error
/*
C。a 的类型是 int,b 的类型是 float,两个不同类型的数值不能相加,编译报错
*/
题目31:
func main() {
a := [5]int{1, 2, 3, 4, 5}
t := a[3:4:4]
fmt.Println(t[0])
}
A.3
B.4
C.compilation error
/*
B。[i,j,k],长度:j-i,容量:k-i。
*/
题目32:
func main() {
a := [2]int{5, 6}
b := [3]int{5, 6}
if a == b {
fmt.Println("equal")
} else {
fmt.Println("not equal")
}
}
A. compilation error
B. equal
C. not equal
/*
A。Go 中的数组是值类型,可比较,另外一方面,数组的长度也是数组类型的组成部分,
所以 a 和 b 是不同的类型,是不能比较的,所以编译错误。
*/
题目33:
关于 cap() 函数的适用类型,下面说法正确的是()
A. array
B. slice
C. map
D. channel
/*
ABD
len() 和 cap() 的区别:
len() 适用于 array、slice、map、channel,返回当前元素数量。
cap() 适用于 array、slice、channel,返回底层存储容量。
对 map 使用 cap() 会直接导致编译错误。
*/
题目34:
type People struct{}
func (p *People) ShowA() {
fmt.Println("showA")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("showB")
}
type Teacher struct {
People
}
func (t *Teacher) ShowB() {
fmt.Println("teacher showB")
}
func main() {
t := Teacher{}
t.ShowA()
}
/*
showA showB
结构体嵌套。Teacher 没有自己 ShowA(),所以调用内部类型 People 的同名方法,
需要注意的是第 5 行代码调用的是 People 自己的 ShowB 方法。
*/
题目35:
下面代码下划线处可以填入哪个选项?
func main() {
var s1 []int
var s2 = []int{}
if __ == nil {
fmt.Println("yes nil")
}else{
fmt.Println("no nil")
}
}
A. s1
B. s2
C. s1、s2 都可以
/*
A
s1 被声明为一个切片类型,但没有被初始化。在这种情况下,s1 的默认值是 nil。
s2 被声明为一个空的切片。虽然 s2 是空的,但它已经被初始化,不是 nil。
一个空切片的长度和容量为零,但它不等于 nil。
*/
题目36:
func main() {
i := 65
fmt.Println(string(i))
}
A. A
B. 65
C. compilation error
/*
A。UTF-8 编码中,十进制数字 65 对应的符号是 A。
*/
题目37:
func main() {
var i interface{}
if i == nil {
fmt.Println("nil")
return
}
fmt.Println("not nil")
}
A. nil
B. not nil
C. compilation error
/*
A。当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil。
*/
题目38:
const (
a = iota
b = iota
)
const (
name = "name"
c = iota
d = iota
)
func main() {
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
/*
参考答案及解析:0 1 1 2。
知识点:iota 的用法。
iota 是 golang 语言的常量计数器,只能在常量的表达式中使用。
iota 在 const 关键字出现时将被重置为0,const中每新增一行常量声明将使 iota 计数一次。
*/
题目39:
type Math struct {
x, y int
}
var m = map[string]Math{
"foo": Math{2, 3},
}
func main() {
m["foo"].x = 4
fmt.Println(m["foo"].x)
}
A. 4
B. compilation error
/*
参考答案及解析:B,编译报错 cannot assign to struct field m["foo"].x in map。
错误原因:对于类似 X = Y的赋值操作,必须知道 X 的地址,才能够将 Y 的值赋给 X,
但 go 中的 map 的 value 本身是不可寻址的。
有两个解决办法:
a.使用临时变量
type Math struct {
x, y int
}
var m = map[string]Math{
"foo": Math{2, 3},
}
func main() {
tmp := m["foo"]
tmp.x = 4
m["foo"] = tmp
fmt.Println(m["foo"].x)
}
b.修改数据结构
type Math struct {
x, y int
}
var m = map[string]*Math{
"foo": &Math{2, 3},
}
func main() {
m["foo"].x = 4
fmt.Println(m["foo"].x)
fmt.Printf("%#v", m["foo"]) // %#v 格式化输出详细信息
}
*/
题目40:
var p *int
func foo() (*int, error) {
var i int = 5
return &i, nil
}
func bar() {
//use p
fmt.Println(*p)
}
func main() {
p, err := foo()
if err != nil {
fmt.Println(err)
return
}
bar()
fmt.Println(*p)
}
A. 5 5
B. runtime error
/*
参考答案及解析:B。知识点:变量作用域。问题出在操作符:=,对于使用:=定义的变量,
如果新变量与同名已定义的变量不在同一个作用域中,那么 Go 会新定义这个变量。
对于本例来说,main() 函数里的 p 是新定义的变量,会遮住全局变量 p,导致执行到bar()时程序,
全局变量 p 依然还是 nil,程序随即 Crash。
func main() {
var err error
p, err = foo() // 使用赋值(=)而不是短声明(:=)
if err != nil {
fmt.Println(err)
return
}
bar() // 现在全局 p 有效,输出 5
fmt.Println(*p) // 输出 5
}
*/
题目41:
A、B、C、D 哪些选项有语法错误?
type S struct {
}
func f(x interface{}) {
}
func g(x *interface{}) {
}
func main() {
s := S{}
p := &s
f(s) //A
g(s) //B
f(p) //C
g(p) //D
}
/*
语法错误的选项:B 和 D
*interface{} 是一种特殊的指针类型,不能直接从其他类型的值或指针隐式转换。
var i interface{} = s
g(&i)
var i interface{} = p
g(&i)
*/
题目42:
下面 A、B 两处应该填入什么代码,才能确保顺利打印出结果?
type S struct {
m string
}
func f() *S {
return __ //A
}
func main() {
p := __ //B
fmt.Println(p.m) //print "foo"
}
/*
参考答案及解析:
A. &S{"foo"}
B. *f() 或者 f()
f() 函数返回参数是指针类型,所以可以用 & 取结构体的指针;B 处,
如果填 *f(),则 p 是 S 类型;如果填 f(),则 p 是 *S 类型,
不过都可以使用 p.m 取得结构体的成员
*/
题目43:
实现使用字符串函数名,调用函数。
思路:采用反射的Call方法实现。
package main
import (
"fmt"
"reflect"
)
type Animal struct{
}
func (a *Animal) Eat(){
fmt.Println("Eat")
}
func main(){
a := Animal{}
reflect.ValueOf(&a).MethodByName("Eat").Call([]reflect.Value{})
}
题目44:
有三个函数,分别打印"cat", "fish","dog"要求每一个函数都用一个goroutine,按照顺序打印100次。
package main
import (
"fmt"
"time"
)
var dog = make(chan struct{})
var cat = make(chan struct{})
var fish = make(chan struct{})
func Dog() {
<-fish
fmt.Println("dog")
dog <- struct{}{}
}
func Cat() {
<-dog
fmt.Println("cat")
cat <- struct{}{}
}
func Fish() {
<-cat
fmt.Println("fish")
fish <- struct{}{}
}
func main() {
for i := 0; i < 100; i++ {
go Dog()
go Cat()
go Fish()
}
fish <- struct{}{}
time.Sleep(10 * time.Second)
}
题目45:
两个协程交替打印10个字母和数字
思路:采用channel来协调goroutine之间顺序。
package main
import (
"fmt"
"time"
)
var word = make(chan struct{}, 1)
var num = make(chan struct{}, 1)
func printNums() {
for i := 0; i < 10; i++ {
<-word
fmt.Println(1)
num <- struct{}{}
}
}
func printWords() {
for i := 0; i < 10; i++ {
<-num
fmt.Println("a")
word <- struct{}{}
}
}
func main() {
num <- struct{}{}
go printNums()
go printWords()
time.Sleep(time.Second * 1)
}
题目46:
启动 2个groutine 2秒后取消, 第一个协程1秒执行完,第二个协程3秒执行完。
思路:采用ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
实现2s取消。协程执行完后通过channel通知,是否超时。
package main
import (
"context"
"fmt"
"time"
)
func f1(in chan struct{}) {
time.Sleep(1 * time.Second)
in <- struct{}{}
}
func f2(in chan struct{}) {
time.Sleep(3 * time.Second)
in <- struct{}{}
}
func main() {
ch1 := make(chan struct{})
ch2 := make(chan struct{})
ctx, _ := context.WithTimeout(context.Background(), 2*time.Second)
go func() {
go f1(ch1)
select {
case <-ctx.Done():
fmt.Println("f1 timeout")
break
case <-ch1:
fmt.Println("f1 done")
}
}()
go func() {
go f2(ch2)
select {
case <-ctx.Done():
fmt.Println("f2 timeout")
break
case <-ch2:
fmt.Println("f2 done")
}
}()
time.Sleep(time.Second * 5)
}