slice 转byte go_一文告诉你神奇的Go内建函数源码在哪里

e1f4bbb57ec432d5892724522dbb9b09.png
Go内建函数源码,我好像在哪里见过你。 - 佚名

1. 何为Go内建函数

众所周知,Go是最简单的主流编程语言之一,截至Go 1.15版本,Go语言的关键字的规模依旧保持在25个:

cecd9c0b4a193a80be20c90c3870ba74.png

很多刚入门的gopher可能会问:像bool、byte、error、true、iota甚至int都难道都不是关键字?没错!和其他语言不同,这些标识符并不是关键字,在Go中它们被称为预定义标识符。这些标识符拥有universe block作用域(关于go代码块作用域的详细解析,可参考我的技术专栏:“改善Go语⾔编程质量的50个有效实践”),可以在任何源码位置使用。

8c361fe840895a13dbeca66817913cd1.png

从上图我们看到:所谓的Go内建函数也包含在这个预定义标识符集合中,只是这些标识符被用作函数名称标识符罢了。

2. 预定义标识符可被override

Go语言的关键字是保留的,我们无法将其用于规范之外的其他场合,比如作为变量的标识符。但是预定义标识符不是关键字,我们可以override它们。下面就是一个对默认表示整型类型的预定义标识符int进行override的例子:

package mainimport ("fmt""unsafe"
)type int int8func main() {var a int = 5fmt.Printf("%Tn", a) // main.int,而不是intfmt.Println(unsafe.Sizeof(a)) // 1,而不是8
}

在上述这个源文件中,预定义标识符int被override为一个自定义类型int,该类型的underlying type为int8,于是当我们输出该类型变量(代码中的变量a)的类型和长度时,我们得到的是http://main.int和1,而不是int和8。

3. 预定义标识符的声明源码在哪里

Go是开源的编程语言,这些预定义标识符想必也都有自己的“归宿”吧,的确是这样的。Go的每个发行版都带有一份源码,而预定义标识符就在这份源码中。

Go 1.14为例,我们可以在下面路径中找到预定义标识符的源码:

$GOROOT/src/builtin/builtin.go

以string、int、uint这几个代表原生类型的预定义标识符为例,它们的声明代码如下:

// $GOROOT/src/builtin/builtin.go// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string// int is a signed integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, int32.
type int int// uint is an unsigned integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, uint32.
type uint uint

同时,我们利用go doc builtin.int也可以查看预定义标识符int的文档:

$go doc builtin.int
package builtin // import "builtin"type int intint is a signed integer type that is at least 32 bits in size. It is adistinct type, however, and not an alias for, say, int32.func cap(v Type) int
func copy(dst, src []Type) int
func len(v Type) int

4. 内建函数的源码在哪里?

作为预声明标识符子集的内建函数们在builtin.go中也都有自己的位置,比如:以append这个内建函数为例,我们可以在Go安装包的builtin.go中找到它的原型(Go 1.14):

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
// slice = append(slice, elem1, elem2)
// slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
// slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

但我们惊奇的发现:这里没有append函数的实现。那么append内建函数实现的源码究竟在哪里呢?本质上讲append函数,包括其他内建函数其实并没有自己的实现源码。

内建函数仅仅是一个标识符,在Go源码编译期间,Go编译器遇到内建函数标识符时会将其替换为若干runtime的调用,我们还以append函数为例,我们输出下面代码的汇编代码(Go 1.14):

// append.go
package mainimport "fmt"func main() {var s = []int{5, 6}s = append(s, 7, 8)fmt.Println(s)
}$go tool compile -S append.go > append.s

汇编节选如下(append.s):

