Go语言入门笔记(一)| 青训营

162 阅读4分钟

一、GO上手

1.1 运行环境

看到这篇文章,希望你的go环境已经配置好了。如果没有,也没关系,这里给出一个在线Go运行环境

1.2 编写运行hello,world

让我们以输出hello world!来上手Go吧!

1.2.1 创建hello.go文件,编写代码

创将一个hello.go文件,写入以下代码:

 package main
 ​
 import "fmt"
 ​
 func main() {
     fmt.Println("Hello, World!")
 }

1.2.2 运行hello.go

  • 现在我们已经编写好源码文件呢?如何运行呢?

go run hello.go ,运行go程序。 细心点会发现,文件目录下没有生成可执行文件,例如hello.exe等。

go run 是 go语言的标准命令之一。

image.png

  • go run 命令做了那些工作呢,使我们的.go源文件运行起来呢?

    Go语言是编译型语言,区别于解释型语言。编译型语言和解释型语言的最大的区别是前者生成可执行文件,后者不生成。常见的编译型语言还有C、C++,常见的解释型语言有python、JavaScript等。

    由于Go语言是编译型语言,所以go run指令可以看成分两步完成。第一,编译链接生成二进制可执行程序。第二步,执行。

    • go run 命令可以简单的分两步:编译、运行。
    • 对于编译,go语言同样为我们提供了一个命令,go build
    • 编译:go build hello.go 运行后会生成可执行文件 hello.exe。如果想更深入的了解go build的命令,可以用 go help build 查看go build命令的使用详解。
    • 执行:命令行输入./hello.exe运行程序
  • 详细的编译过程,我们可以借助指令go build -n hello.go-n 的意思是打印go build命令编译文件时用到的命令,但并不执行它们。

1.3 go build详细过程

输入命令:go build -n hello.go,查看go build命令的执行过程。

过程看起来很乱,仔细观看下来可以发现主要由几部分组成,分别是:

  • 创建临时目录,mkdir -p $WORK/b001/;
  • 查找依赖信息,cat >$WORK/b001/importcfg << ...;
  • 执行源代码编译,... compile ...;
  • 收集链接库文件,cat >$WORK/b001/importcfg.link << ...;
  • 生成可执行文件,... link.exe -o ...;
  • 移动可执行文件,mv $WORK\b001\exe\a.out.exe hello.exe;

注意:其中$WORK 是指配置的$GOPATH路径。查看关于go的环境变量,可以用命令 go env

整个go build过程如下面流程

image.png 确实和C++编译过程很像,毕竟Go和C++都是编译型语言。

整个编译、链接过程都是在临时目录中进行的,最后将生成的可执行文件移动到源文件同级目录下。

1.4 go run详细过程

输入命令:go run -n hello.go,查看go run命令的执行过程。

仔细观看下来可以发现也是主要由几部分组成,分别是:

  • 创建临时目录,mkdir -p $WORK/b001/;
  • 查找依赖信息,cat >$WORK/b001/importcfg << ...;
  • 执行源代码编译,... compile ...;
  • 收集链接库文件,cat >$WORK/b001/importcfg.link << ...;
  • 生成可执行文件,... link.exe -o ...;
  • 执行可执行文件,$WORK\b001\exe\hello.exe

1.4.1 go run 不“生成”可执行文件

go run与go build 命令执行过程的区别,主要是在最后一步。go build在最后一步将可执行文件hello.exe从临时目录移动到源文件的同级目录,而go run命令不移动可执行文件,而是直接执行hello.exe。

这也就使得go run命令生成的可执行文件hello.exe,在运行后,会随临时目录$WORK/b001/删除。

1.4.2 go run与go build 的关系

宏观上,两条指令存在以下等价关系:

 go run hello.go    // 编译运行
 ​
 // 等价于以下命令
 go build hello.go // 编译
 ./hello.exe      // 运行
 rm ./hello.exe   // 删除可执行文件

二、详解Go语言文件

以上面的hello.go程序为例:

 package main
 ​
 import "fmt"
 ​
 func main() {
     fmt.Println("Hello, World!")
 }

大致可分为三部分:

  • 定义包名。package
  • 导入包。 import
  • 编写函数。

2.1 定义包

2.1.1 包的概念

包(package)是Go语言实现代码复用的重要手段。包的存在可以让工程师只关心包中有用的函数,而其他大多数的函数都不需要去关心。

