今天说一下这个golang 基础

12.是否可以编译通过?如果通过,输出什么?

func main() {
	i := GetValue()

	switch i.(type) {
	case int:
		println("int")
	case string:
		println("string")
	case interface{}:
		println("interface")
	default:
		println("unknown")
	}

}

func GetValue() int {
	return 1
}

解析

考点:type

编译失败,因为type只能使用在interface

13.下面函数有什么问题?

func funcMui(x,y int)(sum int,error){
    return x+y,nil
}

解析

考点:函数返回值命名
在函数有多个返回值时,只要有一个返回值有指定命名,其他的也必须有命名。如果返回值有有多个返回值必须加上括号;如果只有一个返回值并且有命名也需要加上括号;此处函数第一个返回值有sum名称,第二个未命名,所以错误。

14.是否可以编译通过?如果通过,输出什么?

package main

func main() {

	println(DeferFunc1(1))
	println(DeferFunc2(1))
	println(DeferFunc3(1))
}

func DeferFunc1(i int) (t int) {
	t = i
	defer func() {
		t += 3
	}()
	return t
}

func DeferFunc2(i int) int {
	t := i
	defer func() {
		t += 3
	}()
	return t
}

func DeferFunc3(i int) (t int) {
	defer func() {
		t += i
	}()
	return 2
}

解析

考点:defer和函数返回值
需要明确一点是defer需要在函数结束前执行。函数返回值名字会在函数起始处被初始化为对应类型的零值并且作用域为整个函数 DeferFunc1有函数返回值t作用域为整个函数,在return之前defer会被执行,所以t会被修改,返回4; DeferFunc2函数中t的作用域为函数,返回1; DeferFunc3返回3

15.是否可以编译通过?如果通过,输出什么?

func main() {
	list := new([]int)
	list = append(list, 1)
	fmt.Println(list)
}

解析

考点:new
list:=make([]int,0)

16.是否可以编译通过?如果通过,输出什么?

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3}
	s2 := []int{4, 5}
	s1 = append(s1, s2)
	fmt.Println(s1)
}

解析

考点:append
append切片时候别漏了’…’

17.是否可以编译通过?如果通过,输出什么?

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")
	}
}

解析

考点:结构体比较
进行结构体比较时候,只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。

sn3:= struct {
    name string
    age  int
}{age:11,name:"qq"}

sn3与sn1就不是相同的结构体了,不能比较。还有一点需要注意的是结构体是相同的,但是结构体属性中有不可以比较的类型,如map,slice。如果该结构属性都是可以比较的,那么就可以使用“==”进行比较操作。

可以使用reflect.DeepEqual进行比较

if reflect.DeepEqual(sn1, sm) {
    fmt.Println("sn1 ==sm")
}else {
    fmt.Println("sn1 !=sm")
}

所以编译不通过:invalid operation: sm1 == sm2

18.是否可以编译通过?如果通过,输出什么?

func Foo(x interface{}) {
	if x == nil {
		fmt.Println("empty interface")
		return
	}
	fmt.Println("non-empty interface")
}
func main() {
	var x *int = nil
	Foo(x)
}

解析

考点:interface内部结构

non-empty interface

19.是否可以编译通过?如果通过,输出什么?

func GetValue(m map[int]string, id int) (string, bool) {
	if _, exist := m[id]; exist {
		return "存在数据", true
	}
	return nil, false
}
func main()  {
	intmap:=map[int]string{
		1:"a",
		2:"bb",
		3:"ccc",
	}

	v,err:=GetValue(intmap,3)
	fmt.Println(v,err)
}

解析

考点:函数返回值类型
nil 可以用作 interface、function、pointer、map、slice 和 channel 的“空值”。但是如果不特别指定的话,Go 语言不能识别类型,所以会报错。报:cannot use nil as type string in return argument.

20.是否可以编译通过?如果通过,输出什么?

const (
	x = iota
	y
	z = "zz"
	k
	p = iota
)

func main()  {
	fmt.Println(x,y,z,k,p)
}

解析

考点:iota
结果:

0 1 zz zz 4

21.编译执行下面代码会出现什么?

package main
var(
    size :=1024
    max_size = size*2
)
func main()  {
    println(size,max_size)
}

解析

考点:变量简短模式
变量简短模式限制:

  • 定义变量同时显式初始化
  • 不能提供数据类型
  • 只能在函数内部使用

结果:

syntax error: unexpected :=

22.下面函数有什么问题?

