整理自golang中文網(wǎng)https://studygolang.com/ 以及公眾號 golang來啦 (侵刪)
不管面試里用不用得到,萬丈高樓平地起,先得從基礎(chǔ)做起
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("觸發(fā)異常")
}
答案:
打印后
打印中
打印前
觸發(fā)異常
參考解析:defer 的執(zhí)行順序是后進先出。當出現(xiàn) panic 語句的時候,會先按照 defer 的后進先出的順序執(zhí)行,最后才會執(zhí)行panic。
func main() {
slice := []int{0,1,2,3}
m := make(map[int]*int)
for key,val := range slice {
m[key] = &val
}
for k,v := range m {
fmt.Println(k,"->",*v)
}
}
答案:
0 -> 3
0 -> 3
0 -> 3
0 -> 3
參考解析:這是新手常會犯的錯誤寫法,for range 循環(huán)的時候會創(chuàng)建每個元素的副本,而不是元素的引用,所以 m[key] = &val 取的都是變量 val 的地址,所以最后 map 中的所有元素的值都是變量 val 的地址,因為最后 val 被賦值為3,所有輸出都是3.
3.下面兩段代碼輸出什么
// 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]
參考解析:這道題考的是使用 append 向 slice 添加元素,第一段代碼常見的錯誤是 [1 2 3],需要注意
make 分配slice內(nèi)存元素默認分配0值, slice內(nèi)存不足底層數(shù)組會重新分配
4.下面這段代碼有什么缺陷
func funcMui(x,y int)(sum int,error){
return x+y,nil
}
答案: 返回第二個參數(shù)未命名
參考解析:
在函數(shù)有多個返回值時,只要有一個返回值有命名,其他的也必須命名。如果有多個返回值必須加上括號();如果只有一個返回值且命名也必須加上括號()。這里的第一個返回值有命名 sum,第二個沒有命名,所以錯誤。
5.new() 與 make() 的區(qū)別
參考答案:
new(T) 和 make(T,args) 是 Go 語言內(nèi)建函數(shù),用來分配內(nèi)存,但適用的類型不同。
new(T) 會為 T 類型的新值分配已置零的內(nèi)存空間,并返回地址(指針),即類型為 *T 的值。換句話說就是,返回一個指針,該指針指向新分配的、類型為 T 的零值。適用于值類型,如數(shù)組、結(jié)構(gòu)體等。
make(T,args) 返回初始化之后的 T 類型的值,這個值并不是 T 類型的零值,也不是指針 *T,是經(jīng)過初始化之后的 T 的引用。make() 只適用于 slice、map 和 channel.
6.下面這段代碼能否通過編譯,不能的話原因是什么;如果能,輸出什么
func main() {
list := new([]int)
list = append(list, 1)
fmt.Println(list)
}
參考答案及解析:不能通過編譯,new([]int) 之后的 list 是一個 *[]int 類型的指針,不能對指針執(zhí)行 append 操作??梢允褂?make() 初始化之后再用。同樣的,map 和 channel 建議使用 make() 或字面量的方式初始化,不要用 new() 。
7.下面這段代碼能否通過編譯,如果可以,輸出什么?
func main() {
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2)
fmt.Println(s1)
}
參考答案及解析:不能通過編譯。append() 的第二個參數(shù)不能直接使用 slice,需使用 … 操作符,將一個切片追加到另一個切片上:append(s1,s2…)?;蛘咧苯痈显兀稳纾篴ppend(s1,1,2,3)。
8.下面這段代碼能否通過編譯,如果可以,輸出什么?
var(
size := 1024
max_size = size*2
)
func main() {
fmt.Println(size,max_size)
}
參考答案及解析:不能通過編譯。這道題的主要知識點是變量聲明的簡短模式,形如:x := 100。但這種聲明方式有限制:
必須使用顯示初始化;
不能提供數(shù)據(jù)類型,編譯器會自動推導(dǎo);
只能在函數(shù)內(nèi)部使用簡短模式;
9.下面這段代碼能否通過編譯?不能的話,原因是什么?如果通過,輸出什么?
func main() {
sn1 := struct {
age int
name string
}{age: 11, name: "qq"}
sn2 := struct {
age int
name string
}{age: 11, name: "qq"}
if sn1 == sn2 {
fmt.Println("sn1 == sn2")
}
sm1 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
sm2 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
if sm1 == sm2 {
fmt.Println("sm1 == sm2")
}
}
參考答案及解析:編譯不通過 invalid operation: sm1 == sm2
這道題目考的是結(jié)構(gòu)體的比較,有幾個需要注意的地方:
1>. 結(jié)構(gòu)體只能比較是否相等,但是不能比較大小。
2>. 相同類型的結(jié)構(gòu)體才能夠進行比較,結(jié)構(gòu)體是否相同不但與屬性類型有關(guān),還與屬性順序相關(guān),sn3 與 sn1 就是不同的結(jié)構(gòu)體;
sn3:= struct {
name string
age int4 }{age:11,name:"qq"}
3>. 如果 struct 的所有成員都可以比較,則該 struct 就可以通過 == 或 != 進行比較是否相等,比較時逐個項進行比較,如果每一項都相等,則兩個結(jié)構(gòu)體才相等,否則不相等;
那什么是可比較的呢,常見的有 bool、數(shù)值型、字符、指針、數(shù)組等,像切片、map、函數(shù)等是不能比較的。 具體可以參考 Go 說明文檔。http://docs.studygolang.com/ref/spec#Comparison_operators
10.通過指針變量 p 訪問其成員變量 name,有哪幾種方式?
A.p.name
B.(&p).name
C.(*p).name
D.p->name
參考答案及解析:A C
11.下面這段代碼能否通過編譯?如果通過,輸出什么?
package main
import "fmt"
type MyInt1 int
type MyInt2 = int
func main() {
var i int =0
var i1 MyInt1 = i
var i2 MyInt2 = i
fmt.Println(i1,i2)
}
參考答案及解析:編譯不通過,cannot use i (type int) as type MyInt1 in assignment。
這道題考的是類型別名與類型定義的區(qū)別。
第 5 行代碼是基于類型 int 創(chuàng)建了新類型 MyInt1,第 6 行代碼是創(chuàng)建了 int 的類型別名 MyInt2,注意類型別名的定義時 = 。所以,第 10 行代碼相當于是將 int 類型的變量賦值給 MyInt1 類型的變量,Go 是強類型語言,編譯當然不通過;而 MyInt2 只是 int 的別名,本質(zhì)上還是 int,可以賦值。
第 10 行代碼的賦值可以使用強制類型轉(zhuǎn)化 var i1 MyInt1 = MyInt1(i).
12.關(guān)于字符串連接,下面語法正確的是?
A. str := 'abc' + '123'
B. str := "abc" + "123"
C. str := '123' + "abc"
D. fmt.Sprintf("abc%d", 123)
參考答案:B D
知識點:字符串連接。除了以上兩種連接方式,還有 strings.Join()、buffer.WriteString()等
13.下面這段代碼能否編譯通過?如果可以,輸出什么?
package main
import "fmt"
const (
x = iota
_
y
z = "zz"
k
p = iota
)
func main() {
fmt.Println(x,y,z,k,p)
}
參考答案及解析:編譯通過,輸出:0 2 zz zz 5。知識點:iota 的使用。給大家貼篇文章,講的很詳細
https://www.cnblogs.com/zsy/p/5370052.html
14.下面賦值正確的是()
A. var x = nil
B. var x interface{} = nil
C. var x string = nil
D. var x error = nil
B D
參考答案及解析:BD。知識點:nil 值。nil 只能賦值給指針、chan、func、interface、map 或 slice 類型的變量。強調(diào)下 D 選項的 error 類型,它是一種內(nèi)置接口類型,看下方貼出的源碼就知道,所以 D 是對的。
type error interface {
Error() string
}
15.關(guān)于init函數(shù),下面說法正確的是()
A. 一個包中,可以包含多個 init 函數(shù);
B. 程序編譯時,先執(zhí)行依賴包的 init 函數(shù),再執(zhí)行 main 包內(nèi)的 init 函數(shù);
C. main 包中,不能有 init 函數(shù);
D. init 函數(shù)可以被其他函數(shù)調(diào)用;
參考答案及解析:AB
關(guān)于 init() 函數(shù)有幾個需要注意的地方:
1> init() 函數(shù)是用于程序執(zhí)行前做包的初始化的函數(shù),比如初始化包里的變量等;
2>一個包可以出線多個 init() 函數(shù),一個源文件也可以包含多個 init() 函數(shù);
3> 同一個包中多個 init() 函數(shù)的執(zhí)行順序沒有明確定義,但是不同包的init函數(shù)是根據(jù)包導(dǎo)入的依賴關(guān)系決定的(看下圖);