Go语言自带了100多个标准包,常用的官方标准包有fmt、net/http、io、log等等。

2.1.2 包和go源文件

  • 所有的.go文件都会声明包名。格式为 package 包名
  • 包中可以有多个.go源文件
  • 任意的.go源文件只能声明在一个包下。

2.1.3 包名和目录名

windows系统中有目录、文件的概念,Go语言又有包的概念,有何联系呢?

  • 包名声明在.go的源文件中。.go的源文件存放在目录中。
  • 同一目录下可以存放多个.go源文件,这些源文件声明的包名是相同的,即同一目录下的go源文件,属于同一个包。
  • 一般来说,我们常用目录名来定义包名。但用package声明包名时,也不一定非要和源文件所处的目录名相同。

2.1.4 包的命名规范

  • 包名尽可能与它所在的目录名保持一致,不要与标准包名冲突。
  • 包名一般为小写。
  • 简短而有意义,避免没有意义的包名。
  • 避免不必要的包名冲突。不同目录中,可能含有相同的包名。

2.1.5 main包

一个可执行的go项目,一定会有一个main包,即一定存在个.go文件声明的包名为package main。并且,main包,一定定义了一个名为main的函数,main函数,是整个go语言项目的入口地址。和C/C++的main函数的作用很相似。

2.2 导包

分两类包:官方标准包、自定义包 官方的包存放在:$GOROOT/src目录下 自定义的包:一般存放在$GOPATH/src目录下。当然,自定义包可以存放在任意位置,关键是导包的时候能找到此包。

导包借助import关键字。导包,就是给出包的路径,然go语言编译的时候能够找到包,并使用包中的函数。

注意:Go语言导入的包必须在程序内用到,否则编译无法通过。

2.2.1 两种包管理方式

GOPATH方式

旧版包管理方式。Go1.11之前常使用。就是配置一个GO工作目录,路径为$GOPATH。自定义的包要放在$GOPATH/src下。

GO MOD方式

新版包管理方式。Go1.11推出。对包的创建位置没有限制。

模式切换

通过命令设置环境变量即可。

 // 使用旧版 $GOPATH 方式
 go env -w GO111MODULE=off
 ​
 // 使用新版 GO MOD 方式
 go env -w GO111MODULE=on
 ​
 // 两种方式,视情况选择一种
 go env -w GO111MODULE=auto
 // // 何种情况选择 GO MOD
     // 1. 项目不在 $GOPATH 目录下
     // 2. 项目中含有go.mod文件

包搜索方式

import "fmt" 是如何根据fmt找到包的呢?

GOPATH方式

  • 搜索$GOROOT/src目录,查找名为fmt的包。找到,则结束。否则进行下一步。
  • 继续在$GOPATH/src 目录下找。如果还找不到,则报错。

GO MOD方式

首先需要go mod init 模块名初始化,此时会创建一个go.mod文件,go mod模式正式开启。

  • 搜索$GOROOT/src目录,查找名为fmt的包。找到,则结束。否则进行下一步。
  • 继续寻找。此时包的路径为 “模块名/包名”。
  •  // hello 模块
     hello
     ├── huft
          ├── mathclass
             └── test.go
          ├── demo1.go
     hello.go
     go.mod
     ​
     // 此时你在hello.go的导包路径
     import (
         "hello/huft"
         "hello/huft/mathclass"
     )
    

注意:$GoROOT$GOPATH 可通过go env 命令查看。

2.2.2 引用格式

I、标准引用

 import "fmt"
 ​
 import (   
     "fmt"
     "log"
 )

II、别名引入

 import F "fmt"
     
 import (
     F "fmt"
     l "log"
 )

给包fmt起了别名F,用F. 代替fmt.前缀。

注意:别名仅在当前文件中有效,即便是同一个包下的其他11文件导入相同包也不可以直接使用别名,除非其他文件导入时取了同样的别名。

III、省略引用当时

 import . "fmt"

命名空间合并,fmt包中的可导出元素不加fmt.前缀直接访问。

IV、仅执行包初始化init函数

 import  _ "fmt"

init初始化函数是编译的时候调用的,开发人员不可以在代码中调用。比如,fmt.Println可以直接用Println代替。

2.2.3 包中元素的访问权限

包中成员以名称首字母大小写决定访问权限。

  • 首字母大写,可被包外访问;
  • 首字母小写,仅包内成员可以访问;

参考

GO笔记之详解GO的编译执行流程