返回顶部
首页 > 资讯 > 精选 >go语言需要编译吗
  • 400
分享到

go语言需要编译吗

2023-07-04 17:07:29 400人浏览 八月长安
摘要

这篇文章主要介绍“Go语言需要编译吗”,在日常操作中,相信很多人在go语言需要编译吗问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go语言需要编译吗”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!go语言需

这篇文章主要介绍“Go语言需要编译吗”,在日常操作中,相信很多人在go语言需要编译吗问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go语言需要编译吗”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

go语言需要编译。Go语言是编译型的静态语言,是一门需要编译才能运行的编程语言,也就说Go语言程序在运行之前需要通过编译器生成二进制机器码(二进制的可执行文件),随后二进制文件才能在目标机器上运行。

Go语言是一门需要编译才能运行的编程语言,也就说代码在运行之前需要通过编译器生成二进制机器码,随后二进制文件才能在目标机器上运行。

简单来说,Go语言是编译型的静态语言(和C语言一样),所以在运行Go语言程序之前,先要将其编译成二进制的可执行文件。

如果我们想要了解Go语言的实现原理,理解它的编译过程就是一个没有办法绕过的事情。下面就来看看Go语言是怎么完成编译的。

预备知识

想要深入了解Go语言的编译过程,需要提前了解一下编译过程中涉及的一些术语和专业知识。这些知识其实在我们的日常工作和学习中比较难用到,但是对于理解编译的过程和原理还是非常重要的。

1) 抽象语法树

在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。而类似于 if else 这样的条件判断语句,可以使用带有两个分支的节点来表示。

以算术表达式 1+3*(4-1)+2 为例,可以解析出的抽象语法树如下图所示:

go语言需要编译吗

图:抽象语法树

抽象语法树可以应用在很多领域,比如浏览器,智能编辑器,编译器。

2) 静态单赋值

在编译器设计中,静态单赋值形式(static single assignment fORM,通常简写为 SSA form 或是 SSA)是中介码(IR,intermediate representation)的属性,它要求每个变量只分配一次,并且变量需要在使用之前定义。在实践中我们通常会用添加下标的方式实现每个变量只能被赋值一次的特性,这里以下面的代码举一个简单的例子:

x := 1x := 2y := x

从上面的描述所知,第一行赋值行为是不需要的,因为 x 在第二行被二度赋值并在第三行被使用,在 SSA 下,将会变成下列的形式:

x1 := 1x2 := 2y1 := x2

从使用 SSA 的中间代码我们就可以非常清晰地看出变量 y1 的值和 x1 是完全没有任何关系的,所以在机器码生成时其实就可以省略第一步,这样就能减少需要执行的指令来优化这一段代码。

根据 Wikipedia(维基百科)对 SSA 的介绍来看,在中间代码中使用 SSA 的特性能够为整个程序实现以下的优化:

  • 常数传播(constant propagation)

  • 值域传播(value range propagation)

  • 稀疏有条件的常数传播(sparse conditional constant propagation)

  • 消除无用的程式码(dead code elimination)

  • 全域数值编号(global value numbering)

  • 消除部分的冗余(partial redundancy elimination)

  • 强度折减(strength reduction)

  • 寄存器分配(reGISter allocation)

因为 SSA 的作用的主要作用就是代码的优化,所以是编译器后端(主要负责目标代码的优化和生成)的一部分。当然,除了 SSA 之外代码编译领域还有非常多的中间代码优化方法,优化编译器生成的代码是一个非常古老并且复杂的领域,这里就不展开介绍了。

3) 指令集架构

最后要介绍的一个预备知识就是指令集架构了,指令集架构(Instruction Set Architecture,简称 ISA),又称指令集或指令集体系,是计算机体系结构中与程序设计有关的部分,包含了基本数据类型,指令集,寄存器,寻址模式,存储体系,中断,异常处理以及外部 I/O。指令集架构包含一系列的 opcode 即操作码(机器语言),以及由特定处理器执行的基本命令。

指令集架构常见种类如下:

  • 复杂指令集运算(Complex Instruction Set Computing,简称 CISC);

  • 精简指令集运算(Reduced Instruction Set Computing,简称 RISC);

  • 显式并行指令集运算(Explicitly Parallel Instruction Computing,简称 EPIC);

  • 超长指令字指令集运算(VLIW)。


