go笔记

值交换
var a int = 100
var b int= 200
a, b = b, a
指针式值交换
func swap(x *int, y *int){
    *x, *y = *y, *x
}


三重指针及其对应关系: pt3 - > pto - > ptr - >变量a
var a int = 1
var ptr1 *int = &a
var ptr2 **int = &ptr1
var ptr3 **(*int) = &ptr2

传参默认:
非引用类型:int、string、struct、array
引用类型:指针、map、slice、channel

结构体
type name struct {
   member definition
   ...
}
variable_name := structure_variable_type {value1, value2...valuen}
or
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}

struct tag, 反射/ast用的定义内容:
// 成员末尾引号里面的就是tag
type user struct{
     Param string `default:xxx`
     Name string "user name"
}
user := &User{"chronos", "pass"}
// 通过反射获取default定义
s := reflect.TypeOf(user).Elem()
// 将tag输出出来
for i := 0; i < s.NumField(); i++ {
    fmt.Println(s.Field(i).Tag)
}
// ---------------------------- Go的selector的疑惑 ----------------------------
type A struct {
    r int
}

func (a A) FuncA() {}

type B A

type C *A

func main() {
    var b B
    var a A
    var c C
    a.FuncA()   //合法
    b.FuncA()   //非法
    c.FuncA()   //非法
    (*c).FuncA()    //合法
    fmt.Println(b.r)    //合法
    fmt.Println(c.r)    //合法
}
type B A 定义的是一个新类型,但是 B 和 A 的底层类型是一样的(这部分参考圣经)
    即它们都是拥有 r 这个成员的结构体
    因此 b.r 和 c.r 合法
b 和 c 无法调用 FuncA() 是因为它们都是新类型并且没有绑定该方法
    c是指向A的指针, (*c) == A, 等同a.FuncA()

// ---------------- 限定标识符
foo.bar // if foo is a package, error, only foo.Bar
// ---------------- 选择器表达式
foo.bar // if foo is local type struct, ok
// ---------------- 选择器深度
type E struct {
    name string
}
func (e E) SayHi() {}
type T struct {
    age byte
    E
}
type A struct {
    age byte
}
t := T{30, E{"Michał"}}
fmt.Println(t.age)    // age 字段深度是 0
fmt.Println(t.name, t.SayHi())    //  E 嵌套在 T 的定义中, name 或者 SayHi 的深度是 1
type C struct {
    gae byte
    T
    A
}
v := C{"c", B{"b", A{"a"}}}
fmt.Println(v.name)    // 2层嵌套, name深度2
// ---------------- 最浅深度选择
如果 C 也有name, v.name默认选C.name, 否则需指定v.T.name
// ---------------- 唯一性
v.age error, 同层(T  A)都有age, 需v.T.age / v.A.age
// ---------------- 空
如果v.T == nil, v.T.age panic, 蹦
// ---------------- 接口
type I interface {
    m()
}
var foo I
foo = E{}
foo.SayHi() // foo 的动态值的一个方法
// ---------------- 命名指针类型
type T struct {
    num int
}
func (t T) m() {}
type P *T
func main() {
    var p P = &T{num: 10}
    fmt.Println(p.num)
    // p.m() // compile-time error: p.m undefined (type P has no field or method m)
    (*p).m()
}
// ---------------------------- Go的selector的疑惑 ----------------------------
结构体指针
var struct_pointer *Books
struct_pointer = &Book1

struct{}
    是一个无元素的结构体类型,通常在没有信息存储时使用。
    优点是大小为0,不需要内存来存储struct{}类型的值。
struct{}{}
    是一个复合字面量,它构造了一个struct{}类型的值,该值也是空。
    两个structt{}{}地址相等:
        a := structt{}{}
        b := structt{}{}
        &a == &b

1 做set,map[string]struct{}
    由于struct{}是空,不关心内容,这样map便改造为set
2 chan struct{}
    可以做退出

字面量不可寻址
func (p *A) FindBug() {...}
func Create() A {return A{}}
    Create().FindBug()    // error, Create return 字面量A{}, can not do &A
    c := Create(); c.FindBug()