init() 函數(shù)在代碼中不能被顯示調(diào)用、不能被引用(賦值給函數(shù)變量),否則出現(xiàn)編譯錯誤;
一個包被引用多次,如 A import B,C import B,A import C,B 被引用多次,但 B 包只會初始化一次;
引入包,不可出現(xiàn)死循壞。即 A import B,B import A,這種情況編譯失?。?/p>
16.下面這段代碼輸出什么以及原因?
func hello() []string {
return nil
}
func main() {
h := hello
if h == nil {
fmt.Println("nil")
} else {
fmt.Println("not nil")
}
}
A. nil
B. not nil
C. compilation error
答案及解析:B。這道題目里面,是將 hello() 賦值給變量 h,而不是函數(shù)的返回值,所以輸出 not nil。
17.下面這段代碼能否編譯通過?如果可以,輸出什么?
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")
}
}
參考答案及解析:編譯失敗??键c:類型選擇,類型選擇的語法形如:i.(type),其中 i 是接口,type 是固定關(guān)鍵字,需要注意的是,只有接口類型才可以使用類型選擇。看下關(guān)于接口的文章。
18.關(guān)于channel,下面語法正確的是()
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-
參考答案及解析:ABC。A、B都是聲明 channel;C 讀取 channel;寫 channel 是必須帶上值,所以 D 錯誤。
19.下面這段代碼輸出什么?
A.0
B.1
C.Compilation error
type person struct {
name string
}
func main() {
var m map[person]int
p := person{"mike"}
fmt.Println(m[p])
}
參考答案及解析:A。打印一個 map 中不存在的值時,返回元素類型的零值。這個例子中,m 的類型是 map[person]int,因為 m 中不存在 p,所以打印 int 類型的零值,即 0。
20.下面這段代碼輸出什么?
A.18
B.5
C.Compilation error
func hello(num ...int) {
num[0] = 18
}
func main() {
i := []int{5, 6, 7}
hello(i...)
fmt.Println(i[0])
}
參考答案及解析:A 。...語法糖把切片傳入只要不發(fā)生擴容就會影響到原始切片
21.下面這段代碼輸出什么?
func main() {
a := 5
b := 8.1
fmt.Println(a + b)
}
A.13.1
B.13
C.compilation error
參考答案及解析:C。a 的類型是 int,b 的類型是 float,兩個不同類型的數(shù)值不能相加,編譯報錯。
22.下面這段代碼輸出什么?
package main
import (
"fmt"
)
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]。基于數(shù)組(切片)可以使用操作符 [i,j] 創(chuàng)建新的切片,從索引 i,到索引 j 結(jié)束,截取已有數(shù)組(切片)的任意部分,返回新的切片,新切片的值包含原數(shù)組(切片)的 i 索引的值,但是不包含 j 索引的值。i、j 都是可選的,i 如果省略,默認是 0,j 如果省略,默認是原數(shù)組(切片)的長度。i、j 都不能超過這個長度值。
假如底層數(shù)組的大小為 k,截取之后獲得的切片的長度和容量的計算方法:長度:j-i,容量:k-i。
截取操作符還可以有第三個參數(shù),形如 [i,j,k],第三個參數(shù) k 用來限制新切片的容量,但不能超過原數(shù)組(切片)的底層數(shù)組大小。截取獲得的切片的長度和容量分別是:j-i、k-i。
所以例子中,切片 t 為 [4],長度和容量都是 1。
詳細的知識點可以看下關(guān)于切片的文章。
23.下面這段代碼輸出什么?
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 中的數(shù)組是值類型,可比較,另外一方面,數(shù)組的長度也是數(shù)組類型的組成部分,所以 a 和 b 是不同的類型,是不能比較的,所以編譯錯誤。
24.關(guān)于 cap() 函數(shù)的適用類型,下面說法正確的是()
A. array
B. slice
C. map
D. channel
A B D
知識點:cap(),cap() 函數(shù)不適用 map。
25.下面這段代碼輸出什么?
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。當且僅當接口的動態(tài)值和動態(tài)類型都為 nil 時,接口類型值才為 nil。
關(guān)于接口詳細的介紹可以看下之前的文章。
26.下面這段代碼輸出什么?
func main() {
s := make(map[string]int)
delete(s, "h")
fmt.Println(s["h"])
}
A. runtime panic
B. 0
C. compilation error
參考答案及解析:B。刪除 map 不存在的鍵值對時,不會報錯,相當于沒有任何作用;獲取不存在的減值對時,返回值類型對應(yīng)的零值,所以返回 0。
27.下面屬于關(guān)鍵字的是()
A.func
B.struct
C.class
D.defer
參考答案及解析:ABD。知識點:Go 語言的關(guān)鍵字。Go 語言有 25 個關(guān)鍵字,看下圖:

28.下面這段代碼輸出什么?
func main() {
i := -5
j := +5
fmt.Printf("%+d %+d", i, j)
}
A. -5 +5
B. +5 +5
C. 0 0
參考答案及解析:A。%d表示輸出十進制數(shù)字,+表示輸出數(shù)值的符號。這里不表示取反。
29.下面這段代碼輸出什么?
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。知識點:結(jié)構(gòu)體嵌套。在嵌套結(jié)構(gòu)體中,People 稱為內(nèi)部類型,Teacher 稱為外部類型;通過嵌套,內(nèi)部類型的屬性、方法,可以為外部類型所有,就好像是外部類型自己的一樣。此外,外部類型還可以定義自己的屬性和方法,甚至可以定義與內(nèi)部相同的方法,這樣內(nèi)部類型的方法就會被“屏蔽”。這個例子中的 ShowB() 就是同名方法。
30.定義一個包內(nèi)全局字符串變量,下面語法正確的是()
A. var str string
B. str := ""
C. str = ""
D. var str = ""
參考答案及解析:AD。B 只支持局部變量聲明;C 是賦值,str 必須在這之前已經(jīng)聲明;
31.下面這段代碼輸出什么?
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
知識點:結(jié)構(gòu)體嵌套。這道題可以結(jié)合第 12 天的第三題一起看,Teacher 沒有自己 ShowA(),所以調(diào)用內(nèi)部類型 People 的同名方法,需要注意的是第 5 行代碼調(diào)用的是 People 自己的 ShowB 方法。
32.下面這段代碼輸出什么?
func hello(i int) {
fmt.Println(i)
}
func main() {
i := 5
defer hello(i)
i = i + 10
}
參考答案及解析:5。這個例子中,hello() 函數(shù)的參數(shù)在執(zhí)行 defer 語句的時候會保存一份副本,在實際調(diào)用 hello() 函數(shù)時用,所以是 5.
func main() {
str := "hello"
str[0] = 'x'
fmt.Println(str)
}
A. hello
B. xello
C. compilation error
C
34.下面代碼輸出什么?
func incr(p *int) int {
*p++
return *p
}
func main() {
p :=1
incr(&p)
fmt.Println(p)
}
A. 1
B. 2
C. 3
B
35.對 add() 函數(shù)調(diào)用正確的是()
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}…)
A B D
36.下面代碼下劃線處可以填入哪個選項?
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 都可以
C
37.下面這段代碼輸出什么?
func main() {
i := 65
fmt.Println(string(i))
}
A. A
B. 65
C. compilation error
參考答案及解析:A。UTF-8 編碼中,十進制數(shù)字 65 對應(yīng)的符號是 A。
38.下面這段代碼輸出什么?
type A interface {
ShowA() int
}
type B interface {
ShowB() int
}
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
c := Work{3}
var a A = c
var b B = c
fmt.Println(a.ShowA())
fmt.Println(b.ShowB())
}
13 23
參考答案及解析:13 23。知識點:接口。一種類型實現(xiàn)多個接口,結(jié)構(gòu)體 Work 分別實現(xiàn)了接口 A、B,所以接口變量 a、b 調(diào)用各自的方法 ShowA() 和 ShowB(),輸出 13、23。
39.切片 a、b、c 的長度和容量分別是多少?
func main() {
s := [3]int{1, 2, 3}
a := s[:0]
b := s[:2]
c := s[1:2:cap(s)]
}
參考答案及解析:a、b、c 的長度和容量分別是 0 3、2 3、1 2。知識點:數(shù)組或切片的截取操作。截取操作有帶 2 個或者 3 個參數(shù),形如:[i:j] 和 [i:j:k],假設(shè)截取對象的底層數(shù)組長度為 l。在操作符 [i:j] 中,如果 i 省略,默認 0,如果 j 省略,默認底層數(shù)組的長度,截取得到的切片長度和容量計算方法是 j-i、l-i。操作符 [i:j:k],k 主要是用來限制切片的容量,但是不能大于數(shù)組的長度 l,截取得到的切片長度和容量計算方法是 j-i、k-i
40.下面代碼中 A B 兩處應(yīng)該怎么修改才能順利編譯?
func main() {
var m map[string]int //A
m["a"] = 1
if v := m["b"]; v != nil { //B
fmt.Println(v)
}
}
m := make(map[stringint])
v, ok := m["b"]; ok
在 A 處只聲明了map m ,并沒有分配內(nèi)存空間,不能直接賦值,需要使用 make(),都提倡使用 make() 或者字面量的方式直接初始化 map。
B 處,v,k := m["b"] 當 key 為 b 的元素不存在的時候,v 會返回值類型對應(yīng)的零值,k 返回 false。
- 下面代碼輸出什么?
type A interface {
ShowA() int
}
type B interface {
ShowB() int
}
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
c := Work{3}
var a A = c
var b B = c
fmt.Println(a.ShowB())
fmt.Println(b.ShowA())
}
A. 23 13
B. compilation error
參考答案及解析:B。知識點:接口的靜態(tài)類型。a、b 具有相同的動態(tài)類型和動態(tài)值,分別是結(jié)構(gòu)體 work 和 {3};a 的靜態(tài)類型是 A,b 的靜態(tài)類型是 B,接口 A 不包括方法 ShowB(),接口 B 也不包括方法 ShowA(),編譯報錯。看下編譯錯誤:
a.ShowB undefined (type A has no field or method ShowB)
b.ShowA undefined (type B has no field or method ShowA)
42.下面代碼中,x 已聲明,y 沒有聲明,判斷每條語句的對錯。
1. x, _ := f()
2. x, _ = f()
3. x, y := f()
4. x, y = f()
參考答案及解析:錯、對、對、錯。知識點:變量的聲明。1.錯,x 已經(jīng)聲明,不能使用 :=;2.對;3.對,當多值賦值時,:= 左邊的變量無論聲明與否都可以;4.錯,y 沒有聲明。
43.下面代碼輸出什么?
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
func increaseB() (r int) {
defer func() {
r++
}()
return r
}
func main() {
fmt.Println(increaseA())
fmt.Println(increaseB())
}
A. 1 1
B. 0 1
C. 1 0
D. 0 0
B
44.下面代碼輸出什么?
type A interface {
ShowA() int
}
type B interface {
ShowB() int
}
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
var a A = Work{3}
s := a.(Work)
fmt.Println(s.ShowA())
fmt.Println(s.ShowB())
}
A. 13 23
B. compilation error
A 類型斷言可以用來獲取接口的底層值, 通常的語法:i.(Type), 其中i是接口,其實i是接口,Type是類型或接口。編譯時會自動檢測i的動態(tài)類型與type是否一致
45.f1()、f2()、f3() 函數(shù)分別返回什么?
func f1() (r int) {
defer func() {
r++
}()
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
1 5 1
46.下面的題目輸出什么?
type Person struct {
age int
}
func main() {
person := &Person{28}
// 1.
defer fmt.Println(person.age)
// 2.
defer func(p *Person) {
fmt.Println(p.age)
}(person)
// 3.
defer func() {
fmt.Println(person.age)
}()
person.age = 29
}
29 29 28
47.下面代碼輸出什么?
func f() {
defer fmt.Println("D")
fmt.Println("F")
}
func main() {
f()
fmt.Println("M")
}
A. F M D
B. D F M
C. F D M
C
48.下面代碼輸出什么
type Person struct {
age int
}
func main() {
person := &Person{28}
// 1.
defer fmt.Println(person.age)
// 2.
defer func(p *Person) {
fmt.Println(p.age)
}(person)
// 3.
defer func() {
fmt.Println(person.age)
}()
person = &Person{29}
}
29 28 28
49.下面的兩個切片聲明中有什么區(qū)別?哪個更可取?
A. var a []int
B. a := []int{}
.參考答案及解析:A 聲明的是 nil 切片;B 聲明的是長度和容量都為 0 的空切片。第一種切片聲明不會分配內(nèi)存,優(yōu)先選擇
50.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
51.下面 A、B 兩處應(yīng)該填入什么代碼,才能確保順利打印出結(jié)果?
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() 函數(shù)返回參數(shù)是指針類型,所以可以用 & 取結(jié)構(gòu)體的指針;B 處,如果填 *f(),則 p 是 S 類型;如果填 f(),則 p 是 *S 類型,不過都可以使用 p.m 取得結(jié)構(gòu)體的成員
52.下面的代碼各有幾處語法問題,各是什么?
package main
import (
"fmt"
)
func main() {
var x string = nil
if x == nil {
x = "default"
}
fmt.Println(x)
}
string 不能是nil 不能和nil 比較
53.return 之后的 defer 語句會執(zhí)行嗎,下面這段代碼輸出什么?
var a bool = true
func main() {
defer func(){
fmt.Println("1")
}()
if a == true {
fmt.Println("2")
return
}
defer func(){
fmt.Println("3")
}()
}
參考答案及解析:2 1。defer 關(guān)鍵字后面的函數(shù)或者方法想要執(zhí)行必須先注冊,return 之后的 defer 是不能注冊的, 也就不能執(zhí)行后面的函數(shù)或方法。
54.下面這段代碼輸出什么?為什么?
func main() {
s1 := []int{1, 2, 3}
s2 := s1[1:]
s2[1] = 4
fmt.Println(s1)
s2 = append(s2, 5, 6, 7)
fmt.Println(s1)
}
我們知道,golang 中切片底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組。當使用 s1[1:] 獲得切片 s2,和 s1 共享同一個底層數(shù)組,這會導(dǎo)致 s2[1] = 4 語句影響 s1。
而 append 操作會導(dǎo)致底層數(shù)組擴容,生成新的數(shù)組,因此追加數(shù)據(jù)后的 s2 不會影響 s1。
但是為什么對 s2 賦值后影響的卻是 s1 的第三個元素呢?這是因為切片 s2 是從數(shù)組的第二個元素開始,s2 索引為 1 的元素對應(yīng)的是 s1 索引為 2 的元素。
55.下面選項正確的是?
func main() {
if a := 1; false {
} else if b := 2; false {
} else {
println(a, b)
}
}
A. 1 2
B. compilation error
A
56.下面這段代碼輸出什么?
func main() {
m := map[int]string{0:"zero",1:"one"}
for k,v := range m {
fmt.Println(k,v)
}
}
0 zero
1 one
或者是
1 one
0 zero
57.下面這段代碼輸出什么?
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
}
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
參考答案及解析:
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
程序執(zhí)行到 main() 函數(shù)三行代碼的時候,會先執(zhí)行 calc() 函數(shù)的 b 參數(shù),即:calc("10",a,b),輸出:10 1 2 3,得到值 3,因為
defer 定義的函數(shù)是延遲函數(shù),故 calc("1",1,3) 會被延遲執(zhí)行;
程序執(zhí)行到第五行的時候,同樣先執(zhí)行 calc("20",a,b) 輸出:20 0 2 2 得到值 2,同樣將 calc("2",0,2) 延遲執(zhí)行;
程序執(zhí)行到末尾的時候,按照棧先進后出的方式依次執(zhí)行:calc("2",0,2),calc("1",1,3),則就依次輸出:2 0 2 2,1 1 3 4。
58.下面這段代碼輸出什么?為什么?
func (i int) PrintInt () {
fmt.Println(i)
}
func main() {
var i int = 1
i.PrintInt()
}
A. 1
B. compilation error
參考答案及解析:B?;陬愋蛣?chuàng)建的方法必須定義在同一個包內(nèi),上面的代碼基于 int 類型創(chuàng)建了 PrintInt() 方法,由于 int 類型和方法 PrintInt() 定義在不同的包內(nèi),所以編譯出錯。
59.下面這段代碼輸出什么?為什么?
type People interface {
Speak(string) string
}
type Student struct{}
func (stu *Student) Speak(think string) (talk string) {
if think == "speak" {
talk = "speak"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Student{}
think := "speak"
fmt.Println(peo.Speak(think))
}
A. speak
B. compilation error
參考答案及解析:B。編譯錯誤 Student does not implement People (Speak method has pointer receiver),值類型 Student 沒有實現(xiàn)接口的 Speak() 方法,而是指針類型 *Student 實現(xiàn)該方法。
60.下面這段代碼輸出什么?
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 0 0 0
知識點:iota 的用法。
iota 是 golang 語言的常量計數(shù)器,只能在常量的表達式中使用。
iota 在 const 關(guān)鍵字出現(xiàn)時將被重置為0,const中每新增一行常量聲明將使 iota 計數(shù)一次。
推薦閱讀:
https://studygolang.com/articles/2192
61.下面這段代碼輸出什么?為什么?
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func main() {
var s *Student
if s == nil {
fmt.Println("s is nil")
} else {
fmt.Println("s is not nil")
}
var p People = s
if p == nil {
fmt.Println("p is nil")
} else {
fmt.Println("p is not nil")
}
}
s is nil
s is not nil
參考答案及解析:s is nil 和 p is not nil。這道題會不會有點詫異,我們分配給變量 p 的值明明是 nil,然而 p 卻不是 nil。記住一點,當且僅當動態(tài)值和動態(tài)類型都為 nil 時,接口類型值才為 nil。上面的代碼,給變量 p 賦值之后,p 的動態(tài)值是 nil,但是動態(tài)類型卻是 *Student,是一個 nil 指針,所以相等條件不成立。
62.下面這段代碼輸出什么?
type Direction int
const (
North Direction = iota
East
South
West
)
func (d Direction) String() string {
return [...]string{"North", "East", "South", "West"}[d]
}
func main() {
fmt.Println(South)
}
參考答案及解析:South。知識點:iota 的用法、類型的 String() 方法。
根據(jù) iota 的用法推斷出 South 的值是 3;另外,如果類型定義了 String() 方法,當使用 fmt.Printf()、fmt.Print() 和 fmt.Println() 會自動使用 String() 方法,實現(xiàn)字符串的打印。
63.下面代碼輸出什么?
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 的地址,才能夠?qū)?Y 的值賦給 X,但 go 中的 map 的 value 本身是不可尋址的。
有兩個解決辦法:
使用臨時變量
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)
}
修改數(shù)據(jù)結(jié)構(gòu)
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 格式化輸出詳細信息
}
64.下面的代碼有什么問題?
func main() {
fmt.Println([...]int{1} == [2]int{1})
fmt.Println([]int{1} == []int{1})
}
參考答案及解析:有兩處錯誤
go 中不同類型是不能比較的,而數(shù)組長度是數(shù)組類型的一部分,所以 […]int{1} 和 [2]int{1} 是兩種不同的類型,不能比較;
切片是不能比較的;
65.下面這段代碼輸出什么?如果編譯錯誤的話,為什么?
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() 函數(shù)里的 p 是新定義的變量,會遮住全局變量 p,導(dǎo)致執(zhí)行到bar()時程序,全局變量 p 依然還是 nil,程序隨即 Crash。