Golang M 2023 6 topic
阅读目录
- topic
- defer panic
- slice make
- new() 与 make()
- 变量声明的简短模式
- 结构体
- 题
- 1 下面这段代码能否通过编译?如果通过,输出什么?
- 2 关于字符串连接,下面语法正确的是?
- 3 下面这段代码能否编译通过?如果可以,输出什么?
- 4 下面赋值正确的是?
- 5 关于 init 函数,下面说法正确的是?
- 6 下面这段代码输出什么以及原因?
- 7 下面这段代码能否编译通过?如果可以,输出什么?
- 8 关于channel,下面语法正确的是?
- 9 下面这段代码输出什么?
- 10 下面这段代码输出什么?
- 11 关于 cap() 函数的适用类型,下面说法正确的是?
- 12 下面这段代码输出什么?
- 13 下面这段代码输出什么?
- 14 下面属于关键字的是?
- 15 下面这段代码输出什么?
- 16 下面这段代码输出什么?
- 17 定义一个包内全局字符串变量,下面语法正确的是?
- 18 下面这段代码输出什么?
- 19 下面代码输出什么?
- 20 下面代码输出什么?
- 21 下面代码下划线处可以填入哪个选项?
- 22 下面这段代码输出什么?
- 23 下面这段代码输出什么?
- 24 切片 a、b、c 的长度和容量分别是多少?
- 25 下面代码中 A B 两处应该怎么修改才能顺利编译?
- 26 下面代码中,x 已声明,y 没有声明,判断每条语句的对错?
- 27 下面代码输出什么?
- 28 类型断言
- 29 匿名返回
topic
类型转换和类型断言
类型转换语法:Type(expression)
类型断言语法为:expression.(Type)
1 类型转换示例代码
package main
import "fmt"
//典型的类型转换示例
func main() {
// 类型转换示例
var a int = 5
var b int = 2
var c float32
c = float32(a) / float32(b) //这里就是典型的类型转换
fmt.Printf("a的类型:%T\n", a)
fmt.Printf("b的类型:%T\n", b)
fmt.Printf("c的类型:%T\n", c)
fmt.Printf("c的值:%v\n", c)
//如果我们不进行类型转换,看下输出结果
fmt.Printf("不进行类型转换的输出结果:%v\n", a/b) //从结果可以看出值为2,丢失了精度
}
PS E:\TEXT\test_go\one> go run .\main.go
a的类型:int
b的类型:int
c的类型:float32
c的值:2.5
不进行类型转换的输出结果:2
PS E:\TEXT\test_go\one>
2 类型断言代码示例
注意:对于类型断言,expression 必须是接口类型。
package main
import "fmt"
//经典的类型断言示例 搭配switch使用
func main() {
var x interface{}
x = 1
//这就是上面说的expression.(Type)
switch x.(type) {
case float32:
fmt.Println("类型是:float32")
case string:
fmt.Println("类型是:string")
case int:
fmt.Println("类型是:int")
default:
fmt.Println("未知类型")
}
}
PS E:\TEXT\test_go\one> go run .\main.go
类型是:int
PS E:\TEXT\test_go\one>
全局变量
定义全局变量必须用 var
,全局变量要定义在函数之外,而在函数之外定义的变量只能用 var
定义。
全局变量使用 var,编译通过。
package main
var name = "test"
func main() {
// fmt.Println(name)test
}
全局变量不使用 var,编译不通过。
Go语言中变量、init函数、main函数的执行顺序
1 首先初始化导入包的变量和常量。
2 然后执行导入包的 init 函数。
3 然后初始化本包的变量和常量。
4 然后执行本包的 init 函数。
5 最后执行本包的 main 函数。
Go接口总结
如果两个接口拥有相同方法列表(顺序可以不一致),那么这两个接口实质上同一个接口。
接口 A 是接口 B 的子集,意味着 A 的方法 B 中都有,那么 A 是 B 的基类,所以A=B 是可行的。
接口是否能够调用成功,需要运行的时候才能知道。
接口赋值是否可行,在编译阶段就可以知道。
Go字符串
Go语言中的字符串不支持下标操作:
在 go 语言中,字符串是一种基本类型,和其它开发语言不同,Go 的字符串是由单个字节连接起来的。
Go 语言统一使用 utf-8 编码标识 Unicode 文本。当字符为 ASCII 编码时,占用 1个字节,其它字符占用 2 到 4 个字节,中文占用 3 个字节。
如何修改字符串的内容有三种方法:
1 将字符串转成 byte 切片,再根据下标替换内容。
2 将字符串转为 rune 切片,再根据下标替换内容。
3 使用原生包 strings 中的 Replace() 方法。
package main
import (
"fmt"
"strings"
)
func main() {
//第一种方法 将字符串转成byte切片
s := "hello"
s2 := []byte(s)
s2[0] = 'x'
fmt.Printf("转成byte切片:%v\n", string(s2))
//打印结果:转成byte切片:xello
// 第二种方法 将字符串转为rune切片
s3 := "潜力股"
//注意:中文字符串要进行修改,只能转成rune切片,不能转成byte切片
s4 := []rune(s3)
s4[1] = '水'
fmt.Printf("转成rune切片:%v\n", string(s4))
//打印结果:转成rune切片:潜水股
// 第三种方法,使用原生包strings 中的 Replace() 方法
s5 := "abcdef"
old := "abc"
newString := "ABC"
//最后一个参数n的作用是:返回将s5中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串。
s6 := strings.Replace(s5, old, newString, -1)
fmt.Printf("strings替换之后的:%v\n", s6)
//打印结果:strings替换之后的:ABCdef
}
PS E:\TEXT\test_go\one> go run .\main.go
转成byte切片:xello
转成rune切片:潜水股
strings替换之后的:ABCdef
PS E:\TEXT\test_go\one>
slice
slice 可以通过 append 方式实现元素的删除。
*
切片使用冒号分隔时遵循"前闭后开"
原则,即包括前面的值,不包括后面的值。
1 删除单个元素
package main
import "fmt"
//使用append 删除单个元素
func main() {
var data = []int{0, 1, 2, 3, 4, 5}
//删除元素2
index := 2
//切片使用冒号分隔时遵循"前闭后开"原则,即包括前面的值,不包括后面的值。
data1 := append(data[:index], data[index+1:]...)
fmt.Println(data1)
}
PS E:\TEXT\test_go\one> go run .\main.go
[0 1 3 4 5]
PS E:\TEXT\test_go\one>
2 删除多个元素
package main
import "fmt"
//使用append 删除多个元素
func main() {
var data = []int{0, 1, 2, 3, 4, 5}
//删除元素0,1,2
index := 2
//切片使用冒号分隔时遵循"前闭后开"原则,即包括前面的值,不包括后面的值。
data1 := append(data[:0], data[index+1:]...)
fmt.Println(data1)
}
PS E:\TEXT\test_go\one> go run .\main.go
[3 4 5]
PS E:\TEXT\test_go\one>
3 切片下标打印显示
package main
import (
"fmt"
"sort"
)
func main() {
// var x = []int{2: 2, 3, 0: 1}
// fmt.Println(x) // 用了下标显示 [1 0 2 3]
var x = []int{2, 1, 0, 3}
fmt.Println(x) // [2 1 0 3]
sort.Ints(x)
fmt.Println(x) // [0 1 2 3]
}
Go 语言是如何实现切片扩容的?
package main
import "fmt"
func main() {
arr := make([]int, 0)
for i := 0; i < 200; i++ {
fmt.Println("len 为", len(arr), "cap 为", cap(arr))
arr = append(arr, i)
}
}
看下面代码的 defer 的执行顺序是什么? defer的作用和特点是什么?
defer的作用是:
你只需要在调用普通函数或方法前加上关键字defer,就完成了defer所需要的语法。
当defer语句被执行时,跟在defer后面的函数会被延迟执行。
直到包含该defer语句的函数执行完毕时,defer后的函数才会被执行,不论包含defer语句的函数是通过return正常结束,还是由于panic导致的异常结束。
你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。
defer 的常用场景:
通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。
释放资源的defer应该直接跟在请求资源的语句后。
Golang Slice 的底层实现
切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对底层数组的抽象。
因为基于数组实现,所以它的底层的内存是连续分配的,效率非常高,还可以通过索引获得数据,可以迭代以及垃圾回收优化。
切片本身并不是动态数组或者数组指针。
它内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。
切片本身是一个只读对象,其工作机制类似数组指针的一种封装。
切片对象非常小,是因为它是只有3个字段的数据结构:
- 指向底层数组的指针
- 切片的长度
- 切片的容量
Golang Slice 的扩容机制,有什么注意点?
Go 中切片扩容的策略是这样的:
首先判断,如果新申请容量大于2倍的旧容量,最终容量就是新申请的容量。
否则判断,如果旧切片的长度小于1024,则最终容量就是旧容量的两倍。
否则判断,如果旧切片长度大于等于1024,则最终容量从旧容量开始循环增加原来的 1/4, 直到最终容量大于等于新申请的容量。
如果最终容量计算值溢出,则最终容量就是新申请容量。
扩容前后的 Slice 是否相同?
情况一:
原数组还有容量可以扩容(实际容量没有填充完),这种情况下,扩容以后的数组还是指向原来的数组,对一个切片的操作可能影响多个指针指向相同地址的Slice。
情况二:
原来数组的容量已经达到了最大值,再想扩容, Go 默认会先开一片内存区域,把原来的值拷贝过来,然后再执行 append() 操作。
这种情况丝毫不影响原数组。
要复制一个Slice,最好使用Copy函数。
切片赋值
1 切片赋值后修改赋值的变量原数据会变
package main
import "fmt"
func main() {
// 设置元素数量为1000
const elementCount = 3
// 预分配足够多的元素切片
srcData := make([]int, elementCount)
// 将切片赋值
for i := 0; i < elementCount; i++ {
srcData[i] = i
}
// 引用切片数据
refData := srcData
srcData[0] = 999
refData[1] = 888
fmt.Println(refData)
fmt.Println(srcData)
}
E:\TEXT\test_go\one\test>go run main.go
[999 888 2]
[999 888 2]
E:\TEXT\test_go\one\test>
2 slice复制,需要使用copy(dst, src)函数
这是copy的一个坑,如果要做slice复制,需要使用copy(dst, src)函数。
但是copy实际复制的元素个数是从两个slice中取最小值,即min(len(dst), len(src)),如果len(dst)=0则没有办法完成复制。
func main() {
src := []int{1, 2, 3}
dst := make([]int, 0)
copy(dst, src)
fmt.Print(dst) // []
}
func main() {
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
fmt.Print(dst) //[1 2 3]
}
3 copy 复制到另一个数组切片中,修改值两个变量不会相互影响
package main
import "fmt"
func main() {
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
// fmt.Print(dst) //[1 2 3]
dst[1] = 8888
fmt.Println(src)
fmt.Println(dst)
}
defer panic
参考解析:
defer 的执行顺序是后进先出。
当出现 panic 语句的时候,会先按照 defer 的后进先出的顺序执行,最后才会执行panic。
package main
import "fmt"
func main() {
defer_all()
}
func defer_all() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
E:\TEXT\test_go\one\test>go run main.go
打印后
打印中
打印前
panic: 触发异常
goroutine 1 [running]:
main.defer_all()
E:/TEXT/test_go/one/test/main.go:13 +0x6b
main.main()
E:/TEXT/test_go/one/test/main.go:6 +0x17
exit status 2
E:\TEXT\test_go\one\test>
slice make
1 遍历
package main
import "fmt"
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)
}
}
直接给答案:
E:\TEXT\test_go\one\test>go run main.go
1 -> 3
2 -> 3
3 -> 3
0 -> 3
E:\TEXT\test_go\one\test>
参考解析:
这是新手常会犯的错误写法,for range 循环的时候会创建每个元素的副本,而不是元素的引用,所以 m[key] = &val
取的都是变量 val 的地址,所以最后 map 中的所有元素的值都是变量 val 的地址,因为最后 val 被赋值为3,所有输出都是3。
正确的写法:
package main
import "fmt"
func main() {
slice := []int{0, 1, 2, 3}
m := make(map[int]*int)
for key, val := range slice {
value := val
m[key] = &value
}
for k, v := range m {
fmt.Println(k, "->", *v)
}
}
E:\TEXT\test_go\one\test>go run main.go
3 -> 3
0 -> 0
1 -> 1
2 -> 2
E:\TEXT\test_go\one\test>
2 下面这段代码能否通过编译,如果可以,输出什么?
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
s2 := []int{5, 6}
s1 = append(s1, s2)
fmt.Println(s1)
}
E:\TEXT\test_go\one\test>go run main.go
# command-line-arguments
.\main.go:8:18: cannot use s2 (variable of type []int) as type int in argument to append
E:\TEXT\test_go\one\test>
参考答案及解析:
不能通过编译。
append() 的第二个参数不能直接使用 slice,需使用 …
操作符,将一个切片追加到另一个切片上:append(s1,s2…)
。
或者直接跟上元素,形如:append(s1,1,2,3)
。
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
s2 := []int{5, 6}
s1 = append(s1, s2...)
fmt.Println(s1)
// [1 2 3 5 6]
}
new() 与 make()
1 下面两段代码输出什么?
package main
import "fmt"
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
// [0 0 0 0 0 1 2 3]
}
package main
import "fmt"
func main() {
s := make([]int, 0)
s = append(s, 1, 2, 3, 4)
fmt.Println(s)
// [1 2 3 4]
}
参考解析:
这道题考的是使用 append 向 slice 添加元素,第一段代码常见的错误是 [1 2 3],需要注意。
2 下面这段代码有什么缺陷
func funcMui(x,y int)(sum int,error){
return x+y,nil
}
参考答案:第二个返回值没有命名。
参考解析:
在函数有多个返回值时,只要有一个返回值有命名,其他的也必须命名。如果有多个返回值必须加上括号();
如果只有一个返回值且命名也必须加上括号()。
这里的第一个返回值有命名 sum,第二个没有命名,所以错误。
3 new() 与 make() 的区别
参考答案:
new(T) 和 make(T,args) 是 Go 语言内建函数,用来分配内存,但适用的类型不同。
new(T) 会为 T 类型的新值分配已置零的内存空间,并返回地址(指针),即类型为 *T
的值。换句话说就是,返回一个指针,该指针指向新分配的、类型为 T 的零值。
适用于值类型,如数组、结构体等。
make(T,args) 返回初始化之后的 T 类型的值,这个值并不是 T 类型的零值,也不是指针 *T
,是经过初始化之后的 T 的引用。
make() 只适用于 slice、map 和 channel。
4 下面这段代码能否通过编译,不能的话原因是什么;如果能,输出什么?
package main
import "fmt"
func main() {
list := new([]int)
list = append(list, 1)
fmt.Println(list)
}
参考答案及解析:
不能通过编译,new([]int)
之后的 list 是一个 *[]int
类型的指针,不能对指针执行 append 操作。
可以使用 make() 初始化之后再用。
同样的,map 和 channel 建议使用 make() 或字面量的方式初始化,不要用 new() 。
package main
import "fmt"
func main() {
list := make([]int, 0)
list = append(list, 1)
fmt.Println(list)
}
5 通过指针变量 p 访问其成员变量 name,有哪几种方式?
A.p.name
B.(&p).name
C.(*p).name
D.p->name
参考答案及解析:AC
。&
取址运算符,*
指针解引用。
变量声明的简短模式
1 下面这段代码能否通过编译,如果可以,输出什么?
package main
import "fmt"
var (
size:=1024
max_size = size * 2
)
func main() {
fmt.Println(size, max_size)
}
参考答案及解析:
不能通过编译。
这道题的主要知识点是变量声明的简短模式,形如:x := 100
。
但这种声明方式有限制:
1.必须使用显示初始化;
2.不能提供数据类型,编译器会自动推导;
3.只能在函数内部使用简短模式;
结构体
1 下面这段代码能否通过编译?不能的话,原因是什么?如果通过,输出什么?
package main
import "fmt"
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")
// 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
这道题目考的是结构体的比较,有几个需要注意的地方:
- 1 结构体只能比较是否相等,但是不能比较大小。
- 2 相同类型的结构体才能够进行比较,结构体是否相同不但与属性类型有关,还与属性顺序相关,sn3 与 sn1 就是不同的结构体;
sn3:= struct {
name string
age int
}{age:11,name:"qq"}
- 4 如果 struct 的所有成员都可以比较,则该 struct 就可以通过
==
或!=
进行比较是否相等,比较时逐个项进行比较,如果每一项都相等,则两个结构体才相等,否则不相等;
那什么是可比较的呢,常见的有 bool、数值型、字符、指针、数组等。
切片、map、函数等是不能比较的。
题
1 下面这段代码能否通过编译?如果通过,输出什么?
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)
。
package main
import "fmt"
type MyInt1 int
type MyInt2 = int
func main() {
var i int = 0
var i1 MyInt1 = MyInt1(i)
var i2 MyInt2 = i
fmt.Println(i1, i2)
}
2 关于字符串连接,下面语法正确的是?
A. str := ‘abc’ + ‘123’
B. str := “abc” + “123”
str := ‘123’ + “abc”
D. fmt.Sprintf(“abc%d”, 123)
参考答案及解析:BD。
知识点:字符串连接。
除了以上两种连接方式,还有 strings.Join()
、buffer.WriteString()
等。
3 下面这段代码能否编译通过?如果可以,输出什么?
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
}
参考答案及解析:
编译通过,输出:0 2 zz zz 5。
知识点:iota 的使用。
4 下面赋值正确的是?
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 是对的。
5 关于 init 函数,下面说法正确的是?
A. 一个包中,可以包含多个 init 函数;
B. 程序编译时,先执行依赖包的 init 函数,再执行 main 包内的 init 函数;
C. main 包中,不能有 init 函数;
D. init 函数可以被其他函数调用;
参考答案及解析:AB。
关于 init() 函数有几个需要注意的地方:
1 init() 函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等;
2 一个包可以出线多个 init() 函数,一个源文件也可以包含多个 init() 函数;
3 同一个包中多个 init() 函数的执行顺序没有明确定义,但是不同包的 init 函数是根据包导入的依赖关系决定的(看下图);
4 init() 函数在代码中不能被显示调用、不能被引用(赋值给函数变量),否则出现编译错误;
5 一个包被引用多次,如 A import B,C import B,A import C,B 被引用多次,但 B 包只会初始化一次;
6 引入包,不可出现死循坏。
即 A import B,B import A,这种情况编译失败;
6 下面这段代码输出什么以及原因?
package main
import "fmt"
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。
package main
import "fmt"
func hello() []string {
return nil
}
func main() {
h := hello
if h() == nil {
fmt.Println("nil")
} else {
fmt.Println("not nil")
}
}
输出:nil
7 下面这段代码能否编译通过?如果可以,输出什么?
package main
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 是固定关键字,需要注意的是, 只有接口类型才可以使用类型选择。
package main
func iType(x interface{}) {
switch x.(type) {
case int:
println("int")
println(fmt.Sprintf("int %#v", x))
// int 1
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}
}
func GetValue() int {
return 1
}
func main() {
i := GetValue()
iType(i)
}
8 关于channel,下面语法正确的是?
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-
参考答案及解析:ABC。
A、B都是声明 channel;
C 读取 channel;
写 channel 是必须带上值,所以 D 错误。
9 下面这段代码输出什么?
package main
import "fmt"
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
E:\TEXT\test_go\one\test>go run main.go
0
E:\TEXT\test_go\one\test>
参考答案及解析:A。
打印一个 map 中不存在的值时,返回元素类型的零值。
这个例子中,m 的类型是 map[person]int
,因为 m 中不存在 p,所以打印 int 类型的零值,即 0。
10 下面这段代码输出什么?
package main
import "fmt"
func hello(num ...int) {
num[0] = 18
}
func main() {
i := []int{5, 6, 7}
hello(i...)
fmt.Println(i[0])
}
A.18
B.5
C.Compilation error
参考答案及解析:18。
知识点:可变函数。
11 关于 cap() 函数的适用类型,下面说法正确的是?
A. array
B. slice
C. map
D. channel
参考答案及解析:ABD。
知识点:cap(),cap() 函数不适用 map。
12 下面这段代码输出什么?
package main
import "fmt"
func main() {
var i interface{}
if i == nil {
fmt.Println("nil") // nil
return
}
fmt.Println("not nil")
}
A. nil
B. not nil
C. compilation error
参考答案及解析:A。
当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil。
13 下面这段代码输出什么?
package main
import "fmt"
func main() {
s := make(map[string]int)
delete(s, "h")
fmt.Println(s["h"]) // 0
}
A. runtime panic
B. 0
C. compilation error
参考答案及解析:B。
删除 map 不存在的键值对时,不会报错,相当于没有任何作用;
获取不存在的减值对时,返回值类型对应的零值,所以返回 0。
14 下面属于关键字的是?
A.func
B.struct
C.class
D.defer
参考答案及解析:ABD。
知识点:Go 语言的关键字。Go 语言有 25 个关键字,看下图:
15 下面这段代码输出什么?
package main
import "fmt"
func main() {
i := -5
j := +5
fmt.Printf("%+d %+d", i, j)
// -5 +5
}
A. -5 +5
B. +5 +5
C. 0 0
参考答案及解析:A。
%d 表示输出十进制数字,+表示输出数值的符号。
这里不表示取反。
16 下面这段代码输出什么?
package main
import "fmt"
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
}
参考答案及解析:teacher showB。
知识点:结构体嵌套。
在嵌套结构体中,People 称为内部类型,Teacher 称为外部类型;
通过嵌套,内部类型的属性、方法,可以为外部类型所有,就好像是外部类型自己的一样。
此外,外部类型还可以定义自己的属性和方法,甚至可以定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。
这个例子中的 ShowB() 就是同名方法。
17 定义一个包内全局字符串变量,下面语法正确的是?
A. var str string
B. str := “”
C. str = “”
D. var str = “”
参考答案及解析:AD。
B 只支持局部变量声明;
C 是赋值,str 必须在这之前已经声明;
18 下面这段代码输出什么?
package main
import "fmt"
func hello(i int) {
fmt.Println(i)
}
func main() {
i := 5
defer hello(i)
i = i + 10
}
参考答案及解析:5。
这个例子中,hello() 函数的参数在执行 defer 语句的时候会保存一份副本,在实际调用 hello() 函数时用,所以是 5。
19 下面代码输出什么?
func main() {
str := "hello"
str[0] = 'x'
fmt.Println(str)
}
A. hello
B. xello
C. compilation error
参考代码及解析:C。
知识点:常量,Go 语言中的字符串是只读的。
20 下面代码输出什么?
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() 返回自增后的结果。
21 下面代码下划线处可以填入哪个选项?
package main
import "fmt"
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。
知识点:nil 切片和空切片。nil 切片和 nil 相等,一般用来表示一个不存在的切片;空切片和 nil 不相等,表示一个空的集合。
22 下面这段代码输出什么?
func main() {
i := 65
fmt.Println(string(i))
}
A. A
B. 65
C. compilation error
参考答案及解析:A。
UTF-8 编码中,十进制数字 65 对应的符号是 A。
23 下面这段代码输出什么?
package main
import "fmt"
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。
24 切片 a、b、c 的长度和容量分别是多少?
package main
import "fmt"
func main() {
s := [3]int{1, 2, 3}
a := s[:0]
fmt.Println(a) //[]
b := s[:2]
fmt.Println(b) // [1 2]
c := s[1:2:cap(s)]
fmt.Println(c) // [2]
}
25 下面代码中 A B 两处应该怎么修改才能顺利编译?
package main
import "fmt"
func main() {
var m map[string]int //A
m["a"] = 1
if v := m["b"]; v != nil { //B
fmt.Println(v)
}
}
参考答案及解析:
package main
import "fmt"
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。
26 下面代码中,x 已声明,y 没有声明,判断每条语句的对错?
1. x, _ := f()
2. x, _ = f()
3. x, y := f()
4. x, y = f()
参考答案及解析:错、对、对、错。
知识点:变量的声明。
1.错,x 已经声明,不能使用 :=
;
2.对;
3.对,当多值赋值时,:=
左边的变量无论声明与否都可以;
4.错,y 没有声明。
27 下面代码输出什么?
package main
import "fmt"
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(), "increaseA")
fmt.Println(increaseB(), "increaseB")
}
/*
PS E:\TEXT\test_go\one\test> go run .\main.go
0 increaseA
1 increaseB
PS E:\TEXT\test_go\one\test>
*/
A. 1 1
B. 0 1
C. 1 0
D. 0 0
参考答案及解析:B。
知识点:defer、返回值。
注意一下,increaseA() 的返回参数是匿名,increaseB() 是具名。
28 类型断言
package main
import "fmt"
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。
知识点:类型断言。
29 匿名返回
package main
import "fmt"
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
}
func main() {
fmt.Println(f1(), "f1")
fmt.Println(f2(), "f2")
fmt.Println(f3(), "f3")
}
PS E:\TEXT\test_go\one\test> go run .\main.go
1 f1
5 f2
1 f3
PS E:\TEXT\test_go\one\test>
相关文章
- Golang 方法
- 一个简单的Golang实现的HTTP Proxy
- golang语言并发与并行——goroutine和channel的详细理解
- Golang LicenseServer授权服务器的设计 与 RSA 密钥对的应用
- Golang Gin 框架 Route备注
- Golang每日一练(leetDay0040)
- Golang 022. 不定方程之一元换分币
- Golang 019. 斐波那契数列
- golang单元测试类示例代码:reflect.DeepEqual比较两个map的值是否相同
- Golang中支持可变参数
- [Golang]Go Packages
- golang channel本质——共享内存
- Golang 协程控制关闭
- Golang gRPC学习(04): Deadlines超时限制
- golang depth read map
- golang 数据类型之间的转换
- Golang包管理工具之govendor的使用
- golang日志输出
- Golang Struct 带标签的结构体