关于go的疑问

这必然是一路风景

1、go的时间time.sleep()其中的参数默认单位不是秒而是微秒 go中有专门的时间值time.Second/Minute/Hour...

2、channel的 make(chan int, arg)第二个参数定义channel buffer,默认是unbuffer,单线程使用的时候必须要定义,否则会被阻塞(unbuffer意思是只要我收到了,我就会阻塞在这个地方,等人来取,单线程就会直接报错了编不过,例子如下)https://www.jianshu.com/p/f12e1766c19f

func Ping(ping chan<- int, s int) {
	ping <- s
}

func Pong(ping <-chan int, pong chan<- int) {
	msg := <-ping
	pong <- msg
}

func ChannelDirect() {
	pi := make(chan int)          // 会报错当ping <- s的时候
	po := make(chan int)
	Ping(pi, 1)
	Pong(pi, po)
	fmt.Println(<-po)
}

3、fmt.printf("%T", arg) 判断arg的类型

4、channel synchronization 其实就是通过在goroutine里面放置channel的方式,保证goroutine中的代码运行完毕

func worker(done chan bool) {
    fmt.Print("working...")
    time.Sleep(time.Second)
    fmt.Println("done")

    done <- true
}

func main() {

    done := make(chan bool, 1)
    go worker(done)
    <-done                           // 这里阻塞直到goroutine运行完
}

5、goroutines leaking 永远不要在不知道它将如何停止的情况下启动一个goroutine,因为是并发的,所以会一直占用内存,其中一种情况就是channel没有设置buffer,则默认为阻塞的,如果channel阻塞且正好在goroutines中,则会造成此goroutine永远释放不了

6、数组和切片的区别:1.定义的时候数组必须定长度或者[...]让机器判定长度,切片则不用。多维数组只有第一层能使用[...]

7、数组传递的是值,是值类型,map和slice传递的是指针

8、切片是对底层数组的进一层封装,所以切片是由数组切来的时候,并不是复制了一份,而是直接的引用,改变数组的值会同时更改切片的值,切片的赋值也有相同效果,赋值的结果是复制了引用,底层数组是同一个

9、当切片append元素时,如果append后长度未超出cap长度,则依然适用上面的规则,如果append后超出了,则会复制一个新的底层数组重新指向,此时再更改上面的数组就不再影响slice

func ArraySlice() {
	new_array := [3]int{1, 3, 5}
	new_slice := new_array[:2]
	fmt.Println(new_slice)
	new_slice = append(new_slice, 9, 10)
	new_array[1] = 7
	fmt.Println(new_slice)
	fmt.Println(cap(new_slice))
}
stdout-->
[1 3]
[1 3 9 10]
6  // 原来是3

9、切片没有删除方法,可以append(slice[:index], slice[index+1:]...),删除掉index位置的元素就是从index+1开始append到index位置其实,注意第二个后面的三个点是go的语法糖,必须要加的,不然会直接append一个切片上去,其实就类似python的*args的*

10、切片是否为空必须要用len是否为0来判断,不能弄nil

11、new与make的区别:

  1. 二者都是用来做内存分配的。

  2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;

  3. 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针

  4. new和&在用于结构体时没什么区别,

    a := &simple_struct{}
    a := new(simple_struct)
    输出的都是*simple_struct
    

    但是对于基本类型有区别,例如不可能出现这种语法 a := &int() 只能是a:=new(int)

12、map是没有cap的,所以make(map, 111)第二个参数是没用的(目前暂时认为,不确定),map也是一个指向底层数据的指针,是引用类型

13、go的goroutine并发不是越多越好,因为协程是go的内部处理调度,绑定的是固定几个cpu的内核线程,所以当goroutine到达一定数量,cpu被一直满载占用,此时再创建协程运算效率就并不会有什么明显提升了,而go中协程的调度负荷反而会越来越重,有可能还会造成性能的下降(个人理解)

14、for index, stu := range stus {},这个时候index和stu是固定的内存空间,range的作用把stus解析后赋值给前面两个变量,前面两个变量的地址是不变的,所以在这里面尽量不要有关于前面两个变量的指针操作

15、go语言指针的使用需要声明后用new分配内存空间,struct的属性占用连续的内存空间,排列方法是属性内存和结构体内存的对齐,可以从这个方面优化大体积结构体

16、go语言的方法接收者用指针比较常见,struct是值类型,如果接收者是值类型则修改只在局部函数中生效,方法可以对任意本地类型生效,例如type MyInt int,那么给MyInt加一个方法完全可以

什么时候应该使用指针类型接收者
需要修改接收者中的值
接收者是拷贝代价比较大的大对象
保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。

补充:接收者如果是值类型,那么当对象是指针类型是可以直接使用的,因为go加了语法糖自动*obj取值了,反之接收者如果是指针类型,不能使用值类型对象。

17、go的继承通过结构体的嵌套实现,被嵌套的结构体是父类,结构体变量大写开头为公开,小写为私有(一定要注意,json.Marshal就不会读取私有的,如果是tag 小写是可以的例如`json:"name"`)

18、defer是延迟语句,执行在函数return值的时候(没有return其实就是默认return nil猜测,因为defer是可以正常执行的,但是我用goland无法获取无返回值的值会直接报错), defer延迟了语句执行,但是没有延迟传参操作,如果defer之后更改了defer函数的入参值,defer时更改是不会生效的,因为当传参时是进行了copy操作,{补充:函数传参是值拷贝形式}

func Test(A int ) {
d := &A
fmt.Printf("%p
", &A)
defer func(a int) {
	fmt.Printf("%p
", &a)
	fmt.Println(a)
}(A)
*d = 12
fmt.Println(A)
fmt.Println("in Test")
}

0xc00000a200
12
in Test
0xc00000a230  // 和上面指针已经不一样了,所以是复制了一份
11

19、go tool compile -S XXX.go 可以把go编译到汇编不生成binary

20、go语言错误处理机制panic相当于python的raise,recover相当于try/except,不过recover必须搭配defer使用并且位置在panic之前,因为panic是直接抛出异常如果在后面就直接报错了无法捕获(写到最开头不就完了)

21、interface{}虽然好用,最好少用

22、函数返回值中所用名字,此函数中不能再次初始化定义

func test() A string {
    A = "hello"
}
此时不能用 A:= 

23、判定类型时可以使用a.(类型),同时也是类型的强转,返回两个值,第一个值是转化后数据,第二个值是类型判定true还是false,或者用switch可以写成a.(type),只输出true或false,之后用case判定输出类型

24、go语言写入文件语句

w1, err = os.OpenFile(".\logtest.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644),最后一个是文件的permission,类比linux  777 755,第二个参数可以点进去看源码,对应写文件模式
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	// The remaining values may be or'ed in to control behavior.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
而w1.WriteString(msg.(string))输出两个值,第一个值是写入文件的字节数,第二个是err

25、如果方法带参数,接口也必须带参数

26、reflect.ValueOf(x).Elem()返回x指针值,作用是什么?这个reflect要单独开一章讲

27、unicode.IsLetter、unicode.IsDigit、unicode.IsSpace判断字符类型,传参int32一般需要转化一下

28、strings.ToLower strings.ToUpper转化大小写

29、go数组是值类型,可以通过赋值来copy,而切片是引用类型的,需要用copy方法,一维切片可以用浅copy,多维切片需要深copy,因为内部切片也是引用,浅copy复制的是引用不能完全复制。具体方法参照此连接

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 有任何问题请随时交流~ Email: araise1@163.com
原文地址:https://www.cnblogs.com/seasen/p/12981788.html