1 Go语言基础知识介绍

介绍Go语言之前,我们先了解一下有哪些开源项目是Go语言开发的,其中就包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。

Go语言经过了十几年的发展与成长,已经得到了不断的完善和优化。所以对于像我们这样想学习goalng的入门初学者来说,只要保持正确的学习方向和路径,我们的学习成果一定能够证明我们的努力和付出是值得的!

另外说下学习golang的一些个人感受:golang在某些地方很像C语言,所以如果你已经掌握了C语言或其它编程语言,那么你能够很快的掌握golang的一些基础运用。并且你会在对golang的一些指针操作、内存分配以及多协程处理上有更深的认识和体会。但是在日后的项目设计上,建议可以先尝试以golang的设计思想去实现一些功能设计,而不要代入C的一些设计思想。如果你没有学习过C语言或者其他编程语言,golang作为你的第一入门语言来说也不会非常困难,因为它的语法和规则以及设计思想已经帮助你解决了很多问题,而你只需要把大部分精力用来进行代码设计。但是,请不要放弃对底层原理的学习和探索,这样你才能够对golang掌握的更加熟练。

1.1 Go语言设计的由来

Go语言出自 Ken Thompson 和 Rob Pike、Robert Griesemer 之手,他们都是计算机科学领域的重量级人物。

  • Ken Thompson
    贝尔实验室 Unix 团队成员,C语言、Unix 和 Plan 9 的创始人之一,在 20 世纪 70 年代,设计并实现了最初的 UNIX 操作系统,仅从这一点说,他对计算机科学的贡献怎么强调都不过分。他还与 Rob Pike 合作设计了 UTF-8 编码方案。
  • Rob Pike
    Go语言项目总负责人,贝尔实验室 Unix 团队成员,除帮助设计 UTF-8 外,还帮助开发了分布式多用户操作系统 Plan 9、Inferno 操作系统和 Limbo 编程语言,并与人合著了《The Unix Programming Environment》,对 UNIX 的设计理念做了正统的阐述。
  • Robert Griesemer
    就职于 Google,参与开发 Java HotSpot 虚拟机,对语言设计有深入的认识,并负责 Chrome 浏览器和 Node.js 使用的 Google V8 JavaScript 引擎的代码生成部分。


其实Golang的诞生初衷是为了满足Google本身的需求,但是这门语言的设计却融合了Go语言团队多年的经验和对编程语言设计的深入认识。设计团队借鉴了 Pascal、Oberon 和C语言的设计智慧,同时让Go语言具备动态语言的便利性。

Go语言的所有设计者都说,设计Go语言是因为 C++ 给他们带来了挫败感。在 Google I/O 2012 的 Go 设计小组见面会上,Rob Pike 是这样说的:

我们做了大量的 C++ 开发,厌烦了等待编译完成,尽管这是玩笑,但在很大程度上来说也是事实。

1.2 Go 是编译型语言

Go 使用编译器来编译代码。编译器将源代码编译成二进制(或字节码)格式;在编译代码时,编译器检查错误、优化性能并输出可在不同平台上运行的二进制文件。要创建并运行 Go 程序,程序员必须执行如下步骤。

(1)使用文本编辑器创建 Go 程序;

(2)保存文件;

(3)编译程序;

(4)运行编译得到的可执行文件。

这不同于 Python、Ruby 和 JavaScript 等语言,它们不包含编译步骤。Go 自带了编译器,因此无须单独安装编译器。

1.3 Go工程结构

一个Go语言项目的目录一般包含以下三个子目录:

  • src 目录:放置项目和库的源文件;
  • pkg 目录:放置编译后生成的包/库的归档文件;
  • bin 目录:放置编译后生成的可执行文件。

三个目录中我们需要重点关注的是 src 目录,其他两个目录了解即可,下面来分别介绍一下这三个目录。

src 目录

用于以包(package)的形式组织并存放 Go 源文件,这里的包与 src 下的每个子目录是一一对应。例如,若一个源文件被声明属于 log 包,那么它就应当保存在 src/log 目录中。