package main
const cl  = 100

var bl    = 123

func main()  {
    println(&bl,bl)
    println(&cl,cl)
}

解析

考点:常量
常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,

cannot take the address of cl

23.编译执行下面代码会出现什么?

package main

func main()  {

    for i:=0;i<10 ;i++  {
    loop:
        println(i)
    }
    goto loop
}

解析

考点:goto
goto不能跳转到其他函数或者内层代码

goto loop jumps into block starting at

24.编译执行下面代码会出现什么?

package main
import "fmt"

func main()  {
    type MyInt1 int
    type MyInt2 = int
    var i int =9
    var i1 MyInt1 = i
    var i2 MyInt2 = i
    fmt.Println(i1,i2)
}

解析

考点:**Go 1.9 新特性 Type Alias **
基于一个类型创建一个新类型,称之为defintion;基于一个类型创建一个别名,称之为alias。MyInt1为称之为defintion,虽然底层类型为int类型,但是不能直接赋值,需要强转;MyInt2称之为alias,可以直接赋值。

结果:

cannot use i (type int) as type MyInt1 in assignment

25.编译执行下面代码会出现什么?

package main
import "fmt"

type User struct {
}
type MyUser1 User
type MyUser2 = User
func (i MyUser1) m1(){
    fmt.Println("MyUser1.m1")
}
func (i User) m2(){
    fmt.Println("User.m2")
}

func main() {
    var i1 MyUser1
    var i2 MyUser2
    i1.m1()
    i2.m2()
}

解析

考点:**Go 1.9 新特性 Type Alias **
因为MyUser2完全等价于User,所以具有其所有的方法,并且其中一个新增了方法,另外一个也会有。但是

i1.m2()

是不能执行的,因为MyUser1没有定义该方法。结果:

MyUser1.m1
User.m2

26.编译执行下面代码会出现什么?

package main

import "fmt"

type T1 struct {
}
func (t T1) m1(){
    fmt.Println("T1.m1")
}
type T2 = T1
type MyStruct struct {
    T1
    T2
}
func main() {
    my:=MyStruct{}
    my.m1()
}

解析

考点:**Go 1.9 新特性 Type Alias **
是不能正常编译的,异常:

ambiguous selector my.m1

结果不限于方法,字段也也一样;也不限于type alias,type defintion也是一样的,只要有重复的方法、字段,就会有这种提示,因为不知道该选择哪个。改为:

my.T1.m1()
my.T2.m1()

type alias的定义,本质上是一样的类型,只是起了一个别名,源类型怎么用,别名类型也怎么用,保留源类型的所有方法、字段等。

27.编译执行下面代码会出现什么?

package main

import (
    "errors"
    "fmt"
)

var ErrDidNotWork = errors.New("did not work")

func DoTheThing(reallyDoIt bool) (err error) {
    if reallyDoIt {
        result, err := tryTheThing()
        if err != nil || result != "it worked" {
            err = ErrDidNotWork
        }
    }
    return err
}

func tryTheThing() (string,error)  {
    return "",ErrDidNotWork
}

func main() {
    fmt.Println(DoTheThing(true))
    fmt.Println(DoTheThing(false))
}

解析

考点:变量作用域
因为 if 语句块内的 err 变量会遮罩函数作用域内的 err 变量,结果:

<nil>
<nil>

改为:

func DoTheThing(reallyDoIt bool) (err error) {
    var result string
    if reallyDoIt {
        result, err = tryTheThing()
        if err != nil || result != "it worked" {
            err = ErrDidNotWork
        }
    }
    return err
}

28.编译执行下面代码会出现什么?

package main

func test() []func()  {
    var funs []func()
    for i:=0;i<2 ;i++  {
        funs = append(funs, func() {
            println(&i,i)
        })
    }
    return funs
}

func main(){
    funs:=test()
    for _,f:=range funs{
        f()
    }
}

解析

考点:闭包延迟求值
for循环复用局部变量i,每一次放入匿名函数的应用都是想一个变量。结果:

0xc042046000 2
0xc042046000 2

如果想不一样可以改为:

func test() []func()  {
    var funs []func()
    for i:=0;i<2 ;i++  {
        x:=i
        funs = append(funs, func() {
            println(&x,x)
        })
    }
    return funs
}

29.编译执行下面代码会出现什么?

package main

func test(x int) (func(),func())  {
    return func() {
        println(x)
        x+=10
    }, func() {
        println(x)
    }
}

