语法

数据类型

类型转换

数据结构

程序控制

for/break/continue

switch/case/default

fallthrough

select/case/default

函数调用

“面向对象”

进阶语法

goto

select

  1. 除 default 外,如果只有一个 case 语句评估通过,那么就执行这个case里的语句;
  2. 除 default 外,如果有多个 case 语句评估通过,那么通过伪随机的方式随机选一个;
  3. 如果 default 外的 case 语句都没有通过评估,那么执行 default 里的语句;
  4. 如果没有 default,那么代码块会被阻塞,直到有一个 case 通过评估;否则一直阻塞

itoa

参考 https://studygolang.com/articles/22498

slice 切片

defer

defer 的语句在 return 之后执行

panic

recover

internal package

Type assertions

package main

import "fmt"

func main() {
	var i interface{} = "hello"

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f, ok := i.(float64)
	fmt.Println(f, ok)

	f = i.(float64) // panic
	fmt.Println(f)
}

channel

Go提供了range关键字,将其使用在channel上时,会自动等待channel的动作一直到channel被关闭,如下:

ticker := time.NewTicker(time.Minute * 5)
for range ticker.C {
	doSomeThing()
}

如下例子,ch 初始化以后,取出的值默认是 false。

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
ch := make(chan bool)
go func() {
  defer close(ch)
  //具体的任务,这里模拟做的任务需要1秒完成
  time.Sleep(time.Second * 1)
}()
select {
  case <- ch:
  fmt.Println("ch bool", <- ch)
  case <- ctx.Done():
  fmt.Println("ctx done")
}
// output:
// ch bool false

带缓冲区和不带缓冲区的区

c1 := make(chan int)  // 不带缓冲区
c2 := make(chan int, 100)  // 带缓冲区
  • 非缓冲 channelchannel 发送和接收动作是同时发生的
  • 例如 ch := make(chan int) ,如果没 goroutine 读取接收者<-ch ,那么发送者ch<- 就会一直阻塞
  • 缓冲 channel 类似一个队列,只有队列满了才可能发送阻塞

特殊注释

go:linkname

//go:linkname 注释标签引导编译器在编译时将当前私有函数链接到指定的目标函数,也可以作用到变量上面

举例:

time.Sleep() 声明在 time 包,但是实现在 runtime 包的 runtime.timeSleep()

>>>>>>>>>> time/sleep.go <<<<<<<<<<<<<<
package time

// Sleep pauses the current goroutine for at least the duration d.
// A negative or zero duration causes Sleep to return immediately.
func Sleep(d Duration)

>>>>>>>> runtime/time.go <<<<<<<<<<<<<

// timeSleep puts the current goroutine to sleep for at least ns nanoseconds.
//go:linkname timeSleep time.Sleep
func timeSleep(ns int64) {
	if ns <= 0 {
		return
	}

	gp := getg()
	t := gp.timer
	if t == nil {
		t = new(timer)
		gp.timer = t
	}
	t.f = goroutineReady
	t.arg = gp
	t.nextwhen = nanotime() + ns
	gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1)
}

自己实现一遍,文件目录如下:

├── main.go
├── linked
│   ├── linked.s
│   └── linked.go
├── inner
│   └── inner.go

inner/inner.go

package inner

import (
	_ "unsafe"
)

//go:linkname assert linked.Assert
func assert(i interface{}) {
	value, ok := i.(string)
	if ok == false {
		println("false", value)
	} else {
		fmt.Println("true", value)
	}
}

linked/linked.go

package linked

import (
	_ "inner"
)

func Assert(s string)

main.go

func main() {
		linked.Assert("test")
}

// output:
// false

go:noescape

该指令指定下一个有声明但没有主体(意味着实现有可能不是 Go)的函数,不允许编译器对其做逃逸分析。

一般情况下,该指令用于内存分配优化。因为编译器默认会进行逃逸分析,会通过规则判定一个变量是分配到堆上还是栈上。但凡事有意外,一些函数虽然逃逸分析其是存放到堆上。但是对于我们来说,它是特别的。我们就可以使用 go:noescape 指令强制要求编译器将其分配到函数栈上。

named return value

func f() (i int, s string) {
    i = 17
    s = "abc"
    return // same as return i, s
}

https://tour.golang.org/basics/7

https://yourbasic.org/golang/named-return-values-parameters/

数学计算

位移计算

CGo

举例

package rand

/*
#include <stdlib.h>
*/
import "C"

func Random() int {
    return int(C.random())
}

func Seed(i int) {
    C.srandom(C.uint(i))
}
comments powered by Disqus