============================================================================================================
定义切片
var identifier []type
slice1 := make([]type, len)
切片初始化/截取
s :=[] int {1,2,3}
s := arr[:]
s := arr[startIndex:]
s := arr[:endIndex]
s := arr[startIndex:endIndex]
s = append(s, 0)
**注意,效率坑**, append<cap, 连续内存添加
>cap时, 切片会整个复制到新内存扩容再添加
// 切片参数传递是引用, 但只有指针传递才变原
b = make([]*st, 0) // 原
test(b)
func test(b *[]*st, v *st) {
    *b = append(*v, st)
}
// 因为, append返回新地址(大部分情况), 而引用本身是个地址复制,
//        改变不影响原
// 于是, 只能改变原指针指向append返回来改变原
============================================================================================================

数组坑
// a, b共享底层数组, 改b动a, 除非b通过append重新分配内存
a := []int{1,2,3...}
b := a[0:1]
// 操作数值指针
func test(b *[]byte) {
    fmt.Println((*b)[0:3]) // 不能*b, 要(*b)
}
字面量不可寻址
arr1 := [3]int{1, 2, 3}[1:] // error 字面量数组寻址是不合法的

arr1 := [3]int{1, 2, 3}
arr1 = arr1[1:]    // error 把一个数组转换成一个 slice
============================================================================================================
range 关键字
for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素
在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对
============================================================================================================

集合(map as set)
var countryCapitalMap map[string]string
countryCapitalMap = make(map[string]string)
countryCapitalMap [ "France" ] = "巴黎"
delete(countryCapitalMap, "France")
============================================================================================================

递归
func recursion() {
    recursion() /* 函数调用自身 */
}
============================================================================================================
类型转换
type_name(expression)
val.(type_name) // val is interface

============================================================================================================
接口
type interface_name interface {
    method_name1 [return_type]
    ...
}
interface中只存在方法,而方法的形参就来自于其实现类型
其他类型如果实现了interface中的所有方法,就说类型实现了该interface
实现接口方法
func (struct_name BookA) method_name1() [return_type] {}
func (struct_name BookB) method_name1() [return_type] {}
interface{} 的值不是任意类型,而是 interface{} 类型
转化
names := []string{"hello", "world"}
var interfaceSlice []interface{} = make([]interface{}, len(names))
for i,d := range names {
    interfaceSlice[i] = d
}

iface 和 eface 都是 Go 中描述接口的底层结构体
区别在于 iface 描述的接口包含方法
而 eface 则是不包含任何方法的空接口:interface{}

iface 内部维护两个指针:
tab 指向一个 itab 实体, 它表示接口的类型以及赋给这个接口的实体类型(动态类型)
data 则指向接口具体的值,一般而言是一个指向堆内存的指针。(动态值)

使用 interface 时不需要显式在 struct 上声明要实现哪个 interface
只需要实现对应 interface 中的方法即可
go 会自动进行 interface 的检查,并在运行时执行从其他类型到 interface 的自动转换
即使实现了多个 interface,go 也会在使用对应 interface 时实现自动转换。

编译器会由此检查 *myWriter 类型是否实现了 io.Writer 接口
var _ io.Writer = (*myWriter)(nil)
============================================================================================================


开启 goroutine
go 函数名( 参数列表 )
同一个程序中的所有 goroutine 共享同一个地址空间

设置缓冲区
ch := make(chan int, 100)
带缓冲区:
    通道允许发送端的数据发送和接收端的数据获取处于异步状态
    发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据
    缓冲区一满,数据发送端无法再发送数据
通道不带缓冲:
    发送方会阻塞直到接收方从通道中接收了值

range 关键字来实现遍历读取到的数据
v, ok := <-ch
for i := range c
============================================================================================================
数据竞争:
当两个线程(goroutine)并发地访问同一个变量,
并且其中至少一个访问是写操作时。
检测:
go run -race

context
它主要的用处,是在于控制goroutine的生命周期。
当一个计算任务被goroutine承接了之后,由于某种原因(超时,或者强制退出)
我们希望中止这个goroutine的计算任务,那么就用得到这个Context了

gRPC 的 Metadata(md) 简单理解,就是 Http 的 Header 中的 key-value 对
    在 metadata 中,key 永远是 string 类型
    但是 value 可以是 string 也可以是二进制数据。为了在 metadata 中存储二进制数据,我们仅仅需要在 key 的后面加上一个 - bin 后缀。
    具有 - bin 后缀的 key 所对应的 value 在创建 metadata时会被编码(base64)
    收到的时候会被解码
    md := metadata.Pairs(
        "key", "string value",
        "key-bin", string([]byte{96, 102}),
    )
