下面面試題均收集來自go語言中文網
1.下面是否可以編譯透過?不能透過是為什麼
func main() {
list := new([]int)
list = append(list, 1)
fmt.Println(list)
}
解析:不能透過,因為 new 函式返回到 是一個指標,而 append
函式是對切片的操作,不是指標,可以修改成*list= append(*list,1)
.
channel 、slice、map 必須透過 make 初始化,這題也可以使用make
函式,make([]int,0),0表示初始化有 0 個元素,這樣結果才會是[1],如果寫成 make([]int,1),表示有一個元素了,執行結果就為[0,1],預設有一個 0 元素。
2.是否可以編譯透過?如果透過,輸出什麼?
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2)
fmt.Println(s1)
}
解析:不能透過,append切片時,要後面追加…,正確應該:s1 = append(s1, s2...)
,此時結果才會是 []int{1, 2, 3,4,5}
3.已知字串 s=’hello’,把第一個字母變成 c
s := "hello"
c := []byte(s) // 將字串 s 轉換為 []byte 型別
c[0] = 'c'
s = string(c) // 再轉換回 string 型別
fmt.Printf("%s", s)// cello
4、以下程式碼有什麼問題,說明原因
type student struct {
Name string
Age int
}
func pase_student() {
m := make(map[string]*student)
stus := []student{
{Name: "zhou",Age: 24},
{Name: "li",Age: 23},
{Name: "wang",Age: 22},
}
for _,stu := range stus {
m[stu.Name] =&stu
}
}
解答:考點 for
, 與Java的foreach一樣,都是使用副本的方式。所以m[stu.Name]=&stu實際上一致指向同一個指標, 最終該指標的值為遍歷的最後一個struct的值複製,正確寫法因該是:
for i:=0;i<len(stus);i++ {
m[stus[i].Name] = &stus[i]
}
5.下面的程式碼會輸出什麼,並說明原因
func main() {
runtime.GOMAXPROCS(1)
wg := sync.WaitGroup{}
wg.Add(20)
for i := 0; i < 10; i++ {
go func() {
fmt.Println("A: ", i)
wg.Done()
}()
}
for i:= 0; i < 10; i++ {
go func(i int) {
fmt.Println("B: ", i)
wg.Done()
}(i)
}
wg.Wait()
}
解答:考察 go執行的隨機性和閉包。誰也不知道執行後列印的順序是什麼樣的,所以只能說是隨機數字。 但是A:均為輸出10,B:從0~9輸出(順序不定)。 第一個go func中i是外部for的一個變數,地址不變化。遍歷完成後,最終i=10。 故go func執行時,i的值始終是10。
第二個go func中i是函式引數,與外部for中的i完全是兩個變數。 尾部(i)將發生值複製,go func內部指向值複製地址。
6、下面程式碼會觸發異常嗎
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)
}
}
解答:select會隨機選擇一個可用通用做收發操作。 所以程式碼是有肯觸發異常,也有可能不會。 單個chan如果無緩衝時,將會阻塞。但結合 select可以在多個chan間等待執行。有三點原則:
1.select 中只要有一個case能return,則立刻執行。
2.當如果同一時間有多個case均能return則偽隨機方式抽取任意一個執行。
3.如果沒有一個case能return則可以執行”default”塊。
上面語句可能會觸發panic,case到下面的時候就觸犯了,case到上面的就列印1.
7、下面程式碼輸出什麼?
func calc(index string, a, b int) int {
ret := a+ b
fmt.Println(index,a, b, ret)
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
}
解答:這道題考察 defer ,需要注意到defer執行順序和值傳遞. index:1肯定是最後執行的,但是index:1的第三個引數是一個函式,所以最先被呼叫 calc("10",1,2)==>10,1,2,3
,執行index:2時,與之前一樣,需要先呼叫 calc("20",0,2)==>20,0,2,2
,執行到b=1時候開始呼叫,index:2==>calc("2",0,2)==>2,0,2,2
,最後執行 index:1==>calc("1",1,3)==>1,1,3,4
.
8、select可以用於什麼,常用語gorotine的完美退出
解答:golang 的 select 就是監聽 IO 操作,當 IO 操作發生時,觸發相應的動作
每個case語句裡必須是一個IO操作,確切的說,應該是一個面向channel的IO操作
9、map如何順序讀取
map不能順序讀取,是因為他是無序的,想要有序讀取,首先的解決的問題就是,把key變為有序,所以可以把key放入切片,對切片進行排序,遍歷切片,透過key取值。
10、交替列印數字和字母,使用兩個 goroutine
交替列印序列,一個 goroutinue
列印數字, 另外一個goroutine
列印字母, 最終效果如下 12AB34CD56EF78GH910IJ
解答:問題很簡單,使用 channel
來控制列印的進度。使用兩個 channel
,來分別控制數字和字母的列印序列, 數字列印完成後透過 channel
通知字母列印, 字母列印完成後通知數字列印,然後週而復始的工作。
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
chan_n := make(chan bool)
chan_c := make(chan bool, 1)
done := make(chan struct{})
go func() {
for i := 1; i < 11; i += 2 {
<-chan_c
fmt.Print(i)
fmt.Print(i + 1)
chan_n <- true
}
}()
go func() {
char_seq := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"}
for i := 0; i < 10; i += 2 {
<-chan_n
fmt.Print(char_seq[i])
fmt.Print(char_seq[i+1])
chan_c <- true
}
done <- struct{}{}
}()
chan_c <- true
<-done
}
看完上面的程式碼,是不是會有些疑惑,為什麼 chan_c
需要快取,而 chan_n
不需要呢?
當兩個列印 goroutine
無限交替執行時,沒有快取是OK的,但很明顯上面的示例不是,列印數字的 goroutine
先退出,也就沒有了 goroutine
來讀取 chan_c
中的內容了, 而列印字母的goroutine
就會阻塞在 chan_c <- true
,這樣就導致了死鎖。
11.下面兩段程式碼輸出什麼。
// 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],需要注意。
12.下面這段程式碼有什麼缺陷
func funcMui(x,y int)(sum int,error){
return x+y,nil
}
參考答案:第二個返回值沒有命名。
參考解析:
在函式有多個返回值時,只要有一個返回值有命名,其他的也必須命名。如果有多個返回值必須加上括號();如果只有一個返回值且命名也必須加上括號()。這裡的第一個返回值有命名 sum,第二個沒有命名,所以錯誤。
13.new() 與 make() 的區別
參考答案:
new(T) 和 make(T,args) 是 Go 語言內建函式,用來分配記憶體,但適用的型別不同。
new(T) 會為 T 型別的新值分配已置零的記憶體空間,並返回地址(指標),即型別為 *T
的值。換句話說就是,返回一個指標,該指標指向新分配的、型別為 T 的零值。適用於值型別,如陣列、結構體等。
make(T,args) 返回初始化之後的 T 型別的值,這個值並不是 T 型別的零值,也不是指標 *T
,是經過初始化之後的 T 的引用。make() 只適用於 slice、map 和 channel.
14 下面這段程式碼能否透過編譯?不能的話,原因是什麼?如果透過,輸出什麼?
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
這道題目考的是結構體的比較,有幾個需要注意的地方:
- 結構體只能比較是否相等,但是不能比較大小。
- 相同型別的結構體才能夠進行比較,結構體是否相同不但與屬性型別有關,還與屬性順序相關,sn3 與 sn1 就是不同的結構體;
sn3:= struct {
name string
age int
}{age:11,name:"qq"}
- 如果 struct 的所有成員都可以比較,則該 struct 就可以透過 == 或 != 進行比較是否相等,比較時逐個項進行比較,如果每一項都相等,則兩個結構體才相等,否則不相等;
那什麼是可比較的呢,常見的有 bool、數值型、字元、指標、陣列等,像切片、map、函式等是不能比較的。
15 下面這段程式碼能否透過編譯?如果透過,輸出什麼?
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。
這道題考的是型別別名與型別定義的區別。
第 5 行程式碼是基於型別 int 建立了新型別 MyInt1,第 6 行程式碼是建立了 int 的型別別名 MyInt2,注意型別別名的定義時 = 。所以,第 10 行程式碼相當於是將 int 型別的變數賦值給 MyInt1 型別的變數,Go 是強型別語言,編譯當然不透過;而 MyInt2 只是 int 的別名,本質上還是 int,可以賦值。
第 10 行程式碼的賦值可以使用強制型別轉化 var i1 MyInt1 = MyInt1(i).
16 關於字串連線,下面語法正確的是?
A. str := ‘abc’ + ‘123’
B. str := “abc” + “123”
C str := ‘123’ + “abc”
D. fmt.Sprintf(“abc%d”, 123)
參考答案及解析:BD。知識點:字串連線。除了以上兩種連線方式,還有 strings.Join()、buffer.WriteString()等。
17 下面這段程式碼能否編譯透過?如果可以,輸出什麼?
const (
x = iota
_
y
z = "zz"
k
p = iota
)
func main() {
fmt.Println(x,y,z,k,p)
}
參考答案及解析:編譯透過,輸出:0 2 zz zz 5。知識點:iota 的使用。iota在const關鍵字出現時將被重置為0(const內部的第一行之前),const中每新增一行常量宣告將使iota計數一次(iota可理解為const語句塊中的行索引)。z=”zz”,下面k 預設就等於了上面z的值,下面的p雖然等於iota,但是沒有const關鍵字,所以還是自增。
18 下面賦值正確的是()
A. var x = nil
B. var x interface{} = nil
C. var x string = nil
D. var x error = nil
參考答案及解析:BD。知識點:nil 值。nil 只能賦值給指標、chan、func、interface、map 或 slice 型別的變數。強調下 D 選項的 error 型別,它是一種內建介面型別,看下方貼出的原始碼就知道,所以 D 是對的。
type error interface {
Error() string
}
19 關於init函式,下面說法正確的是()
A. 一個包中,可以包含多個 init 函式;
B. 程式編譯時,先執行依賴包的 init 函式,再執行 main 包內的 init 函式;
C. main 包中,不能有 init 函式;
D. init 函式可以被其他函式呼叫;
1.參考答案及解析:AB。關於 init() 函式有幾個需要注意的地方:
init() 函式是用於程式執行前做包的初始化的函式,比如初始化包裡的變數等;
一個包可以出線多個 init() 函式,一個原始檔也可以包含多個 init() 函式;
同一個包中多個 init() 函式的執行順序沒有明確定義,但是不同包的init函式是根據包匯入的依賴關係決定的(看下圖);
init() 函式在程式碼中不能被顯示呼叫、不能被引用(賦值給函式變數),否則出現編譯錯誤;
一個包被引用多次,如 A import B,C import B,A import C,B 被引用多次,但 B 包只會初始化一次;
引入包,不可出現死循壞。即 A import B,B import A,這種情況編譯失敗;
20 下面這段程式碼輸出什麼以及原因?
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,而不是函式的返回值,所以輸出 not nil。如果換成h:=hello()
;那麼h= nil;就列印nil了。
21 下面這段程式碼能否編譯透過?如果可以,輸出什麼?
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")
}
}
參考答案及解析:編譯失敗。考點:型別選擇,型別選擇的語法形如:i.(type),其中 i 是介面,type 是固定關鍵字,需要注意的是,只有介面型別才可以使用型別選擇。看下關於介面的文章,這裡要斷言型別,需要用到反射。
22 關於channel,下面語法正確的是()
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-
參考答案及解析:ABC。A、B都是宣告 channel;C 讀取 channel;寫 channel 是必須帶上值,所以 D 錯誤。
23 下面這段程式碼輸出什麼?
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。
24 下面這段程式碼輸出什麼?
func main() {
a := 5
b := 8.1
fmt.Println(a + b)
}
A.13.1
B.13
C.compilation error
參考答案及解析:C。a 的型別是 int,b 的型別是 float,兩個不同型別的數值不能相加,編譯報錯。
25 下面這段程式碼輸出什麼?
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]。基於陣列(切片)可以使用運算子 [i,j] 建立新的切片,從索引 i,到索引 j 結束,擷取已有陣列(切片)的任意部分,返回新的切片,新切片的值包含原陣列(切片)的 i 索引的值,但是不包含 j 索引的值。i、j 都是可選的,i 如果省略,預設是 0,j 如果省略,預設是原陣列(切片)的長度。i、j 都不能超過這個長度值。
假如底層陣列的大小為 k,擷取之後獲得的切片的長度和容量的計算方法:長度:j-i,容量:k-i。
擷取運算子還可以有第三個引數,形如 [i,j,k],第三個引數 k 用來限制新切片的容量,但不能超過原陣列(切片)的底層陣列大小。擷取獲得的切片的長度和容量分別是:j-i、k-i。
所以例子中,切片 t 為 [4],長度和容量都是 1。
26 下面這段程式碼輸出什麼?
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 是不同的型別,是不能比較的,所以編譯錯誤。
27 關於 cap() 函式的適用型別,下面說法正確的是()
A. array
B. slice
C. map
D. channel
參考答案及解析:ABD。知識點:cap(),cap() 函式不適用 map。
28 下面這段程式碼輸出什麼?
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。
29 下面這段程式碼輸出什麼?
func main() {
s := make(map[string]int)
delete(s, "h")
fmt.Println(s["h"])
}
A. runtime panic
B. 0
C. compilation error
參考答案及解析:B。刪除 map 不存在的鍵值對時,不會報錯,相當於沒有任何作用;獲取不存在的減值對時,返回值型別對應的零值,所以返回 0。
30 下面這段程式碼輸出什麼?
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。知識點:結構體巢狀。在巢狀結構體中,People 稱為內部型別,Teacher 稱為外部型別;透過巢狀,內部型別的屬性、方法,可以為外部型別所有,就好像是外部型別自己的一樣。此外,外部型別還可以定義自己的屬性和方法,甚至可以定義與內部相同的方法,這樣內部型別的方法就會被“遮蔽”。這個例子中的 ShowB() 就是同名方法。
31 下面這段程式碼輸出什麼?
func hello(i int) {
fmt.Println(i)
}
func main() {
i := 5
defer hello(i)
i = i + 10
}
參考答案及解析:5。這個例子中,hello() 函式的引數在執行 defer 語句的時候會儲存一份副本,在實際呼叫 hello() 函式時用,所以是 5.
32 下面這段程式碼輸出什麼?
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 方法。
33 下面程式碼輸出什麼?
func main() {
str := "hello"
str[0] = 'x'
fmt.Println(str)
}
A. hello
B. xello
C. compilation error
參考程式碼及解析:C。知識點:常量,Go 語言中的字串是隻讀的。如果要修改,需要轉換成位元組切片去操作,然後再轉成字串。
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。知識點:指標,incr() 函式里的 p 是 *int
型別的指標,指向的是 main() 函式的變數 p 的地址。第 2 行程式碼是將該地址的值執行一個自增操作,incr() 返回自增後的結果。
35 對 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。知識點:可變函式。
36 下面這段程式碼輸出什麼?
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。知識點:介面。一種型別實現多個介面,結構體 Work 分別實現了介面 A、B,所以介面變數 a、b 呼叫各自的方法 ShowA() 和 ShowB(),輸出 13、23。
37 .切片 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。知識點:陣列或切片的擷取操作。擷取操作有帶 2 個或者 3 個引數,形如:[i:j] 和 [ik],假設擷取物件的底層陣列長度為 l。在運算子 [i:j] 中,如果 i 省略,預設 0,如果 j 省略,預設底層陣列的長度,擷取得到的切片長度和容量計算方法是 j-i、l-i。運算子 [ik],k 主要是用來限制切片的容量,但是不能大於陣列的長度 l,擷取得到的切片長度和容量計算方法是 j-i、k-i。
38 下面程式碼中 A B 兩處應該怎麼修改才能順利編譯?
func main() {
var m map[string]int //A
m["a"] = 1
if v := m["b"]; v != nil { //B
fmt.Println(v)
}
}
參考答案及解析:
func main() {
m := make(map[string]int)
m["a"] = 1
if v,ok := m["b"]; ok {
fmt.Println(v)
}
}
在 A 處只宣告瞭map m ,並沒有分配記憶體空間,不能直接賦值,需要使用 make(),都提倡使用 make() 或者字面量的方式直接初始化 map。
B 處,v,k := m[“b”] 當 key 為 b 的元素不存在的時候,v 會返回值型別對應的零值,k 返回 false。
39 下面程式碼輸出什麼?
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。知識點:介面的靜態型別。a、b 具有相同的動態型別和動態值,分別是結構體 work 和 {3};a 的靜態型別是 A,b 的靜態型別是 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)
40 下面程式碼輸出什麼?
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。知識點:defer、返回值。注意一下,increaseA() 的返回引數是匿名,increaseB() 是具名。
41 下面程式碼輸出什麼?
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) //把a 轉換成Work 型別
fmt.Println(s.ShowA())
fmt.Println(s.ShowB())
}
A. 13 23
B. compilation error
參考答案及解析:A。知識點:型別斷言。
42 下面程式碼段輸出什麼?
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。變數 person 是一個指標變數 。
1.person.age 此時是將 28 當做 defer 函式的引數,會把 28 快取在棧中,等到最後執行該 defer 語句的時候取出,即輸出 28;
2.defer 快取的是結構體 Person{28} 的地址,最終 Person{28} 的 age 被重新賦值為 29,所以 defer 語句最後執行的時候,依靠快取的地址取出的 age 便是 29,即輸出 29;
3.閉包引用,輸出 29;
又由於 defer 的執行順序為先進後出,即 3 2 1,所以輸出 29 29 28。
43 下面程式碼輸出什麼?
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。這道題在上個題目的基礎上做了一點點小改動,前一題最後一行程式碼 person.age = 29 是修改引用物件的成員 age,這題最後一行程式碼 person = &Person{29} 是修改引用物件本身,來看看有什麼區別。
1處.person.age 這一行程式碼跟之前含義是一樣的,此時是將 28 當做 defer 函式的引數,會把 28 快取在棧中,等到最後執行該 defer 語句的時候取出,即輸出 28;
2處.defer 快取的是結構體 Person{28} 的地址,這個地址指向的結構體沒有被改變,最後 defer 語句後面的函式執行的時候取出仍是 28;
3處.閉包引用,person 的值已經被改變,指向結構體 Person{29},所以輸出 29.
由於 defer 的執行順序為先進後出,即 3 2 1,所以輸出 29 28 28。
44 下面的兩個切片宣告中有什麼區別?哪個更可取?
A. var a []int
B. a := []int{}
參考答案及解析:A 宣告的是 nil 切片;B 宣告的是長度和容量都為 0 的空切片。第一種切片宣告不會分配記憶體,優先選擇。
45 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
}
參考答案及解析:BD。函式引數為 interface{} 時可以接收任何型別的引數,包括使用者自定義型別等,即使是接收指標型別也用 interface{},而不是使用 *interface{}
。
永遠不要使用一個指標指向一個介面型別,因為它已經是一個指標。
46 下面 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 取得結構體的成員。
47 下面的程式碼有幾處語法問題,各是什麼?
package main
import (
"fmt"
)
func main() {
var x string = nil
if x == nil {
x = "default"
}
fmt.Println(x)
}
參考答案及解析:兩個地方有語法問題。golang 的字串型別是不能賦值 nil 的,也不能跟 nil 比較。
48 return 之後的 defer 語句會執行嗎,下面這段程式碼輸出什麼?
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 關鍵字後面的函式或者方法想要執行必須先註冊,return 之後的 defer 是不能註冊的, 也就不能執行後面的函式或方法
48 下面這段程式碼輸出什麼?為什麼?
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)
}
參考答案及解析:
[1 2 4]
[1 2 4]
我們知道,golang 中切片底層的資料結構是陣列。當使用 s1[1:] 獲得切片 s2,和 s1 共享同一個底層陣列,這會導致 s2[1] = 4 語句影響 s1。
而 append 操作會導致底層陣列擴容,生成新的陣列,因此追加資料後的 s2 不會影響 s1。
但是為什麼對 s2 賦值後影響的卻是 s1 的第三個元素呢?這是因為切片 s2 是從陣列的第二個元素開始,s2 索引為 1 的元素對應的是 s1 索引為 2 的元素。
49 下面選項正確的是?
func main() {
if a := 1; false {
} else if b := 2; false {
} else {
println(a, b)
}
}
A. 1 2
B. compilation error
參考答案及解析:A。知識點:程式碼塊和變數作用域。
50 下面這段程式碼輸出什麼?
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
map 的輸出是無序的。
本作品採用《CC 協議》,轉載必須註明作者和本文連結