func main()  {
    a,b:=test(100)
    a()
    b()
}

解析

考点:闭包引用相同变量*
结果:

100
110

30.编译执行下面代码会出现什么?

package main

import (
    "fmt"
    "reflect"
)

func main1()  {
    defer func() {
       if err:=recover();err!=nil{
           fmt.Println(err)
       }else {
           fmt.Println("fatal")
       }
    }()

    defer func() {
        panic("defer panic")
    }()
    panic("panic")
}

func main()  {
    defer func() {
        if err:=recover();err!=nil{
            fmt.Println("++++")
            f:=err.(func()string)
            fmt.Println(err,f(),reflect.TypeOf(err).Kind().String())
        }else {
            fmt.Println("fatal")
        }
    }()

    defer func() {
        panic(func() string {
            return  "defer panic"
        })
    }()
    panic("panic")
}

解析

考点:panic仅有最后一个可以被revover捕获
触发panic("panic")后顺序执行defer,但是defer中还有一个panic,所以覆盖了之前的panic("panic")

defer panic

31. 算法

在utf8字符串判断是否包含指定字符串,并返回下标。 “北京天安门最美丽” , “天安门” 结果:2

解答:

import (
	"fmt"
	"strings"
)

func main(){
	fmt.Println(Utf8Index("北京天安门最美丽", "天安门"))
	fmt.Println(strings.Index("北京天安门最美丽", "男"))
	fmt.Println(strings.Index("", "男"))
	fmt.Println(Utf8Index("12ws北京天安门最美丽", "天安门"))
}

func Utf8Index(str, substr string) int {
	asciiPos := strings.Index(str, substr)
	if asciiPos == -1 || asciiPos == 0 {
		return asciiPos
	}
	pos := 0
	totalSize := 0
	reader := strings.NewReader(str)
	for _, size, err := reader.ReadRune(); err == nil; _, size, err = reader.ReadRune() {
		totalSize += size
		pos++
		// 匹配到
		if totalSize == asciiPos {
			return pos
		}
	}
	return pos
}

32,编程

实现一个单例

解答:

package main

import "sync"

// 实现一个单例

type singleton struct{}

var ins *singleton
var mu sync.Mutex

//懒汉加锁:虽然解决并发的问题,但每次加锁是要付出代价的
func GetIns() *singleton {
	mu.Lock()
	defer mu.Unlock()

	if ins == nil {
		ins = &singleton{}
	}
	return ins
}

//双重锁:避免了每次加锁,提高代码效率
func GetIns1() *singleton {
	if ins == nil {
		mu.Lock()
		defer mu.Unlock()
		if ins == nil {
			ins = &singleton{}
		}
	}
	return ins
}

//sync.Once实现
var once sync.Once

func GetIns2() *singleton {
	once.Do(func() {
		ins = &singleton{}
	})
	return ins
}

33,执行下面的代码发生什么?

package main

import (
	"fmt"
	"time"
)

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 * 100)
}

考点:channel

往已经关闭的channel写入数据会panic的。 结果:

panic: send on closed channel

34,执行下面的代码发生什么?

import "fmt"

type ConfigOne struct {
	Daemon string
}

func (c *ConfigOne) String() string {
	return fmt.Sprintf("print: %v", p)
}

func main() {
	c := &ConfigOne{}
	c.String()
}

考点:fmt.Sprintf

如果类型实现String(),%v和%v格式将使用String()的值。因此,对该类型的String()函数内的类型使用%v会导致无限递归。 编译报错:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

35,编程题

反转整数 反转一个整数,例如:

例子1: x = 123, return 321
例子2: x = -123, return -321

输入的整数要求是一个 32bit 有符号数,如果反转后溢出,则输出 0

func reverse(x int) (num int) {
	for x != 0 {
		num = num*10 + x%10
		x = x / 10
	}
	// 使用 math 包中定义好的最大最小值
	if num > math.MaxInt32 || num < math.MinInt32 {
		return 0
	}
	return
}

36,编程题

合并重叠区间 给定一组 区间,合并所有重叠的 区间。

例如: 给定:[1,3],[2,6],[8,10],[15,18] 返回:[1,6],[8,10],[15,18]

type Interval struct {
	Start int
	End   int
}