一般用来做包验证等
发 pack.SetHeader(ctx, md)/收 packGetHearder()
可以在读pack.Data()之前, 先用packGetHearder()校验
------------------------------------- init() -------------------------------------
init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高
包被导入多次,初始化只需要一次
按照包的依赖关系顺序执行(不允许引用不使用的包)
    初始化导入的包(递归导入)
    对包块中声明的变量进行计算和分配初始值
    执行包中的init函数
init没有被声明,因此也不能被引用
如果为了调用init函数去做一些初始化: import _ "xxxx"
------------------------------------- unsafe -------------------------------------
    unsafe 包中的几个函数都是在编译期间执行完毕
    任何类型的指针都可以被转化为Pointer
    Pointer可以被转化为任何类型的指针
    uintptr可以被转化为Pointer
    Pointer可以被转化为uintptr
    限制:
        不能进行数学运算
        不同类型的指针不能使用 == 或 != 比较
        不同类型的指针变量不能相互赋值
        指向不同类型数据的指针,无法直接相互转换
    必须借助unsafe.Pointer(类似于C的 void指针)代理一下再转换
        f float64
        // 先把*float64 转成 Pointer(描述1)
        // 再把Pointer转成*uint64(描述2)
        return *(*uint64)(unsafe.Pointer(&f))
    不要试图引入一个uintptr类型的临时变量,可能会破坏代码的安全性
------------------------------------- env 环境编辑 -------------------------------------
看关键
go env
go env | grep GOPROXY
修改宏
go env -w GOPROXY="http://172.16.10.69:9090"
如冲突报错, 先
unset GOPROXY
------------------------------------- sample -------------------------------------
// 闭包f, 对函数fa和形参a的'同名引用'
func fa(a int) func(i int) int {
    return func(i int) int {
        println(&a, a)
        a += i
        return a
    }
}
f := fa(1) // f = func(1 int) int, a=1, 实际上同时引用了f(i)和a
f(1)    // 地址a, 1 \n return 2 // a + i=2
f(2)    // 地址a, 2 \n return 4 // a + i=4

v, ok := map[key]
if !ok {map不存在key}

v, ok := <-chan
if !ok {通道已关闭}

if _, ok := body.(type2); !ok {assert类型转换失败,不可转换}

Go 中引入错误的处理方式为:defer, panic, recover
    ...
    defer func() {
        err := recover()
        if nil != err {
            print(err)
            // mail通知开发者出事了or else
        }
    }
    // code down or panic("xxx")
------------------------------------- test -------------------------------------
package main

import "fmt"
import "strconv"

//import "sort"
type St0 struct {
    Aaa uint32
}
type St1 struct {
    Bbb uint32
    Ccc *St0
}
func fff(l *[]*St0) {
    *l = append(*l, &St0{Aaa:33})
    *l = append(*l, &St0{Aaa:44})
}
func stradd(str1 *string) {
    fmt.Println("Hello, Wstr! ", *str1)
}
func btr(bar []byte) {
    fmt.Println("Hello, Wstr! ", bar)
}
var mp map[uint32]*St1
func main() {
    mp = make(map[uint32]*St1)
    pc := mp[666]
    if nil == pc {
        pc = &St1{Ccc : &St0{}}
    }
    pc.Bbb = 233
    pc.Ccc.Aaa = 999
    mp[666] = pc
    fmt.Println("Hello, World! ", pc)                        // Hello, World!  &{233 0xc00008601c}
    fmt.Println("Hello, Wfuck! ", mp)                        // Hello, Wfuck!  map[666:0xc0000661e0]
    fmt.Println("Hello, Wshit! ", ("1" == strconv.Itoa(1)))    // Hello, Wshit!  true
    l := make([]*St0, 0)
    fff(&l)
    fmt.Println("Hello, Wlist! ", l)                        // Hello, Wlist!  [0xc000086038 0xc00008603c]
    var dm map[string]interface{}
    fmt.Println("Hello, Wmap! ", nil == dm["f"])            // Hello, Wmap!  true
    str1 := "head"
    stradd(&str1)                                            // Hello, Wstr!  head
    var btr1 []byte = []byte("ccc")
    btr2 := make([]byte, 0)
    btr(btr1)                                                // Hello, Wstr!  [99 99 99]
    btr(btr2)                                                // Hello, Wstr!  []
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值