不同的处理器(CPU)使用了大不相同的机器语言,所以我们的程序想要在不同的机器上运行,就需要将源代码根据架构编译成不同的机器语言。

编译原理

Go语言编译器的源代码在 cmd/compile 目录中,目录下的文件共同构成了Go语言的编译器,学过编译原理的人可能听说过编译器的前端和后端,编译器的前端一般承担着词法分析、语法分析、类型检查和中间代码生成几部分工作,而编译器后端主要负责目标代码的生成和优化,也就是将中间代码翻译成目标机器能够运行的机器码。

go语言需要编译吗

Go的编译器在逻辑上可以被分成四个阶段:词法与语法分析、类型检查和 AST 转换、通用 SSA 生成和最后的机器代码生成,下面我们来分别介绍一下这四个阶段做的工作。

1) 词法与语法分析

所有的编译过程其实都是从解析代码的源文件开始的,词法分析的作用就是解析源代码文件,它将文件中的字符串序列转换成 Token 序列,方便后面的处理和解析,我们一般会把执行词法分析的程序称为词法解析器(lexer)。

而语法分析的输入就是词法分析器输出的 Token 序列,这些序列会按照顺序被语法分析器进行解析,语法的解析过程就是将词法分析生成的 Token 按照语言定义好的文法(Grammar)自下而上或者自上而下的进行规约,每一个 Go 的源代码文件最终会被归纳成一个 SourceFile 结构:

SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" }

标准的 golang 语法解析器使用的就是 LALR(1) 的文法,语法解析的结果其实就是上面介绍过的抽象语法树(AST),每一个 AST 都对应着一个单独的Go语言文件,这个抽象语法树中包括当前文件属于的包名、定义的常量、结构体和函数等。

如果在语法解析的过程中发生了任何语法错误,都会被语法解析器发现并将消息打印到标准输出上,整个编译过程也会随着错误的出现而被中止。

2) 类型检查

当拿到一组文件的抽象语法树 AST 之后,Go语言的编译器会对语法树中定义和使用的类型进行检查,类型检查分别会按照顺序对不同类型的节点进行验证,按照以下的顺序进行处理:

  • 常量、类型和函数名及类型;

  • 变量的赋值和初始化;

  • 函数和闭包的主体;

  • 哈希键值对的类型;

  • 导入函数体;

  • 外部的声明;

通过对每一棵抽象节点树的遍历,我们在每一个节点上都会对当前子树的类型进行验证保证当前节点上不会出现类型错误的问题,所有的类型错误和不匹配都会在这一个阶段被发现和暴露出来。

类型检查的阶段不止会对树状结构的节点进行验证,同时也会对一些内建的函数进行展开和改写,例如 make 关键字在这个阶段会根据子树的结构被替换成 makeslice 或者 makechan 等函数。

其实类型检查不止对类型进行了验证工作,还对 AST 进行了改写以及处理Go语言内置的关键字,所以,这一过程在整个编译流程中是非常重要的,没有这个步骤很多关键字其实就没有办法工作。【相关推荐:Go视频教程

3) 中间代码生成

当我们将源文件转换成了抽象语法树,对整个语法树的语法进行解析并进行类型检查之后,就可以认为当前文件中的代码基本上不存在无法编译或者语法错误的问题了,Go语言的编译器就会将输入的 AST 转换成中间代码。

Go语言编译器的中间代码使用了 SSA(Static Single Assignment Form) 的特性,如果我们在中间代码生成的过程中使用这种特性,就能够比较容易的分析出代码中的无用变量和片段并对代码进行优化。

在类型检查之后,就会通过一个名为 compileFunctions 的函数开始对整个Go语言项目中的全部函数进行编译,这些函数会在一个编译队列中等待几个后端工作协程的消费,这些 Goroutine 会将所有函数对应的 AST 转换成使用 SSA 特性的中间代码。

4) 机器码生成

Go语言源代码的 cmd/compile/internal 目录中包含了非常多机器码生成相关的包,不同类型的 CPU 分别使用了不同的包进行生成 amd64、arm、arm64、mips、mips64、ppc64、s390x、x86 和 wasm,也就是说Go语言能够在几乎全部常见的 CPU 指令集类型上运行。

编译器入口

Go语言的编译器入口是 src/cmd/compile/internal/GC 包中的 main.go 文件,这个 600 多行的 Main 函数就是Go语言编译器的主程序,这个函数会先获取命令行传入的参数并更新编译的选项和配置,随后就会开始运行 parseFiles 函数对输入的所有文件进行词法与语法分析得到文件对应的抽象语法树:

func Main(archInit func(*Arch)) {    // ...    lines := parseFiles(flag.Args())

接下来就会分九个阶段对抽象语法树进行更新和编译,就像我们在上面介绍的,整个过程会经历类型检查、SSA 中间代码生成以及机器码生成三个部分:

  • 检查常量、类型和函数的类型;

  • 处理变量的赋值;

  • 对函数的主体进行类型检查;

  • 决定如何捕获变量;

  • 检查内联函数的类型;

  • 进行逃逸分析;

  • 将闭包的主体转换成引用的捕获变量;

  • 编译顶层函数;

  • 检查外部依赖的声明;

了解了剩下的编译过程之后,我们重新回到词法和语法分析后的具体流程,在这里编译器会对生成语法树中的节点执行类型检查,除了常量、类型和函数这些顶层声明之外,它还会对变量的赋值语句、函数主体等结构进行检查:

for i := 0; i < len(xtop); i++ {    n := xtop[i]    if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {        xtop[i] = typecheck(n, ctxStmt)    }}for i := 0; i < len(xtop); i++ {    n := xtop[i]    if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {        xtop[i] = typecheck(n, ctxStmt)    }}for i := 0; i < len(xtop); i++ {    n := xtop[i]    if op := n.Op; op == ODCLFUNC || op == OCLOSURE {        typecheckslice(Curfn.Nbody.Slice(), ctxStmt)    }}checkMapKeys()for _, n := range xtop {    if n.Op == ODCLFUNC && n.Func.Closure != nil {        capturevars(n)    }}escapes(xtop)for _, n := range xtop {    if n.Op == ODCLFUNC && n.Func.Closure != nil {        transformclosure(n)    }}

类型检查会对传入节点的子节点进行遍历,这个过程会对 make 等关键字进行展开和重写,类型检查结束之后并没有输出新的数据结构,只是改变了语法树中的一些节点,同时这个过程的结束也意味着源代码中已经不存在语法错误和类型错误,中间代码和机器码也都可以正常的生成了。

    initssaconfig()    peekitabs()    for i := 0; i < len(xtop); i++ {        n := xtop[i]        if n.Op == ODCLFUNC {            funccompile(n)        }    }    compileFunctions()    for i, n := range externdcl {        if n.Op == ONAME {            externdcl[i] = typecheck(externdcl[i], ctxExpr)        }    }    checkMapKeys()}

在主程序运行的最后,会将顶层的函数编译成中间代码并根据目标的 CPU 架构生成机器码,不过这里其实也可能会再次对外部依赖进行类型检查以验证正确性。

到此,关于“go语言需要编译吗”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: go语言需要编译吗

