本文转载自微信公众号《我的脑子是炸鱼》,作者陈建宇。转载本文请联系脑筋急转弯公众号。大家好,我是炸鱼。最近看到一个很有意思的话题。我们通常使用Go的内置函数len来获取各种映射和切片的长度。他是怎么意识到的?正当我想看len的具体实现时,一显身手,却发现是个空方法:funclen(vType)int在注释中没有链接到其他运行时函数,那么len函数是怎么调用的呢?之前也做了一些笔记,在这里分享给大家,共同进步。.谜底的答案今天,建宇将带领大家一起解开这个谜团。既然是谜底的答案,就要从头揭晓。事实上,Go语言中并没有len函数的具体实现代码。它实际上是Go编译器的“魔法”,而不是实际的函数调用。展开下一节,我们可以更深入地了解Go编译器的内部工作原理。Go编译器在编译时,会解析命令行参数和Go源文件中指定的标志,对解析后的Go包进行类型检查,并将函数编译成机器码。代码,最后将编译后的包定义写入磁盘。定义原始类型、内置函数和运算符函数的内部阶段在types/universe.go中。同时,内置函数会匹配特定的运算符。可以清楚的知道OLEN对应的内置函数len:varbuiltinFuncs=[...]struct{namestringopOp}{{"append",OAPPEND},{??"cap",OCAP},{"close",OCLOSE},{“复杂”,OCOMPLEX},{“复制”,OCOPY},{“删除”,ODELETE},{“imag”,OIMAG},{“len”,OLEN},..}在编译时,分为五个阶段进行类型检查:第一阶段:常量、类型、函数名和类型。第二阶段:变量赋值、接口赋值、别名声明。第三阶段:类型检查函数体。第4阶段:检查外部声明。阶段5:检查键入的映射键,未使用的导入。如果在最后的类型检查阶段遇到len函数,则将其转换为UnaryExpr类型。一个UnaryExpr节点表示一个单数表达式,最后不会变成函数调用:functypecheck1(nir.Node,topint)ir.Node{ifn,ok:=n.(*ir.Name);ok{typecheckdef(n)}switchn.Op(){...caseir.OCAP,ir.OLEN:n:=n.(*ir.UnaryExpr)returntcLenCap(n)}}调用*ir.UnaryExpr转换后,会调用tcLenCap,即typecheck,使用okforlen数组验证参数的合法性或发出相关错误信息:functcLenCap(n*ir.UnaryExpr)ir.Node{n.X=Expr(n.X)n.X=DefaultLit(n.X,nil)n.X=implicitstar(n.X)...varokboolifn.Op()==ir.OLEN{ok=okforlen[t.Kind()]}else{ok=okforcap[t.Kind()]}...n.SetType(类型.Types[types.TINT])return}完成上述步骤后,所有函数都将进行类型检查,所有函数都将排队等待编译:base.Timer.Start("be","compilefuncs")fcount:=int64(0)fori:=0;i
