真正在工作中用 Go 的工夫不久,所以也作为老手,总结了一些常见的问题和坑
Go 中指针应用留神点
// 1. 空指针反向援用不非法
package main
func main() {
var p *int = nil
*p = 0
}
// in Windows: stops only with: <exit code="-1073741819" msg="process crashed"/>
// runtime error: invalid memory address or nil pointer dereference
// 2. 文字或者常量援用也不非法
const i = 5
ptr := &i //error: cannot take the address of i
ptr2 := &10 //error: cannot take the address of 10
Go 语言常见内置函数
sort
// sort 包
import "sort"
sort.Strings(keys)
close 用于管道通信,select 用于通信的 switch
type T int
func main() {c := make(chan T)
close(c)
}
// select 用法
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received", i1, "from c1\n")
case c2 <- i2:
fmt.Printf("sent", i2, "to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {fmt.Printf("received", i3, "from c3\n")
} else {fmt.Printf("c3 is closed\n")
}
default:
fmt.Printf("no communication\n")
}
len、cap
len 用于返回某个类型的长度或数量(字符串、数组、切片、map 和管道);
cap 是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
new、make
new 和 make 均是用于分配内存:new 用于值类型和用户定义的类型,如自定义构造,make 用于内置援用类型(切片、map 和管道)。
它们的用法就像是函数,然而将类型作为参数:new (type)、make (type)。new (T) 调配类型 T 的零值并返回其地址,也就是指向类型 T 的指针。
它也能够被用于根本类型:v := new(int)。make (T) 返回类型 T 的初始化之后的值,因而它比 new 进行更多的工作,new () 是一个函数,不要遗记它的括号
copy、append
用于复制和连贯切片
panic、recover
两者均用于错误处理机制
print、println
底层打印函数,在部署环境中倡议应用 fmt 包
complex、real、imag
操作复数,应用场景不多
Go 不反对函数重载
Go 语言不反对这项个性的次要起因是函数重载须要进行多余的类型匹配影响性能;没有重载意味着只是一个简略的函数调度。所以你须要给不同的函数应用不同的名字,咱们通常会依据函数的特色对函数进行命名
如果须要申明一个在内部定义的函数,你只须要给出函数名与函数签名,不须要给出函数体:
func flushICache(begin, end uintptr) // implemented externally
函数也能够以申明的形式被应用,作为一个函数类型,就像:
type binOp func(int, int) int
Go 的 map 遍历时候变量地址始终用的是同一个
最佳实际:读数据能够用 key,value,写数据用地址,如果要把地址赋值给另外的 map,那么须要用长期变量
kvMap := make(map[int]int)
kvMap[0] = 100
kvMap[1] = 101
kvMap[2] = 102
kvMap[3] = 103
for k, v := range kvMap {println(k, &k, v, &v)
}
// 0 0xc000049e50 100 0xc000049e48
// 1 0xc000049e50 101 0xc000049e48
// 2 0xc000049e50 102 0xc000049e48
// 3 0xc000049e50 103 0xc000049e48
Go 遍历的 key,value 是值,而不是地址
// Version A:
items := make([]map[int]int, 5)
for i:= range items {items[i] = make(map[int]int, 1)
items[i][1] = 2
}
fmt.Printf("Version A: Value of items: %v\n", items)
// Version B: NOT GOOD!
items2 := make([]map[int]int, 5)
for _, item := range items2 {item = make(map[int]int, 1) // item is only a copy of the slice element.
item[1] = 2 // This 'item' will be lost on the next iteration.
}
fmt.Printf("Version B: Value of items: %v\n", items2)
该当像 A 版本那样通过索引应用切片的 map 元素。在 B 版本中取得的项只是 map 值的一个拷贝而已,所以真正的 map 元素没有失去初始化
锁和 sync 包
并发编程在大部分语言外面都会有,用来解决多个线程对临界资源的拜访,经典的做法是一次只能让一个线程对共享变量进行操作。当变量被一个线程扭转时 (临界区),咱们为它上锁,直到这个线程执行实现并解锁后,其余线程能力拜访它,Go 语言的这种锁机制是通过 sync 包中的 Mutex 来实现拜访的,上面是一个例子,另外在 sync 包中还有一个读写锁,RWMutex,其写互斥的办法与 Mutex 一样,读互斥采纳上面的办法
mu sync.Mutex
func update(a int) {mu.Lock()
a = xxx
mu.Unlock()}
mu2 sync.RWMutex
mu2.RLock()
mu2.RUnlock()
吴邪,小三爷,混迹于后盾,大数据,人工智能畛域的小菜鸟。
更多请关注