golang面试-代码运行题1-56
目录
- 1.代码运行题--下面代码有什么问题?
- 2.代码运行题--下面代码输出是什么?string转结构体
- 3.代码运行题--下面代码输出是什么?unit
- 4.代码运行题--下面代码输出是什么?rune类型
- 5.代码运行题--下面代码输出是什么?字符串截取rune类型
- 6.代码运行题--下面代码运行效率比较?难
- 7.代码运行题--下面代码输出是什么?defer和局部全局变量
- 8.代码运行题--下面代码输出是什么?数组和切片传递
- 9.代码运行题--下面代码有什么问题?
- 10.代码运行题--下面代码有什么问题?捕获panic
- 11.代码运行题--下面代码有什么问题?
- 12.代码运行题--下面代码输出是什么?defer,panic
- 13.代码运行题--下面代码有什么问题?指针
- 14.代码运行题--下面代码输出是什么?goroutine
- 15.代码运行题--下面代码输出是什么?
- 16.代码运行题--下面代码会触发异常吗?select case
- 19.代码运行题--下面代码输出是什么?defer
- 20.代码运行题--下面代码输出是什么?切片append
- 21.代码运行题--下面代码有什么问题?Mutex,RWMutex
- 22.代码运行题--下面代码为什么不能编译过?interface
- 23.代码运行题--下面代码输出是什么?interface
- 24.代码运行题--下面代码有什么问题?string
- 25.代码运行题--下面代码输出是什么?
- 26.代码运行题--下面代码输出是什么?数组切片底层数组
- 27.代码运行题--下面代码输出是什么?结构体比较
- 28.代码运行题--下面代码输出是什么?数组切片比较
- 29.代码运行题--下面代码有什么问题?map
- 30.代码运行题--下面代码有什么问题?Mutex死锁
- 31.代码运行题--下面代码有什么问题?RWMutex死锁
- 32.代码运行题--下面代码有什么问题?Mutex
- 33.代码运行题--下面代码有什么问题?Waitgroup
- 34.代码运行题--下面代码有什么问题?channel
- 35.代码运行题--下面代码有什么问题?channel
- 36.代码运行题--下面代码有什么问题?map
- 37.代码运行题--下面代码有什么问题?goroutine
- 38.代码运行题--下面哪一行代码会panic?
- 39.代码运行题--下面代码输出结果?int8
- 40.代码运行题--下面代码输出结果?sync.Map,断言
- 41.代码运行题--下面代码输出结果?range
- 42.代码运行题--下面代码输出结果?
- 43.代码运行题--下面代码输出结果?nil
- 44.代码运行题--下面代码有什么问题?map
- 45.代码运行题--下面代码输出结果?切片初始化
- 46.代码运行题--下面代码输出结果?类型断⾔
- 47.代码运行题--下面代码有什么问题?
- 48.代码运行题--下面代码有什么问题?变量名重复声明
- 49.代码运行题--下面代码输出结果?const
- 50.代码运行题--下面代码输出结果?局部作用域
- 51.代码运行题--下面代码输出结果?结构体:=赋值
- 52.代码运行题--下面代码输出结果?chan
- 53.代码运行题--下面代码输出结果?string转结构体
- 54.代码运行题--下面代码输出结果?数组值传递,切片引用(指针)传递
- 55.代码运行题--下面代码有什么问题?
- 56.代码运行题--下面代码有什么问题?多级指针错误
1.代码运行题--下面代码有什么问题?
type Param map[string]interface{}
type Show struct {
Param
}
func main1() {
s := new(Show)
s.Param["RMB"] = 10000
}
解析:panic报错,也不能运行
共发现两个问题:
1. main 函数不能加数字。
2. new 关键字⽆法初始化 Show 结构体中的 Param 属性,所以直接对 s.Param 操作会出错。
修复代码:
type Param map[string]interface{}
type Show struct {
Param
}
func main() {
s := new(Show)
s.Param = Param{}
s.Param["RMB"] = 10000
}
2.代码运行题--下面代码输出是什么?string转结构体
type People struct {
name string `json:"name"`
}
func main() {
js := `{
"name":"11"
}`
var p People
err := json.Unmarshal([]byte(js), &p)
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Println("people: ", p) //people: {}
}
结果:
结果为空,按照 golang 的语法,⼩写开头的⽅法、属性或 struct 是私有的,同样,在json 解码或转码的时候也⽆法上线私有属性的转换。
题⽬中是⽆法正常得到People 的name 值的。⽽且,私有属性name 也不应该加json 的标签。
改为大写
type People struct {
Name string `json:"name"`
}
3.代码运行题--下面代码输出是什么?unit
func main() {
var a uint=1
var b uint=2
fmt.Println(a-b)
}
//结果是多少?为什么
答案:首先肯定不是-1
如果操作系统是64位就是2的64次方减1
如果操作系统是32位就是2的32次方剪1
原因:go是强类型语言,所以uint类型计算,结果肯定也是uint类型
uint类型1-2可以转换为0-1。然后计算机里面没有减法,是以加法做运算,相当于0加负1。负数都会转成补码,得出来结果:所有位数都是1的一个二进制,所以就是当前位数的最大值。2的64次方
rune类型
官方对它的解释是:rune是类型int32的别名。区别是rune做长度计算,特别是中文或者特殊字符
rune作用:
1.统计带有中文或者特殊字符的字符串长度
2.截取中文字符串长度
4.代码运行题--下面代码输出是什么?rune类型
func main() {
a:="jeff你好啊啊啊啊"
fmt.Println(len(a)) //字节长度,22
fmt.Println(len([]rune(a)))//字符串长度10
}
//输出结果:11,这是字节长度
英文字符占用 1 字节,中文字符占用 3 字节
5.代码运行题--下面代码输出是什么?字符串截取rune类型
func main() {
a := "jeff你好啊啊啊啊"
t1 := a[:10] //英文1个字节,中文3个字节。
fmt.Println(t1) //jeff你好
fmt.Println(a[:5]) //jeff�
t2 := string([]rune(a)[0:6])
fmt.Println(t2) //jeff你好
}
6.代码运行题--下面代码运行效率比较?难
func main() {
const (
max = 10000000
)
martixA := createMartix(max) //二维数组
martixB := createMartix(max) //二维数组
//方式一
for i := 0; i < max; i++ {
for j := 0; j < max; j++ {
martixA[i][j] = martixA[i][j] + martixB[i][j]
}
}
//方式二
for i := 0; i < max; i++ {
for j := 0; j < max; j++ {
martixA[i][j] = martixA[i][j] + martixB[j][i]
}
}
}
//两种方式那种效率高?为什么?
答案:
cpu会有3级缓存,cache加载是内存对齐,每次以固定的长度加载数据到cache中
第一种效率更高,因为第一种内存都是在连续的内存里面,可以使用到cache提速
第二种,每循环一次,变量都是不连续的,就会涉及到重新加载从内存里面数据读取到cache,当cache不充足时还会涉及到cache时效,修改等。会导致整个运行速率慢,没有充分利用到cache加速到效果
//cup的cache问题,多核cpu怎么保持cache不冲突和一致的?
把cache里面的状态设置成4个状态:两个bit表示4种状态
分别是:修改状态,独占状态,共享状态,失效状态
//有点复杂,cpu的cache理解不好
举例:第一个程序,把内存加载到cache中,并做修改。此时cache状态为修改且独占状态。当第二个程序,把这一个内存加载到cache时,状态变成共享状态。
7.代码运行题--下面代码输出是什么?defer和局部全局变量
func demo2() error {
var err error
defer func() {
r := recover()
if r != nil {
err = errors.New(fmt.Sprintf("%s", r))
}
}()
panic("demo2")
return err
}
//返回值为 nil,因为defer中的err不会赋值给外面的err
解决:
func demo2() (err error) {
defer func() {
r := recover()
if r != nil {
err = errors.New(fmt.Sprintf("%s", r))
}
}()
panic("demo2")
return err
}
//这样改err就不是nil了
8.代码运行题--下面代码输出是什么?数组和切片传递
map和slice都是指针传递(引用),即函数内部是可以改变参数的值的
数组是值传递,不管函数内部如何改变参数,都是改变的拷贝值
//数组
func main() {
a := [3]int{1, 2, 3}
for k, v := range a {
if k == 0 {
a[0], a[1] = 100, 200
fmt.Println(a) //[100,200,3]
}
a[k] = v + 100
}
fmt.Println(a) //[101,102,103]
}
//切片
func main() {
a := []int{1, 2, 3}
for k, v := range a {
if k == 0 {
a[0], a[1] = 100, 200
fmt.Println(a) //[100,200,3]
}
a[k] = v + 100
}
fmt.Println(a) //[101,300,103]
}
9.代码运行题--下面代码有什么问题?
func main() {
ch := make(chan int, 1000)
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
}()
go func() {
for {
a, ok := <-ch
if !ok {
fmt.Println("close")
return
}
fmt.Println("a: ", a)
}
}()
close(ch)
fmt.Println("ok")
time.Sleep(time.Second * 1)
}
close(ch)直接把chan关闭了,改为:defer close(ch)
10.代码运行题--下面代码有什么问题?捕获panic
type Project struct{}
func (p *Project) deferError() {
if err := recover(); err != nil {
fmt.Println("recover: ", err)
}
}
func (p *Project) exec(msgchan chan interface{}) {
//defer p.deferError() //应该在这里捕获异常panic
for msg := range msgchan {
m := msg.(int) // 报错,因为是string,改为:m,ok := msg.(int),ok=flase
fmt.Println("msg: ", m)
}
}
func (p *Project) run(msgchan chan interface{}) {
for {
defer p.deferError() //应该注释
go p.exec(msgchan)
time.Sleep(time.Second * 2)
}
}
func (p *Project) Main() {
a := make(chan interface{}, 100)
go p.run(a)
go func() {
for {
a <- "1"
time.Sleep(time.Second)
}
}()
time.Sleep(time.Second * 100000000000)
}
func main() {
p := new(Project)
p.Main()
}
解析:
有⼀下⼏个问题:
1. time.Sleep 的参数数值太⼤,超过了 1<<63 - 1 的限制。
2. defer p.deferError() 需要在协程开始出调⽤,否则⽆法捕获 panic 。
11.代码运行题--下面代码有什么问题?
type Student struct {
name string
}
func main() {
m := map[string]Student{"people": {"jeff"}}
m["people"].name = "chary"
fmt.Println(m["people"].name)
}
map的value本身是不可寻址的,因为map中的值会在内存中移动,并且旧的指针地址在map改变时会变得⽆效。
故如果需要修改map值,可以将map 中的⾮指针类型value ,修改为指针类型,⽐如使
⽤map[string]*Student
12.代码运行题--下面代码输出是什么?defer,panic
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
////结果///
打印后
打印中
打印前
触发异常
panic 错误并不能终⽌ defer 的执⾏。
13.代码运行题--下面代码有什么问题?指针
type student struct {
Name string
Age int
}
func pase_student() {
m := make(map[string]*student)
stus := []student{
{Name: "jeff", Age: 18},
{Name: "chary", Age: 17},
}
for _, stu := range stus {
m[stu.Name] = &stu
}
fmt.Println(m["jeff"]) //&{chary 17}
fmt.Println(m["chary"])//&{chary 17}
}
func main() {
pase_student()
}
解析:
golang 的 for ... range 语法中, stu 变量会被复⽤,每次循环会将集合中的值复制给这个变量,因此,会导致最后m 中的map 中储存的都是stus 最后⼀个student 的值。
修改为:
m := make(map[string]student)
m[stu.Name] = stu
14.代码运行题--下面代码输出是什么?goroutine
func main() {
runtime.GOMAXPROCS(1)
wg := sync.WaitGroup{}
wg.Add(20)
for j := 0; j < 10; j++ {
go func(j int) {
fmt.Println("i: ", j)
wg.Done()
}(j)
}
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println("k: ", i)
wg.Done()
}(i)
}
wg.Wait()
}
//输出//
k: 9
i: 0
i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
k: 0
k: 1
k: 2
k: 3
k: 4
k: 5
k: 6
k: 7
k: 8
这个输出结果决定来⾃于调度器优先调度哪个G。从runtime的源码可以看到,当创建⼀个G时,会优先放⼊到下⼀个调度的runnext 字段上作为下⼀次优先调度的G。因此,最先输出的是最后创建的G,也就是9
15.代码运行题--下面代码输出是什么?
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
解析:
输出结果为showA 、showB 。golang 语⾔中没有继承概念,只有组合,也没有虚⽅法,更没有重载。因此, *Teacher 的 ShowB 不会覆写被组合的 People 的⽅法。
16.代码运行题--下面代码会触发异常吗?select case
func main() {
runtime.GOMAXPROCS(1)
int_chan := make(chan int, 1)
string_chan := make(chan string, 1)
int_chan <- 1
string_chan <- "hello"
select {
case value := <-int_chan:
fmt.Println(value)
case value := <-string_chan:
panic(value)
}
}
解析:
结果是随机执⾏。golang 在多个case 可读的时候会公平的选中⼀个执⾏。
选中到case value := <-string_chan则panic
19.代码运行题--下面代码输出是什么?defer
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret) //20,1,2,3 //2,1,3,4
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
}
//输出//
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
defer 在定义的时候会计算好调⽤函数的参数,所以会优先输出10 、20 两个参数。然后根据定义的顺序倒序执⾏。
20.代码运行题--下面代码输出是什么?切片append
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
}
//输出//
[0 0 0 0 0 1 2 3]
解析:
make 在初始化切⽚时指定了⻓度,所以追加数据时会从len(s) 位置开始填充数据。
21.代码运行题--下面代码有什么问题?Mutex,RWMutex
type UserAges struct {
ages map[string]int
sync.RWMutex
}
func (ua *UserAges) Add(name string, age int) {
ua.Lock()
defer ua.Unlock()
ua.ages[name] = age
}
func (ua *UserAges) Get(name string) int {
if age, ok := ua.ages[name]; ok {
return age
}
return -1
}
func main() {
u := UserAges{}
u.ages = make(map[string]int)
u.Add("jeff", 18)
c := u.Get("jeff")
fmt.Println(c)
}
解析:
在执⾏ Get⽅法时可能被thorw。
虽然有使⽤sync.Mutex做写锁,但是map是并发读写不安全的。map属于引⽤类型,并发读写时多个协程⻅是通过指针访问同⼀个地址,即访问共享变量,此时同时读写资源存在竞争关系。会报错误信息:“fatal error:concurrent map read and map write”。
因此,在 Get 中也需要加锁,因为这⾥只是读,建议使⽤读写锁 sync.RWMutex 。
type UserAges struct {
ages map[string]int
sync.RWMutex
}
func (ua *UserAges) Add(name string, age int) {
ua.Lock() //写锁
defer ua.Unlock()
ua.ages[name] = age
}
func (ua *UserAges) Get(name string) int {
ua.RLock() //读锁
defer ua.RUnlock()
if age, ok := ua.ages[name]; ok {
return age
}
return -1
}
func main() {
u := UserAges{}
u.ages = make(map[string]int)
u.Add("jeff", 18)
c := u.Get("jeff")
fmt.Println(c)
}
22.代码运行题--下面代码为什么不能编译过?interface
type People interface {
QueryInfo(id int) string
}
type object struct {
db *gorm.DB
}
func (o *object) QueryInfo(id int) (talk string) {
if id == 0 {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = object{}
id := 0
fmt.Println(peo.QueryInfo(id))
}
解析:
编译失败,值类型 object{} 未实现接⼝People 的⽅法,不能定义为 People 类型
改为:var peo People = &object{}
23.代码运行题--下面代码输出是什么?interface
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func live() People {
var stu *Student
fmt.Println(stu)
return stu
}
func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
//输出//
BBBBBBB
解析:
跟上⼀题⼀样,不同的是*Student 的定义后本身没有初始化值,所以 *Student 是 nil 的,但是*Student 实现了 People 接⼝,接⼝不为 nil 。
24.代码运行题--下面代码有什么问题?string
func main() {
var x string = nil
if x == nil {
x = "default"
}
fmt.Println(x)
}
解析:
golang 中字符串是不能赋值 nil 的,也不能跟 nil ⽐较。
25.代码运行题--下面代码输出是什么?
const (
a = iota // 0
b //1
c //2
)
const (
name = "jeff" // 0
d = iota //1
e = iota //2
)
func main() {
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
fmt.Println(e)
}
//输出
0
1
2
1
2
26.代码运行题--下面代码输出是什么?数组切片底层数组
func main() {
str1 := []string{"a", "b", "c"}
str2 := str1[1:]
str2[1] = "new"
fmt.Println(str1)
fmt.Println(str2)
str2 = append(str2, "z", "x", "y")
fmt.Println(str2)
}
//输出//
[a b new]
[b new]
[b new z x y]
解析:
golang 中的切⽚底层其实使⽤的是数组。当使⽤str1[1:] 使, str2 和 str1 底层共享⼀个数组,这回导致str2[1] = "new" 语句影响 str1 。⽽ append 会导致底层数组扩容,⽣成新的数组,因此追加数据后的 str2 不会影响 str1 。
但是为什么对 str2 复制后影响的却是 str1 的第三个元素呢?这是因为切⽚ str2 是从数组的第⼆个元素开始, str2 索引为 1 的元素对应的是 str1 索引为 2 的元素。
27.代码运行题--下面代码输出是什么?结构体比较
type Student struct {
Name string
}
func main() {
fmt.Println(&Student{Name: "menglu"} == &Student{Name: "menglu"})
fmt.Println(Student{Name: "menglu"} == Student{Name: "menglu"})
}
//输出
false
true
解析:
指针类型⽐较的是指针地址,⾮指针类型⽐较的是每个属性的值
28.代码运行题--下面代码输出是什么?数组切片比较
func main() {
fmt.Println([...]string{"1"} == [...]string{"1"})
fmt.Println([]string{"1"} == []string{"1"}) //不能比较
}
解析:
数组只能与相同纬度⻓度以及类型的其他数组⽐较,切⽚之间不能直接⽐较
29.代码运行题--下面代码有什么问题?map
type Student struct {
Age int
}
func main() {
kv := map[string]Student{"menglu": {Age: 21}}
kv["menglu"].Age = 18 //报错,改为kv["menglu"] = Student{Age: 18}
s := []Student{{Age: 21}}
s[0].Age = 22
fmt.Println(kv, s)
kv2:=make(map[string]Student)
kv2["jeff"] = Student{Age: 18}
kv2["jeff"] = Student{Age: 20}
kv2["jeff"].Age = 22 //报错,改为:Student{Age: 22}
}
解析:v,ok:=kv["menglu"]
golang中的map 通过key 获取到的实际上是两个值,第⼀个是获取到的值,第⼆个是是否存在该key 。因此不能直接通过key 来赋值对象。
30.代码运行题--下面代码有什么问题?Mutex死锁
var mu sync.Mutex
var chain string
func main() {
chain = "main"
A()
fmt.Println(chain)
}
func A() {
mu.Lock()
defer mu.Unlock()
chain = chain + " --> A" //main --> A
B()
}
func B() {
mu.Lock()
defer mu.Unlock()
chain = chain + " --> B" //main --> A --> B
C()
}
func C() {
mu.Lock()
defer mu.Unlock()
chain = chain + " --> C" // main --> A --> B --> C
}
解析:会死锁,因为Mutex是互斥锁,这里全部defer mu.Unlock()
改为:
var mu sync.Mutex
var chain string
func main() {
chain = "main"
A()
fmt.Println(chain)
}
func A() {
mu.Lock()
chain = chain + " --> A" //main --> A
mu.Unlock()
B()
}
func B() {
mu.Lock()
chain = chain + " --> B" //main --> A --> B
mu.Unlock()
C()
}
func C() {
mu.Lock()
chain = chain + " --> C" // main --> A --> B --> C
mu.Unlock()
}
31.代码运行题--下面代码有什么问题?RWMutex死锁
var mu sync.RWMutex
var count int
func main() {
go A()
time.Sleep(2 * time.Second)
mu.Lock()
defer mu.Unlock()
count++
fmt.Println(count)
}
func A() {
mu.RLock()
time.Sleep(3 * time.Second)
defer mu.RUnlock()
mu.RLock()
defer mu.RUnlock()
}
解析:会产⽣死锁panic ,根据sync/rwmutex.go 中注释可以知道,读写锁当有⼀个协程在等待写锁时,其他协程是不能获得读锁的,⽽在A 和C 中同⼀个调⽤链中间需要让出读锁,让写锁优先获取,⽽A 的读锁⼜要求C 调⽤完成,因此死锁。
32.代码运行题--下面代码有什么问题?Mutex
type MyMutex struct {
count int
sync.Mutex
}
func main() {
var mu MyMutex
mu.Lock()
var mu2 = mu
mu.count++
mu.Unlock()
mu2.Lock() //会报错,因为把mu的锁状态也复制过来了
mu2.count++
mu2.Unlock()
fmt.Println(mu.count, mu2.count)
}
解析:报错,panic
加锁后复制变量,会将锁的状态也复制,所以mu2 其实是已经加锁状态,再加锁会死锁。
33.代码运行题--下面代码有什么问题?Waitgroup
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
time.Sleep(time.Millisecond)
wg.Done()
wg.Add(1)
}()
wg.Wait()
}
解析:
WaitGroup 在调⽤ Wait 之后是不能再调⽤ Add ⽅法的。
34.代码运行题--下面代码有什么问题?channel
func main() {
var ch chan int
go func() {
ch = make(chan int, 1)
ch <- 1
}()
go func(ch chan int) {
time.Sleep(time.Second)
<-ch
}(ch)
c := time.Tick(1 * time.Second)
for range c {
fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
}
}
解析:⼀段时间后总是输出 #goroutines: 2
因为 ch 未初始化,写和读都会阻塞,之后被第⼀个协程重新赋值,导致写的ch 都阻塞
35.代码运行题--下面代码有什么问题?channel
func main() {
var ch chan int
var count int
go func() {
ch <- 1
}()
go func() {
count++
close(ch)
}()
<-ch
fmt.Println(count)
}
解析:
ch 未有被初始化,关闭时会报错
36.代码运行题--下面代码有什么问题?map
func main() {
var m sync.Map
m.LoadOrStore("a", 1)
m.Delete("a")
fmt.Println(m.Len())
}
解析:报错
sync.Map 没有Len⽅法。
var ma sync.Map //该类型是开箱即用,只需要声明既可
ma.Store("key", "value") // 存储值
ma.Delete("key") //删除值
ma.LoadOrStore("key", "value")// 获取值,如果没有则存储
fmt.Println(ma.Load("key"))//获取值
37.代码运行题--下面代码有什么问题?goroutine
var c = make(chan int)
var a int
func f() {
a = 1
<-c
}
func main() {
go f()
c <- 0
fmt.Println(a)
}
//输出:
1
解析:输出1,
c <- 0 会阻塞依赖于 f() 的执⾏,f()执行完了才会给a赋值。
如果代码为:
var c = make(chan int)
var a int
func f() {
a = 1
}
func main() {
go f()
fmt.Println(a)
}
//输出:
0
因为f()还没来得及执行,fmt.Println(a)就已经执行完了
38.代码运行题--下面哪一行代码会panic?
func main() {
nil := 123
fmt.Println(nil)
var _ map[string]int = nil
}
解析:
var _ map[string]int = nil会panic。当前作⽤域中,预定义的 nil 被覆盖,此时 nil 是 int 类型值,不能赋值给 map 类型
第二点:初始化map要使用make初始化
39.代码运行题--下面代码输出结果?int8
func main() {
var x int8 = -128
var y = x / -1
fmt.Println(y) //-128
}
解析:-128,int8范围-128~127
溢出
fmt.Println(y) //128越界溢出
//[0,1,1,1,1,1,1,1] = 127
//[1, 0,0,0,0,0,0,0] = -128
//[1,0,0,0,0,0,0,1] =-127
40.代码运行题--下面代码输出结果?sync.Map,断言
func main() {
var m sync.Map
m.Store("address", map[string]string{"province": "江苏", "city": "南京"})
v, _ := m.Load("address")
fmt.Println(v["province"]) //报错,m.Load返回的是interface
fmt.Println(v.(map[string]string)["province"])
}
解析:
因为m.Load返回的是interface类型,所以 v 类型是 interface {} ,这⾥需要⼀个类型断⾔
fmt.Println(v.(map[string]string)["province"])
41.代码运行题--下面代码输出结果?range
func main() {
x := []string{"a", "b", "c"}
for v := range x {
fmt.Println(v)
}
}
//输出
0
1
2
解析:
func main() {
x := []string{"a", "b", "c"}
for k,v := range x {
fmt.Println(v)
}
}
//暑促好
a
b
c
42.代码运行题--下面代码输出结果?
type User struct{}
type User1 User
type User2 = User
func (i User1) m1() {
fmt.Println("m1")
}
func (i User) m2() {
fmt.Println("m2")
}
func main() {
var i1 User1
var i2 User2
i1.m1()
i2.m2()
}
//输出
m1
m2
43.代码运行题--下面代码输出结果?nil
func Foo(x interface{}) {
if x == nil {
fmt.Println("nil")
return
}
fmt.Println("no nil")
}
func demo() (u error) {
return
}
func main() {
var x *int = nil
y := demo()
Foo(x) //no nil
Foo(y) //nil
}
解析:
考点:interface 的内部结构,我们知道接⼝除了有静态类型,还有动态类型和动态值,当且仅当动态值和动态类型都为 nil 时,接⼝类型值才为 nil。这⾥的 x 的动态类型是 *int ,所以 x 不为nil。
44.代码运行题--下面代码有什么问题?map
type Param map[string]interface{}
type Show struct {
*Param
}
func main() {
s := new(Show)
s.Param["day"] = 2
}
解析:
1. map 需要初始化才能使⽤;
2. 指针不⽀持索引。修复代码如下
type Param map[string]interface{}
type Show struct {
*Param
}
func main() {
s := new(Show)
p := make(Param)
p["day"] = 2
s.Param = &p
tmp := *s.Param
fmt.Println(tmp["day"])
}
45.代码运行题--下面代码输出结果?切片初始化
var x = []int{2: 2, 3, 0: 1, 6: 999}
func main() {
fmt.Println(x) //[1 0 2 3 0 0 999]
}
初始化切⽚时候,可以指定索引,没有指定索引的元素用0占位
46.代码运行题--下面代码输出结果?类型断⾔
func main() {
x := interface{}(nil)
y := (*int)(nil)
a := y == x
b := y == nil
_, c := x.(interface{})
fmt.Println(a, b, c)
}
//输出
false true false
解析:
类型断⾔语法:i.(Type),其中 i 是接⼝,Type 是类型或接⼝。编译时会⾃动检测 i 的动态类型与 Type 是否⼀致。但是,如果动态类型不存在,则断⾔总是失败。
47.代码运行题--下面代码有什么问题?
type info struct {
result int
}
func work() (int, error) {
return 13, nil
}
func main() {
var data info
data.result, err := work()
fmt.Printf("info: %+v\n", data)
}
解析:
编译失败,不能使⽤短变量声明设置结构体字段值,修复代码:
func main() {
var data info
var err error
data.result, err = work()
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("info: %+v\n", data)
}
48.代码运行题--下面代码有什么问题?变量名重复声明
func main() {
one:=1
one:=2
}
解析:变量名重复声明
不能在单独的声明中重复声明⼀个变量,但在多变量声明的时候是可以的,但必须保证⾄少有⼀个变量是新声明的。
func main() {
one := 1
one, two := 2, 3
fmt.Println(one, two) //2,3
}
49.代码运行题--下面代码输出结果?const
const (
x uint16 = 120
y
s = "abc"
z
)
func main() {
fmt.Println(x)
fmt.Println(y)
fmt.Println(s)
fmt.Println(z)
}
//输出
120
120
abc
abc
50.代码运行题--下面代码输出结果?局部作用域
func main() {
x := 1
fmt.Println(x)
{
fmt.Println(x)
i, x := 2, 2
fmt.Println(i, x)
}
fmt.Println(x) // print ?
}
//输出
1
1
2 2
1
解析:
知识点:变量隐藏。
使⽤变量简短声明符号 := 时,如果符号左边有多个变量,只需要保证⾄少有⼀个变量是新声明的,并对已定义的变量尽进⾏赋值操作。但如果出现作⽤域之后,就会导致变量隐藏的问题,就像这个例⼦⼀样。
这个坑很容易挖,但⼜很难发现。即使对于经验丰富的 Go 开发者⽽⾔,这也是⼀个⾮常常⻅的陷阱。
51.代码运行题--下面代码输出结果?结构体:=赋值
type foo struct {
bar int
}
func main() {
var f foo
f.bar, tmp := 1, 2
}
解析:
编译错误
:= 操作符不能⽤于结构体字段赋值。
52.代码运行题--下面代码输出结果?chan
func main() {
var ch chan int
select {
case v, ok := <-ch:
println(v, ok)
default:
println("default")
}
}
解析:default
ch 为 nil,读写都会阻塞。
53.代码运行题--下面代码输出结果?string转结构体
type People struct {
name string `json:"name"`
}
func main() {
js := `{
"name":"seekload"
}`
var p People
err := json.Unmarshal([]byte(js), &p)
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Println(p)
}
解析:输出{}
知识点:结构体访问控制,因为 name ⾸字⺟是⼩写,导致其他包不能访问,所以输出为空结构体。
54.代码运行题--下面代码输出结果?数组值传递,切片引用(指针)传递
type T struct {
ls []int
}
func foo(t T) {
t.ls[0] = 100
}
func main() {
var t = T{
ls: []int{1, 2, 3},
}
foo(t)
fmt.Println(t.ls[0]) //100
}
解析:输出100
调⽤ foo() 函数时虽然是值传值,但 foo() 函数中,字段 ls 依旧可以看成是指向底层数组的指针。没有打破原来的数组
代码改为:
type T struct {
ls []int
}
func foo(t T) {
t.ls = append(t.ls, 4, 5, 6)
t.ls[0] = 100
}
func main() {
var t = T{
ls: []int{1, 2, 3},
}
foo(t)
fmt.Println(t.ls[0]) //1
}
//这样就输出1了,因为已经append打破原来的底层数组了
55.代码运行题--下面代码有什么问题?
type T struct {
n int
}
func (t *T) Set(n int) {
t.n = n
}
func getT() T {
return T{}
}
func main() {
getT().Set(1)
}
解析:
1.getT().Set(1),不可寻址的结构体不能调⽤带结构体指针接收者的⽅法;
2.直接返回的 T{} 不可寻址;
修复代码:
type T struct {
n int
}
func (t *T) Set(n int) {
t.n = n
}
func getT() T {
return T{}
}
func main() {
t := getT()
t.Set(2)
fmt.Println(t.n)
}
56.代码运行题--下面代码有什么问题?多级指针错误
type N int
func (n N) value() {
n++
fmt.Printf("v:%p,%v\n", &n, n)
}
func (n *N) pointer() {
*n++
fmt.Printf("v:%p,%v\n", n, *n)
}
func main() {
var a N = 25
p := &a
p1 := &p
p1.value()
p1.pointer()
}
解析:编译错误
不能使⽤多级指针调⽤⽅法。
选择了IT,必定终身学习