迷惑了,Go len() 是怎么计算出来的?

您所在的位置:网站首页 直播上的链接是怎么获取的呢 迷惑了,Go len() 是怎么计算出来的?

迷惑了,Go len() 是怎么计算出来的?

2024-03-03 11:26| 来源: 网络整理| 查看: 265

[[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