"".main STEXT size=277 args=0x0 locals=0x580x0000 00000 (xxx.go:5) TEXT "".main(SB), ABIInternal, $88-00x0000 00000 (xxx.go:5) MOVQ (TLS), CX0x0009 00009 (xxx.go:5) CMPQ SP, 16(CX)0x000d 00013 (xxx.go:5) PCDATA $0, $-20x000d 00013 (xxx.go:5) JLS  2670x0013 00019 (xxx.go:5) PCDATA $0, $-10x0013 00019 (xxx.go:5) SUBQ $88, SP0x0017 00023 (xxx.go:5) MOVQ BP, 80(SP)0x001c 00028 (xxx.go:5) LEAQ 80(SP), BP0x0021 00033 (xxx.go:5) PCDATA $0, $-20x0021 00033 (xxx.go:5) PCDATA $1, $-20x0021 00033 (xxx.go:5) FUNCDATA $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)0x0021 00033 (xxx.go:5) FUNCDATA $1, gclocals·568470801006e5c0dc3947ea998fe279(SB)0x0021 00033 (xxx.go:5) FUNCDATA $2, gclocals·bfec7e55b3f043d1941c093912808913(SB)0x0021 00033 (xxx.go:5) FUNCDATA $3, "".main.stkobj(SB)0x0021 00033 (xxx.go:6) PCDATA $0, $10x0021 00033 (xxx.go:6) PCDATA $1, $00x0021 00033 (xxx.go:6) LEAQ type.[2]int(SB), AX0x0028 00040 (xxx.go:6) PCDATA $0, $00x0028 00040 (xxx.go:6) MOVQ AX, (SP)0x002c 00044 (xxx.go:6) CALL runtime.newobject(SB)0x0031 00049 (xxx.go:6) PCDATA $0, $10x0031 00049 (xxx.go:6) MOVQ 8(SP), AX0x0036 00054 (xxx.go:6) MOVQ $5, (AX)0x003d 00061 (xxx.go:6) MOVQ $6, 8(AX)0x0045 00069 (xxx.go:7) PCDATA $0, $20x0045 00069 (xxx.go:7) LEAQ type.int(SB), CX0x004c 00076 (xxx.go:7) PCDATA $0, $10x004c 00076 (xxx.go:7) MOVQ CX, (SP)0x0050 00080 (xxx.go:7) PCDATA $0, $00x0050 00080 (xxx.go:7) MOVQ AX, 8(SP)0x0055 00085 (xxx.go:7) MOVQ $2, 16(SP)0x005e 00094 (xxx.go:7) MOVQ $2, 24(SP)0x0067 00103 (xxx.go:7) MOVQ $4, 32(SP)0x0070 00112 (xxx.go:7) CALL runtime.growslice(SB)0x0075 00117 (xxx.go:7) PCDATA $0, $10x0075 00117 (xxx.go:7) MOVQ 40(SP), AX0x007a 00122 (xxx.go:7) MOVQ 48(SP), CX0x007f 00127 (xxx.go:7) MOVQ 56(SP), DX0x0084 00132 (xxx.go:7) MOVQ $7, 16(AX)0x008c 00140 (xxx.go:7) MOVQ $8, 24(AX)0x0094 00148 (xxx.go:8) PCDATA $0, $00x0094 00148 (xxx.go:8) MOVQ AX, (SP)0x0098 00152 (xxx.go:7) LEAQ 2(CX), AX0x009c 00156 (xxx.go:8) MOVQ AX, 8(SP)0x00a1 00161 (xxx.go:8) MOVQ DX, 16(SP)0x00a6 00166 (xxx.go:8) CALL runtime.convTslice(SB)... ...

我们可以看到:append并没有以独立的身份出现在CALL汇编指令的后面,而是被换成:runtime.growslice、runtime.convTslice以及相关汇编指令了。


Go技术专栏“改善Go语⾔编程质量的50个有效实践”主要满足广大gopher关于Go语言进阶的需求,围绕如何写出地道且高质量Go代码给出50条有效实践建议,欢迎大家订阅!

Gopher Daily(Gopher每日新闻)归档仓库 - https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博:https://weibo.com/bigwhite20xx
  • 微信公众号:iamtonybai
  • 博客:http://tonybai.com
  • github: https://github.com/bigwhite
  • “Gopher部落”知识星球:https://public.zsxq.com/groups/51284458844544

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/471926.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 1586. 二叉搜索树迭代器 II(数组+栈)

文章目录1. 题目2. 解题1. 题目 实现二叉搜索树(BST)的中序遍历迭代器 BSTIterator 类: BSTIterator(TreeNode root) 初始化 BSTIterator 类的实例。 二叉搜索树的根节点 root 作为构造函数的参数传入。 内部指针使用一个不存在于树中且小于…

LeetCode 1570. 两个稀疏向量的点积(哈希)

文章目录1. 题目2. 解题1. 题目 给定两个稀疏向量,计算它们的点积(数量积)。 实现类 SparseVector: SparseVector(nums) 以向量 nums 初始化对象。dotProduct(vec) 计算此向量与 vec 的点积。 稀疏向量 是指绝大多数分量为 0 …

LeetCode 1644. 二叉树的最近公共祖先 II

文章目录1. 题目2. 解题1. 题目 给定一棵二叉树的根节点 root,返回给定节点 p 和 q 的最近公共祖先(LCA)节点。 如果 p 或 q 之一不存在于该二叉树中,返回 null。 树中的每个节点值都是互不相同的。 根据维基百科中对最近公共祖…

这就是搜索引擎--读书笔记五--索引的建立与更新

索引的建立和更新 索引的建立 前一总结里说到,如果索引结构建立好了,可以提高搜索的速度,那么给定一个文档集合,索引是如何建立起来的呢?建立索引的方式有很多种,在这里我就书中提到的三种方法简单总结一下…

LeetCode 1650. 二叉树的最近公共祖先 III(哈希)

