迷惑了,Go len() 是怎么计算出来的? |
您所在的位置:网站首页 › 直播上的链接是怎么获取的呢 › 迷惑了,Go len() 是怎么计算出来的? |
[[416772]] 本文转载自微信公众号「脑子进煎鱼了」,作者陈煎鱼。转载本文请联系脑子进煎鱼了公众号。 大家好,我是煎鱼。 最近看到了一个很有意思的话题,我们平时常常会用 Go 的内置函数 len 去获取各种 map、slice 的长度,那他是怎么实现的呢? 正当我想去看看 len 的具体实现时,一展身手,却发现竟然是个空方法: func len(v Type) int看注解也没有 link 到其他 runtime 函数,那么 len 函数是如何被调用的呢? 先前也做了一些笔记,在此分享给大家,共同进步。 谜底今天就由煎鱼带大家一同解开这个谜底。既然是谜底,那就一开始就揭开。 其实 Go 语言中并没有 len 函数的具体实现代码,他其实是 Go 编译器的 "魔法" ,不是实际的函数调用。 接下来将展开这部分,我们可以更深入地了解 Go 编译器的内部工作原理。 编译器在 Go 编译器编译时会解析命令行参数中指定的标志和 Go 源文件,对解析后的 Go 包进行类型检查,将函数编译为机器代码。代码,最后将编译后的包定义写到磁盘上。 内部定义基本类型、内置函数和操作函数的阶段是在 types/universe.go 当中。同时会进行内置函数和具体的操作符匹配,可以明确知道内置函数 len 对应的是 OLEN: var builtinFuncs = [...]struct { name string op Op }{ {"append", OAPPEND}, {"cap", OCAP}, {"close", OCLOSE}, {"complex", OCOMPLEX}, {"copy", OCOPY}, {"delete", ODELETE}, {"imag", OIMAG}, {"len", OLEN}, ... }在编译时,上分为五个阶段进行类型检查: 第一阶段:常量、类型、以及函数的名称和类型。 第二阶段:变量赋值、接口赋值、别名声明。 第三阶段:类型检查函数体。 第四阶段:检查外部声明。 第五阶段:检查类型的地图键,未使用的导入。如果最后一个类型检查阶段遇到 len 函数,就会转换为 UnaryExpr 类型,一个 UnaryExpr 节点代表一个单数表达式,也最终就是不会成为函数调用: func typecheck1(n ir.Node, top int) ir.Node { if n, ok := n.(*ir.Name); ok { typecheckdef(n) } switch n.Op() { ... case ir.OCAP, ir.OLEN: n := n.(*ir.UnaryExpr) return tcLenCap(n) } }在调用 *ir.UnaryExpr 转换完毕后,会调用 tcLenCap,也就是 typecheck,使用 okforlen 数组来验证参数的合法性或发出相关错误信息: func tcLenCap(n *ir.UnaryExpr) ir.Node { n.X = Expr(n.X) n.X = DefaultLit(n.X, nil) n.X = implicitstar(n.X) ... var ok bool if n.Op() == ir.OLEN { ok = okforlen[t.Kind()] } else { ok = okforcap[t.Kind()] } ... n.SetType(types.Types[types.TINT]) return n }经历过上面的步骤后在对所有内容进行类型检查后,所有函数都将排队等待编译: base.Timer.Start("be", "compilefuncs") fcount := int64(0) for i := 0; i |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |