golang面试-代码运行题1-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()
}
解析:编译错误
不能使⽤多级指针调⽤⽅法。
posted @ 2022-07-19 18:04  Jeff的技术栈  阅读(65)  评论(0编辑  收藏  举报
回顶部