并不是说 src 目录下不能存放 Go 源文件,一般在测试或演示的时候也可以把 Go 源文件直接放在 src 目录下,但是这么做的话就只能声明该源文件属于 main 包了。正常开发中还是建议大家把 Go 源文件放入特定的目录中。

package <包名>
go get

pkg 目录

go install

该目录与 GOROOT 目录(也就是Go语言的安装目录)下的 pkg 目录功能类似,区别在于这里的 pkg 目录专门用来存放项目代码的归档文件。

编译和安装项目代码的过程一般会以代码包为单位进行,比如 log 包被编译安装后,将生成一个名为 log.a 的归档文件,并存放在当前项目的 pkg 目录下。

bin 目录

go install


2 Go基本语法

2.1 变量

数学课中我们知道变量表示没有固定值且可改变的数,计算机课中我们又了解到变量是一段或多段用来存储数据的内存。

作为静态类型的Go语言来说,Go的变量总是有固定的数据类型,类型决定了变量内存的长度和存储格式。我们可以修改变量的变量值,但是无法更改变量的变量类型。

var

上面是一般的定义形式,我们可以放在开头进行变量定义。除了上面的定义方式外,golang还提供了一种简短模式进行变量定义,例如:

:=

(1)定义变量同时显式初始化。

(2)不能提供数据类型。

(3)只能在函数内部使用

我们在使用上面这种定义方式的时候一定需要弄清楚变量的作用域,因为有时候变量名称相同,但是表示的作用域和含义是不同的。例如:

golang里面还有一种匿名变量,当我们遇到一些没有名称的变量、类型或者方法的时候就能使用匿名变量来增强代码的灵活性。

__

注意:匿名变量不占用内存空间,也不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用

  • 作用域
    一个变量(常量、类型或函数)在程序中都有一定的作用范围,称之为作用域。
    了解变量的作用域对我们学习Go语言来说是比较重要的,因为Go语言会在编译时检查每个变量是否使用过,一旦出现未使用的变量,就会报编译错误。如果不能理解变量的作用域,就有可能会带来一些不明所以的编译错误。

我们可以根据变量的位置不同,分为局部变量全局变量形式参数三个类型。

局部变量

声明在函数体内的变量我们称之为局部变量,它的作用域只在函数体内部,函数的参数和返回值变量都属于局部变量。了解C知识就知道,局部变量不是一直存在的,它只定义在它的函数被调用后存在,函数调用结束后这个局部变量就会被销毁掉。例如:

全局变量

在函数体外声明的变量称之为全局变量,全局变量只需要在一个源文件中定义,就可以在所有源文件中使用,当然,不包含这个全局变量的源文件需要使用“import”关键字引入全局变量所在的源文件之后才能使用这个全局变量。

全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写

Go语言程序中全局变量与局部变量名称可以相同,但是函数体内的局部变量会被优先考虑。

形式参数

​ 在定义函数时函数名后面括号中的变量叫做形式参数(简称形参)。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值。

​ 形式参数会作为函数的局部变量来使用。

2.2 常量

const

常量值必须是在编译期就可以确定的字符、字符串、数字或布尔值。例如:

lencaprealimagcomplexunsafe.Sizeof
  • iota常量生成器
enum

我们也可以在多个常量定义中使用多个iota,它们各自单独计数,但是需要确保每行常量的列数相同。例如:

我们如果希望中断iota的自增,我们就需要显式恢复。同时如果后续又想使用iota,则后续自增是按照行序递增的,不像C语言的enum那样按上一取值递增。例如:

2.3 命名

这里给一些命名建议:

  • Go的命名区分大小写,并且首字母大小写决定了其作用域。
  • 一般使用驼峰命名法。
  • 局部变量优先使用短名称。
  • 命名一般是由字母或者下划线开始,由多个字母、数字和下划线组合而成。
  • 不要使用保留关键字。
  • 不建议使用与预定义常量、类型、内置函数相同的名字。
  • 专有名词一般全部大写。