func merge(intervals []Interval) []Interval {
	if len(intervals) <= 1 {
		return intervals
	}

	sort.Slice(intervals, func(i, j int) bool {
		return intervals[i].Start < intervals[j].Start
	})

	res := make([]Interval, 0)
	swap := Interval{}
	for k, v := range intervals {
		if k == 0 {
			swap = v
			continue
		}
		if v.Start <= swap.End {
			swap.End = v.End
		} else {
			res = append(res, swap)
			swap = v
		}
	}
	res = append(res, swap)
	return res
}

37.输出什么?

package main

import (
	"fmt"
)

func main() {
	fmt.Println(len("你好bj!"))
}

考点:编码长度

输出9

38.编译并运行如下代码会发生什么?

package main

import "fmt"

type Test struct {
	Name string
}

var list map[string]Test

func main() {

	list = make(map[string]Test)
	name := Test{"xiaoming"}
	list["name"] = name
	list["name"].Name = "Hello"
	fmt.Println(list["name"])
}

考点:map

编程报错cannot assign to struct field list["name"].Name in map。 因为list[“name”]不是一个普通的指针值,map的value本身是不可寻址的,因为map中的值会在内存中移动,并且旧的指针地址在map改变时会变得无效。 定义的是var list map[string]Test,注意哦Test不是指针,而且map我们都知道是可以自动扩容的,那么原来的存储name的Test可能在地址A,但是如果map扩容了地址A就不是原来的Test了,所以go就不允许我们写数据。你改为var list map[string]*Test试试看。

39.ABCD中哪一行存在错误?

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

}

考点:interface

看到这道题需要第一时间想到的是Golang是强类型语言,interface是所有golang类型的父类,类似Java的Object。 函数中func f(x interface{})interface{}可以支持传入golang的任何类型,包括指针,但是函数func g(x *interface{})只能接受*interface{}.

40.编译并运行如下代码会发生什么?

package main

import (
	"sync"
	//"time"
)

const N = 10

var wg = &sync.WaitGroup{}

func main() {

	for i := 0; i < N; i++ {
		go func(i int) {
			wg.Add(1)
			println(i)
			defer wg.Done()
		}(i)
	}
	wg.Wait()

}

考点:WaitGroup

这是使用WaitGroup经常犯下的错误!请各位同学多次运行就会发现输出都会不同甚至又出现报错的问题。 这是因为go执行太快了,导致wg.Add(1)还没有执行main函数就执行完毕了。 改为如下试试

for i := 0; i < N; i++ {
        wg.Add(1)
		go func(i int) {
			println(i)
			defer wg.Done()
		}(i)
	}
	wg.Wait()

41.执行下面的代码发生什么?

package main

type Param map[string]interface{}

type Show struct {
	*Param
}

func main() {
	s := new(Show)
	s.Param["RMB"] = 10000
}

考点:map初始化

map需要初始化后才能使用。
编译错误:invalid operation: s.Param[“RMB”] (type *Param does not support indexing)

42.执行下面的代码发生什么?

package main
import "fmt"

type student struct {
	Name string
}

func zhoujielun(v interface{}) {
	switch msg := v.(type) {
	case *student, student:
		msg.Name = "qq"
		fmt.Print(msg)
	}
}

考点:类型转换

msg不属于student类型,所以没有Name字段。
改为:

s := v.(student)
s.Name = "qq"

43.执行下面的代码发生什么?

package main
import (
	"encoding/json"
	"fmt"
)

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)
}

考点:结构体访问控制

这道题坑很大,很多同学一看就以为是p的初始化问题,实际上是因为name首字母是小写,导致其他包不能访问,所以输出为空结构体。
改为:

type People struct {
	Name string `json:"name"`
}

44.以下代码有什么问题?

package main

func Stop(stop <-chan bool) {
	close(stop)
}

考点:close channel

有方向的channel不可被关闭

45.实现一个函数可以根据指定的size切割切片为多个小切片

解析

func main() {
	lenth := 11
	size := 5
	list := make([]int, 0, lenth)
	for i := 0; i < lenth; i++ {
		list = append(list, i)
	}
	SpiltList(list, size)
}

func SpiltList(list []int, size int) {
	lens := len(list)
	mod := math.Ceil(float64(lens) / float64(size))
	spliltList := make([][]int, 0)
	for i := 0; i < int(mod); i++ {
		tmpList := make([]int, 0, size)
		fmt.Println("i=", i)
		if i == int(mod)-1 {
			tmpList = list[i*size:]
		} else {
			tmpList = list[i*size : i*size+size]
		}
		spliltList = append(spliltList, tmpList)
	}
	for i, sp := range spliltList {
		fmt.Println(i, " ==> ", sp)
	}
}

46.实现两个go轮流输出:A1B2C3…..Z26

解析

方法一:有缓冲chan

func ChannelFunc() {
	zimu := make(chan int, 1)
	suzi := make(chan int, 1)
	zimu <- 0
	// zimu
	go func() {
		for i := 65; i <= 90; i++ {
			<-zimu
			fmt.Printf("%v", string(rune(i)))
			suzi <- i
		}
		return
	}()

	go func() {
		for i := 1; i <= 26; i++ {
			<-suzi
			fmt.Printf("%v", i)
			zimu <- i
		}
		return
	}()

	time.Sleep(1 * time.Second)
	fmt.Println()
}

方法二:无缓冲chan

func Channel1Func() {
	zimu := make(chan int)
	suzi := make(chan int)

	// zimu
	go func() {
		for i := 65; i <= 90; i++ {
			fmt.Printf("%v", string(rune(i)))
			zimu <- i
			<-suzi
		}
		return
	}()

	go func() {
		for i := 1; i <= 26; i++ {
			<-zimu
			fmt.Printf("%v", i)
			suzi <- i
		}
		return
	}()

	time.Sleep(10 * time.Second)
	fmt.Println()
}

方法三:使用锁

大家可以自己实现,把结果留言给我,答案后续公布。

47.执行下面代码输出什么?

package main

// 47.执行下面代码输出什么?
import "fmt"

func main() {
	five := []string{"Annie", "Betty", "Charley", "Doug", "Edward"}

	for _, v := range five {
		five = five[:2]
		fmt.Printf("v[%s]\n", v)
	}
}

考点:range副本机制

循环内的切片值会缩减为2,但循环将在切片值的自身副本上进行操作。 这允许循环使用原始长度进行迭代而没有任何问题,因为后备数组仍然是完整的。
结果:

v[Annie]
v[Betty]
v[Charley]
v[Doug]
v[Edward]

48.for 和 for range有什么区别?

考点:for range

  1. 使用场景不同 for可以
    • 遍历array和slice
    • 遍历key为整型递增的map
    • 遍历string for range可以完成所有for可以做的事情,却能做到for不能做的,包括
    • 遍历key为string类型的map并同时获取key和value
    • 遍历channel
  2. 实现不同 for可以获取到的是被循环对象的元素本身,可以对其进行修改; for range使用值拷贝的方式代替被遍历的元素本身,是一个值拷贝,而不是元素本身。

49.解决下面问题:输出MutilParam= [ssss [1 2 3 4]]如何做到输出为[ssss 1 2 3 4]?

package main

import "fmt"

func MutilParam(p ...interface{}) {
	fmt.Println("MutilParam=", p)
}
func main() {
	MutilParam("ssss", 1, 2, 3, 4) //[ssss 1 2 3 4]
	iis := []int{1, 2, 3, 4}
	MutilParam("ssss", iis) //输出MutilParam= [ssss [1 2 3 4]]如何做到输出为[ssss 1 2 3 4]
}

考点:函数变参

这样的情况会在开源类库如xorm升级版本后出现Exce函数不兼容的问题。 解决方式有两个:

方法一:interface[]

tmpParams := make([]interface{}, 0, len(iis)+1)
tmpParams = append(tmpParams, "ssss")
for _, ii := range iis {
    tmpParams = append(tmpParams, ii)
}
MutilParam(tmpParams...)

方法二:反射

f := MutilParam
value := reflect.ValueOf(f)
pps := make([]reflect.Value, 0, len(iis)+1)
pps = append(pps, reflect.ValueOf("ssss"))
for _, ii := range iis {
    pps = append(pps, reflect.ValueOf(ii))
}
value.Call(pps)

50.编译并运行如下代码会发生什么?

package main

// 50.编译并运行如下代码会发生什么?
import "fmt"

func main() {
	mmap := make(map[map[string]string]int, 0)
	mmap[map[string]string{"a": "a"}] = 1
	mmap[map[string]string{"b": "b"}] = 1
	mmap[map[string]string{"c": "c"}] = 1
	fmt.Println(mmap)
}

考点:map key类型

golang中的map,的 key 可以是很多种类型,比如 bool, 数字,string, 指针, channel , 还有 只包含前面几个类型的 interface types, structs, arrays。
显然,slice, map 还有 function 是不可以了,因为这几个没法用 == 来判断,即不可比较类型。 可以将map[map[string]string]int改为map[struct]int

正文完