文章目录1. 题目2. 解题1. 题目 给定一棵二叉树中的两个节点 p 和 q,返回它们的最近公共祖先节点(LCA)。 每个节点都包含其父节点的引用(指针)。Node 的定义如下: class Node {public int val;public No…

PHP方向+go+rpc+swoole,瞅瞅 PHP+Swoole 作为网络通信框架

这里瞅瞅Swoole框架,因为说的比较屌,官网里面文档比较多https://www.swoole.com/代码地址(https://gitee.com/swoole/swoole)这里先复制他的说明(https://wiki.swoole.com/)Swoole底层内置了异步非阻塞、多线程的网络IO服务器。PHP程序员仅需处理事件回调…

如何准备考试

最近准备International Requirement Engeering Board 考试,但凡上点年纪对记忆就不行了,时间也不够,就想着怎么偷懒。 因此,就把测试题做了一遍,然后分析各个章节的分值比重及自己容易错的地方的比重。然后有的放矢再去…

LeetCode 1676. 二叉树的最近公共祖先 IV

文章目录1. 题目2. 解题1. 题目 给定一棵二叉树的根节点 root 和 TreeNode 类对象的数组(列表) nodes,返回 nodes 中所有节点的最近公共祖先(LCA)。 数组(列表)中所有节点都存在于该二叉树中&a…

matlab行人检测非极大值抑制,多目标检测中的非极大值抑制(NMS)的算法改进_jza...

非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,可以理解为局部最大搜索。这个局部代表的是一个邻域,邻域有两个参数可变,一是邻域的维数,二是邻域的大小。而是用于目标检测…

Azure SQL 数据库:服务级别与性能问答

ShawnBice 2014 年 5 月 5 日上午 10:00 几天前,我发表了一篇文章,并就 4 月 24 日发布的适用于Windows Azure SQL 数据库的新服务级别提供了一些预料中的问题和解答,在其中为读者介绍了一些详细信息。在这篇跟进文章中,我想提…

LeetCode 1852. 每个子数组的数字种类数(滑窗)

文章目录1. 题目2. 解题1. 题目 给你一个整数数组 nums与一个整数 k,请你构造一个长度 n-k1 的数组 ans,这个数组第i个元素 ans[i] 是每个长度为k的子数组 nums[i:ik-1] [nums[i], nums[i1], ..., nums[ik-1]]中数字的种类数。 返回这个数组 ans。 示…

用chrome模拟微信浏览器访问需要OAuth2.0网页授权的页面

现在很流行微信网页小游戏,用html5制作的小游戏移过来,可以放到微信浏览器中打开,关键是可以做成微信分享朋友圈的形式,大大提高游戏的传播,增强好友的游戏互动。 微信浏览器中打开网页游戏效果还不错,对手…

LeetCode 1891. 割绳子(二分查找)

文章目录1. 题目2. 解题1. 题目 给定一个整数数组 ribbons 和一个整数 k,数组每项 ribbons[i] 表示第 i 条绳子的长度。 对于每条绳子,你可以将任意切割成一系列长度为正整数的部分,或者选择不进行切割。 例如,如果给你一条长度…

LeetCode 1618. 找出适应屏幕的最大字号(二分查找)

文章目录1. 题目2. 解题1. 题目 给定一个字符串 text。并能够在 宽为 w 高为 h 的屏幕上显示该文本。 字体数组中包含按升序排列的可用字号,您可以从该数组中选择任何字体大小。 您可以使用FontInfo接口来获取任何可用字体大小的任何字符的宽度和高度。 FontInf…

UML类图画法及类之间几种关系

文章目录如下: 一、类图画法 二、类之间的几种关系:泛化(Generalization)、实现(Realization)、关联(Association)(又分一般关联、聚合(Aggregation&#xff…

LeetCode 1634. 求两个多项式链表的和

文章目录1. 题目2. 解题1. 题目 多项式链表是一种特殊形式的链表,每个节点表示多项式的一项。 每个节点有三个属性: coefficient:该项的系数。项 9x4 的系数是 9 。power:该项的指数。项 9x4 的指数是 4 。next:指向…

python编程制作接金币游戏,闪电侠接金币的FlashMan类

python the Flash man catch coin gif animation闪电侠是美剧,这里是一个小游戏,操作闪电侠接不断冒出来的金币。本模块定义了FlashMan类。这个模块能单独运行,运行后用鼠标操作闪电侠移动即可。以下是部分代码预览:""&…

LeetCode 1660. 纠正二叉树(BFS)

文章目录1. 题目2. 解题1. 题目 你有一棵二叉树,这棵二叉树有个小问题,其中有且只有一个无效节点,它的右子节点错误地指向了与其在同一层且在其右侧的一个其他节点。 给定一棵这样的问题二叉树的根节点 root ,将该无效节点及其所…

MS SQL Server 常用操作

以下为常用: --发邮件应该很常用吧 exec msdb.dbo.sp_send_dbmailprofile_name mail_profile, --邮件配置主档,通过数据库邮件配置向导生成,一般叫dba_profile,详细看下图recipients email_recipients, --地址,多个用英文逗号隔…

LeetCode 1730. 获取食物的最短路径(BFS)

文章目录1. 题目2. 解题1. 题目 你现在很饿,想要尽快找东西吃。你需要找到最短的路径到达一个食物所在的格子。 给定一个 m x n 的字符矩阵 grid ,包含下列不同类型的格子: * 是你的位置。矩阵中有且只有一个 * 格子。 # 是食物。矩阵中可…