2.4 基本类型

类型长度(字节)默认值说明
bool1false布尔类型,真用true表示,假用false表示
byte10字节类型,可以看作是一个8位二进制数表示的无符号整数类型,uint8
int,uint4,80默认整数类型,根据平台32位或64位不同,长度也不同
int8,uint810-128~127,0~255
int16,uint1620-32768~32767,0~65535
int32,uint3240-21亿~21亿,0~42亿
int64,uint6480
float3240.0由32位二进制数表示的浮点数类型
float6480.0由64位二进制数表示的浮点数类型,默认浮点数类型
complex6480.0+0.01由64位二进制数表示的复数类型,float32类型的实部和虚部联合表示
complex128160.0+.0.01由128位二进制数表示的复数类型,float64类型的实部和虚部联合表示
rune40unicode code point, int32
uintptr4,80足以存储指针的uint
string-""字符串,默认为空字符串,而非NULL
array数组
struct结构体
functionnil函数
interfacenil接口
mapnil字典,引用类型
slicenil切片,引用类型
channelnil通道,引用类型
slicemapchannelnewmakemakeint8int16int32int64float32float64IEEE754float32float64float64float32float32complex128complex64complex128RE + IMiREIMREIMfloatiz := complex(x, y)real(z)imag(z)truefalse==<""\n\r\t\u\U:Unicode\\uint8byteruneUTF-8runeruneint32valueOfTypeB = typeB(valueOfTypeA)int16int32boolint


2.5 关键字

关键字即是被Go语言赋予了特殊含义的单词,也可以称为保留字。

Go语言中的关键字一共有 25 个:

breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar

之所以刻意地将Go语言中的关键字保持的这么少,是为了简化在编译过程中的代码解析。和其它语言一样,关键字不能够作标识符使用。

__var 1num intstuNamegetVal

Go语言中还存在着一些特殊的标识符,叫做预定义标识符,如下表所示:

appendboolbytecapclosecomplexcomplex64complex128uint16
copyfalsefloat32float64imagintint8int16uint32
int32int64iotalenmakenewnilpanicuint64
printprintlnrealrecoverstringtrueuintuint8uintptr

预定义标识符一共有 36 个,主要包含Go语言中的基础数据类型和内置函数,这些预定义标识符也不可以当做标识符来使用。

2.6 运算符

运算符是用来在程序运行时执行数学或逻辑运算的,在Go语言中,一个表达式可以包含多个运算符,当表达式中存在多个运算符时,就会遇到优先级的问题,此时应该先处理哪个运算符呢?这个就由Go语言运算符的优先级来决定的。

Go语言有几十种运算符,被分成十几个级别,有的运算符优先级不同,有的运算符优先级相同,请看下表。

优先级分类运算符结合性
1逗号运算符,从左到右
2赋值运算符=、+=、-=、*=、/=、 %=、 >=、 <<=、&=、^=、|=从右到左
3逻辑或||从左到右
4逻辑与&&从左到右
5按位或|从左到右
6按位异或^从左到右
7按位与&从左到右
8相等/不等==、!=从左到右
9关系运算符<、<=、>、>=从左到右
10位移运算符<<、>>从左到右
11加法/减法+、-从左到右
12乘法/除法/取余*(乘号)、/、%从左到右
13单目运算符!、*(指针)、& 、++、--、+(正号)、-(负号)从右到左
14后缀运算符( )、[ ]、->从左到右

注意:优先级值越大,表示优先级越高。

2.7 小结

掌握基本的语法知识是学习一门语言的必经之路,基础的知识只要细细思考和琢磨就会引发出多种多样的思路和问题。我们探索和解决这些问题的过程就是在巩固基础与深入学习,基础打得牢才能事半功倍。

本文参考并借鉴了雨痕大佬的《Go语言学习笔记》,有兴趣的可以去看看这本书。