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,一经查实,立即删除!

相关文章

modelsim 的高效使用

大概的思路: 1、往modelsim 添加仿真库。 2、将Verilog 文件,testbench文件提出。建好文件夹。比如uart仿真: uart_sim文件夹下:rtl文件夹,test_bench文件夹。test_bench文件夹下sim文件夹。 3、打开modelsim&#xff…

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 …

matlab 中序列求极值的方法,Matlab中求序列的极值

我们知道,在Matlab中有专门求序列最大值和最小值的函数,分别是Max 和 Min,但是有时候我们不满足于求整个序列的最值,而是对序列的极值,也就是局部的最值感兴趣。对于解析函 数,这个比较简单,只要…

oracle rank 语法_oracle rank函数怎么用

首先创建示例表,再初始化几条测试数据create table t_score(id number primary key,student_id number(3),student_name char(8) not null,sub_name varchar2(20),score number(10,2));insert into t_score (id, student_id, student_name, sub_name, score)values…

@synchronized (object)使用详解

synchronized关键字代表这个方法加锁,相当于不管哪一个线 程A每次运行到这个法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A,没有的话,直接…

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

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

c2010页面闪现_vue使用v-if v-show页面闪烁,div闪现的解决方法

在页面层次结构,数据较多的时候,用v-if或者v-show就会出现div闪现,或者部分闪烁的结果。可以在根元素添加v-cloak来解决,并且设置它的样式即可。代码只是示例,还需要自己修改测试。Document[v-cloak]{display: none;}c…

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

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

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程序员仅需处理事件回调…

三角形外接球万能公式_外接球半径常见的求法

2:若一个多面体的各面都与一个球的球面相切,则称这个多面体是这个球的外切多面体,这个球是这个多面体的内切球。球心到截面的距离d与球半径R及截面的半径r有以下关系:.球面被经过球心的平面截得的圆叫.被不…

如何准备考试

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

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

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

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

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

android 粘性view_Android自定义StickinessView粘性滑动效果

design包的出现,Android界面发生了巨大变化,各种滑动配合的效果,下面我就粘性滑动中的一种进行自定义,效果图如下:大家看到效果了,这里我是继承了LinerLayout,方便一点,若果是ViewGr…

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

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

matlab粒子图像测速工具,程序 PIVlab - 时间分辨粒子图像测速(PIV)工具: 一 联合开发网 - pudn.com...

程序所属分类:图形图像处理开发工具:matlab文件大小:7964KB下载次数:29上传日期:2017-07-21 11:48:16上 传 者:long1219说明: PIVlab - 时间分辨粒子图像测速(PIV)工具:一种基于GUI…

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。 示…