本文链接: https://lsjlt.com/news/346584.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • go语言需要编译吗
    这篇文章主要介绍“go语言需要编译吗”,在日常操作中,相信很多人在go语言需要编译吗问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go语言需要编译吗”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!go语言需...
    99+
    2023-07-04
  • go语言是否需要编译
    go语言需要编译。Go语言是编译型的静态语言,是一门需要编译才能运行的编程语言,也就说Go语言程序在运行之前需要通过编译器生成二进制机器码(二进制的可执行文件),随后二进制文件才能在目标机器上运行。本教程操作环境:windows7系统、GO...
    99+
    2023-05-14
    go语言 Go Golang
  • go语言能编译吗
    这篇“go语言能编译吗”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“go语言能编译吗”文章吧。go语言能编译。Go语言是编译...
    99+
    2023-07-04
  • go语言需要delet吗
    go语言不需要delet。Go语言没有引入delete关键字的原因之一是为了保持语言的简洁性和一致性。因为Go语言的设计哲学之一是“保持简单”,因此语言的功能和特性要尽可能简洁和直观,通过将删除元素的功能与映射类型结合起来,可以减少语言的复...
    99+
    2023-07-10
  • go语言需要ioc吗
    go语言不需要ioc。原因是Go语言的设计理念是简洁和直接,故意避免引入复杂的概念和框架,因此没有原生支持IOC。关键是根据项目的具体需求,权衡是否需要引入IOC和所选择的解决方案的复杂性。本教程操作环境:Windows10系统、go1.2...
    99+
    2023-07-10
  • go语言需要分号吗
    本教程操作环境:windows7系统、GO 1.18版本、Dell G3电脑。Go语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号, 因此换行符添加的位置会影响Go代码的正确解...
    99+
    2022-11-25
    Go go语言 Golang
  • go语言能被反编译吗
    go语言不能被反编译,其原因如下:1、Golang编译器将代码转换为中间码,即字节码,无法直接在计算机上运行;2、Golang编译器还会进行代码优化和混淆,使其更难被理解和分析;3、Golang还通过垃圾回收器和众多的安全检查来增加其安全性...
    99+
    2023-07-31
  • Go语言开发:编译的重要性
    Go语言开发:编译的重要性 在进行Go语言开发时,编译是至关重要的一环。通过编译,我们可以将高级语言代码转换为计算机可以理解并执行的机器码,保证程序的运行效率和性能。本文将探讨编译在G...
    99+
    2024-04-02
  • 使用第三方go库需要重新编译吗?
    使用第三方Go库时,不需要重新编译整个项目。Go的包管理工具可以方便地将第三方库导入到项目中,只需在代码中导入相应的包即可使用其中的函数和方法。在编译项目时,Go编译器会自动解析导入的...
    99+
    2024-02-09
    go语言
  • php代码不需要编译吗
    本教程操作环境:windows7系统、PHP8.1版、DELL G3电脑php代码不需要编译吗?php代码需要编译。php是解释型语言。PHP代码其实也需要经过编译器编译,因为是实时编译的,所以我们写的代码能直接看到结果。这个编译器是由PH...
    99+
    2024-04-02
  • go语言需不需要分号
    这篇文章主要介绍了go语言需不需要分号的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇go语言需不需要分号文章都会有所收获,下面我们一起来看看吧。Go语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。...
    99+
    2023-07-04
  • go语言能不能编译
    go语言能编译。Go语言是编译型的静态语言,是一门需要编译才能运行的编程语言。对Go语言程序进行编译的命令有两种:1、“go build”命令,可以将Go语言程序代码编译成二进制的可执行文件,但该二进制文件需要手动运行;2、“go run”...
    99+
    2023-05-14
    编译 Go Golang go语言
  • javascript是编译型语言吗
    这篇“javascript是编译型语言吗”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“ja...
    99+
    2024-04-02
  • Go语言编程:编译与运行
    标题:Go语言编程:编译与运行 Go语言(又称Golang)是由Google开发的一种开源编程语言,它具有简洁、高效、并发等优点,受到了广泛的关注和应用。在进行Go语言编程时,编译与运...
    99+
    2024-04-02
  • go语言是编程语言吗
    本教程操作环境:windows7系统、GO 1.18版本、Dell G3电脑。Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型、并...
    99+
    2022-11-28
    Go Golang go语言
  • golang编译后需要环境支持吗
    否,go 编译后的程序不需要环境支持。go 编译器将其编译为机器码并静态链接所有依赖项,从而创建独立的可执行文件,可以在任何兼容的计算机上运行。 Go 编译后需要环境支持吗? 回答:否...
    99+
    2024-04-21
    golang
  • 如何编译go语言程序
    本篇文章给大家分享的是有关如何编译go语言程序,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Go语言是编译型的静态语言(和C语言一样),所以在运行Go语言程序之前,先要将其编译...
    99+
    2023-06-15
  • go语言用哪个编译器
    go语言用的编译器:1、LiteIDE是一个简单的开源IDE;2、VS Code具有高可扩展性;3、Eclipse插件GoClipse是免费开源的;4、Atom是github推出的跨平台文本编辑器;5、Vim是自由软件并有很多插件;6、Go...
    99+
    2023-07-12
  • go语言如何编译代码
    go语言编译代码的步骤如下:1、编写Go代码并保存到一个或多个以.go为扩展名的文件中;2、使用命令行界面进入到你的源代码文件所在的目录,并执行”go build“命令来编译代码;3、编译成功后,切换到可执行文件所在的目录,并执行”./ma...
    99+
    2023-12-12
    go语言 Golang go语言编译
  • go语言需不需要第三方库
    go语言需要第三方库,尽管go语言本身提供了很多强大的功能,但有的时候开发人员需要使用第三方库来完成特定的任务,第三方库通常提供一些go语言标准库中不包含的功能,并且经过了更广泛的测试和优化,以确保其质量和性能,第三方库的使用可以将开发时间...
    99+
    2023-07-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作