<Go>1 Go 基础知识
本文最后更新于:2023年6月27日 上午
1 Go 基础知识
Google 为什么要创造 Go 语言:
- 计算机硬件技术更新频繁,性能提高很快。但目前主流编程语言发展明显落后于硬件,不能合理利用多核多 CPU 的优势来提示软件系统性能
- 软件系统复杂度越来越高,维护成本也越来越高。目前缺乏一个足够简洁高效的编程语言
- c / c++ 程序运行速度虽然很快,但编译速度很慢。同时存在内存泄漏等一系列困扰。
Golang 标准库 API 文档,或者也可以试试 这个网址
#Go 语言发展史:
- 2007 年,Google 开始设计一门全新的语言,这是 Go 语言的原型
- 2009 年 11 月 10 日,Google 将 Go 语言以开放源代码的方式向全球发布
- 2015 年 8 月 19 日,Go 1.5 版发布。本次更新中 “移除了最后残余的 c 代码”
- 2017 年 2 月 17 日,Go 语言 Go 1.8 版发布
- 2017 年 8 月 24 日,Go 语言 Go 1.9 版发布
- 2018 年 2 月 16 日,Go 语言 Go 1.10 版发布
- 目前的最新版本是 Go 1.18
#Go 语言的特点:
Go 语言既能到达静态编译语言的安全和性能,又达到了动态语言开发维护的高效率。
- Go 从 c 语言中继承了很多概念,包括表达式语法,控制结构。基础数据模型。调用参数传值,指针等。也保留了和 c 语言一样的编译执行方式和弱化的指针。
- 引入包的概念,用于组织程序结构。Go 语言的每一个文件都要归属于一个包,而不能单独存在。
- 垃圾回收机制,内存自动回收,不需要开发人员管理
- 天然并发:
- 从语言层面支持并发,实现简单。
- goroutine,轻量级线程。可以实现大并发处理,高效利用多核
- 基于 CPS 并发模型(Communicating Sequential Processes)实现
- 吸收了管道通信机制,形成 Go 语言特有的管道 channel。通过管道可以实现不同的 goroutine 之间的相互通信
- 函数可以返回多个值
- 新的创新:如切片 slice、延时执行 defer 等
1.1 Go 执行流程
通过编译器,将 Go 源文件编译成机器能识别的二进制码文件的过程称为 编译。
在文件目录下使用 go build 指令以编译文件。此时,可以指定生成的文件名
-
先编译,再执行(
[ ]
内的内容能省略)******>go build [-o 可执行文件名] 源文件名 ******>可执行文件名
graph LR A(.go 文件)--go build 编译-->B(可执行文件)--运行-->C(结果)
-
直接对源码执行 go run 命令
******>go run 源文件名
graph LR A(.go 文件)--go run 编译运行一步-->C(结果)
两种执行方式的区别:
-
编译可执行文件后,将该文件放到没有 Go 开发环境的设备上仍能运行
使用 go run 运行文件则需要 Go 开发环境
-
编译时,编译器会将程序运行依赖的库文件包含在可执行文件中。因此,可执行文件占用空间较大。
1.2 Go 开发注意事项
-
Go 源文件以 .go 为扩展名
-
Go 应用程序的执行入口是 main() 方法
-
Go 语言严格区分大小写
-
Go 方法由一条条语句构成,每个语句后可以不加分号(Go 语言会在每行后自动添加)
-
Go 编译器是逐行翻译的。因此,不能将多条语句写在同一行
-
Go 语言定义的变量或 import 的包如果没有用到,就不能编译通过
-
可以使用 gofmt 指令来对代码进行整理
******>gofmt -w 源文件名
1.3 注释
用于注解说明解释程序的文字就是注释。注释提高了代码阅读性。被注释的文字不会被 Go 编译器执行。
Go 语言中注释有两种形式:单行注释和多行注释
// 单行注释
/* 多行
注释 */
注释不能嵌套注释。Go 官方推荐使用单行注释来注释整个方法和语句
1.4 变量
变量是程序的基本组成单位,相当于内存中一个数据存储空间的表示。该存储区域有自己的名称(变量名)和类型(数据类型)
Go 语言中变量的声明有以下形式:
-
指定变量类型。声明后不赋值的场合,使用默认值
var i int // var 变量名 数据类型 i = 100 // 赋值
-
根据赋值自行判断数据类型(类型推导)
var i = 10 // var 变量名 = 值
-
省略
var
,使用:=
i := 20 // 变量名 := 值
-
一次性声明多个变量
v1, v2, v3 := 100, "Go", 3.2
在函数体外声明的变量称为全局变量:
var n1 = 100 // 全局变量
var (
n2 = 200 // 另一种全局变量的声明方式
n3 = 300
)
func main() {
...
}
使用细节:
-
不能使用
:=
方式声明全局变量因为
:=
等同于一个 变量声明 + 赋值语句。在函数体外可以声明变量,但不能执行语句(也就不能执行赋值语句) -
全局变量即使没有使用过,也能编译通过
1.4.1 常量
常量是变量的一种。使用 const 而非 var 修饰的变量称为常量。
const ci int = 100
const c2 = 1000 // 也能直接省略那个类型名
const c3 = ci - 5
在函数体外声明的常量称为全局常量
const (
i = 100
ui uint64 = 9
)
使用细节:
-
常量必须在声明时即赋值
-
常量赋值后不能更改。
-
常量只能修饰 bool、数值类型、string 类型。
-
常量即使没有使用过,也能编译通过
-
常量必须在编译阶段确定数值。可以使用表达式对常量赋值,但表达式中 不能含有任何变量
-
可以使用 iota 对常量进行序号赋值
const ( o0 = 100 // 100 o1, o11 = iota, iota // 1 1 o2 = iota // 2 o3 // 3 )
届时,iota 项及其之后未赋值的项的值,等于其声明位置的编号(从 0 开始计数)的值
iota 只能对常量声明块中的常量进行赋值
-
在 Go 语言中,不要求常量首字母大写。首字母的大小写是决定标识符的访问范围。
1.5 数据类型
每一种数据都定义了明确的数据类型,在内存中分配了不同大小的空间
- 基本数据类型
- 数值型
- 整数类型(int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, rune, byte)
- 浮点类型(float32, float64)
- 字符型(没有专门的字符型,使用 byte 保存单个字符)
- 布尔型(bool)
- 字符串(string)
- 派生 / 复杂数据类型
- 指针(Pointer)
- 数组
- 结构体(struct)
- 管道(Channel)
- 函数
- 切片(slice)
- 接口(interface)
- map
1.5.1 整数类型
用于存放一个整数数值
uint 为无符号整数,int 为有符号整数。
数字为占用空间大小。int8 占用 1 字节,int16 占用 2 字节,以此类推。
表数范围由其符号有无及占用空间决定。int8 表数范围是 [-128, 127],int32 表数范围是 [-231, 231 - 1],uint8 表数范围是 [0, 255]
rune 等价于 int32,其可以表示一个 Unicode 码
byte 等价于 uint8。存储字符时,选用 byte
整数类型的默认值(零值)是 0
#使用细节
- Go 各整数分为有符号和无符号。Go 中的 int、uint 的大小和操作系统有关。
- Go 的整型默认声明为 int 型
- 使用
unsafe.Sizeof(n)
函数,以查看变量占用空间的大小(字节) - 使用整型变量时,应在保证程序正确运行的情况下,尽量使用占用空间小的数据类型
1.5.2 浮点类型
用于存放一个小数数值
数字为精度大小。float32 为单精度(32 位,占用 4 字节),float64 为双精度(64 位,占用 8 字节)
浮点类型的默认值(零值)是 0
#使用细节
- Go 的浮点型默认声明为 float64 类型
- 浮点数在计算机中的存放方式:浮点数 = 符号位 + 指数位 + 尾数位
- 尾数部分可能丢失,就会造成精度丢失。
1.5.3 字符类型
Go 没有专门的字符型,使用 byte 保存单个字符。Go 的字符串是由字节连接起来的。
字符常量是单引号 ‘ ’
包括的单个字符。Go 运行使用转义字符
#使用细节
-
如果字符在 ASCII 表中,可以直接保存到 byte(或者 rune)
-
如果字符码值大于 255,可以使用 int 保存
-
Go 中,字符的本质是一个整数。直接输出时,输出该字符的 UTF-8 码值
要按照字符方法输出时,应该使用格式化输出
fmt.Printf()
var i int = '★' fmt.Printf("%c", i)
-
Go 语言的字符使用 UTF-8 编码。
-
字符类型可以进行运算,因为其相当于一个整数。
1.5.4 布尔类型
布尔类型用于表示真或假。布尔类型只允许取 true 或 false
bool 类型占 1 字节。其适用于逻辑运算,一般用于程序流程控制
布尔类型的默认值(零值)是 false
1.5.5 字符串类型
Go 语言将字符串也视为基本数据类型
字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串由单个字节连接
字符串的默认值(零值)是空串 “”
字符串有 2 种表示形式
-
双引号包含字符串。此时会识别转义字符
var str1 = "放牛的"
-
反引号包含字符串。此时将以字符串的原生形式输出,包含换行和特殊字符。
var str2 = `矮堇瓜`
这样能实现防止攻击、输出原代码等效果
#使用细节
-
Go 使用 UTF-8 编码标识 Unicode 文本,这样 Go 统一使用 UTF-8 编码,再也不会发生乱码问题。
-
字符串一旦赋值即不能更改。在 Go 中,字符串是不可变的
-
当
+
运算符两侧都是字符串时,Go 会将其解析为字符串拼接一次拼接太长时,能分行编写,但需要把那个
+
运算符留在行尾var str = "马冬什么?\n" + "马冬梅!\n" + "马什么梅啊?\n" + "马冬梅啊!\n" + "什么冬梅啊?\n" + "行,大爷你坐这吧\n" + "好嘞"
这是因为 Go 会自动为每行代码添加
;
。看到有+
,他就懂了!
1.5.6 基本数据类型的转换
Go 中的基本数据类型 不能自动转换,不同变量间赋值时需要显式转换
var d float64 = 10
var i int = int(d) // 数据类型(变量)
#使用细节
-
数据类型既可以从小到大转换,也能从大到小转换
-
被转换的是变量存储的数值。变量本身没有变化
-
从大到小转换时,可能产生数据溢出
var n int32 = 12 var i int8 = int8(n) + 127 // 相当于 int8(n + 127) 结果会溢出 /* var i2 int8 = int8(n) + 128 */ // 编译错误,因为检测到 128 超出范围
1.5.7 基本数据类型与 string 的转换
基本数据类型转成 string:
-
使用
fmt.Sprintf("%参数", 表达式)
var n = 100 var c rune = '★' str := fmt.Sprintf("%c刻晴%c%v%c", c, c, n, c) // ★刻晴★100★
-
使用 strconv 包的函数
var n int = 100 str := strconv.FormatInt(int64(n), 2) // 转化为 2 进制 1100100 str2 := strconv.Itoa(n)
string 转成基本数据类型:
-
使用 strconv 包的函数
str := "100" var n int64 n, _ = strconv.ParseInt(str, 8, 10) // 该函数有多个返回值,_ 即忽略第二个
此时,要确保那个 string 可以转化成有效数据。不能转换时,那个返回值是默认值
1.6 值类型和引用类型
-
值类型:包括基本数据类型、数组、struct 结构体。
对于值类型,其变量直接存储值,内存通常在栈中分配
-
引用类型:包括指针、slice 切片、map、管道 chan、interface 等
引用类型变量存储一个地址,该地址指向的空间真正的存储数据。内存通常在堆上分配。
当没有任何变量引用该地址时,该地址对应的数据空间成为垃圾,由 GC 回收
1.6.1 指针
值类型都有对应的指针类型。
- 声明指针:
指针变量名 *数据类型
- 记录地址:
指针变量名 = &变量名
- 使用指针:
*指针变量名
var i int = 10;
var pointer *int = &i // 指针
fmt.Println(pointer) // 输出一个地址
fmt.Println(*pointer) // 输出 10
1.7 标识符
Go 对各种变量、方法等命名时使用的字符序列称为标识符。凡能自己起名字的地方都叫标识符
#命名规则:
- 标识符由 26 个大小写英文字母、数字、下划线组成
- 数字不能作为标识符开头
- Go 中严格区分大小写
- 标识符不能包含空格
- 下划线
_
在 Go 中是一个特殊的标识符,称为 空标识符。它可以代表其他任何标识符,但其对应的值会被忽略(如,忽略一个返回值)。空标识符只能作为占位符使用,而不能作为标识符使用。 - 不能以系统保留关键字作为标识符,如 break、if 等
- 如果变量、函数、常量名首字母大写,则其可以跨包访问。否则仅限于本包访问
#命名规范:
- 命名包名时:保持 package 名字与目录名保持一致,并使包名简短、有意义,且不和标准库冲突。
- 命名变量、函数、常量时,采用驼峰法命名(即:单词字母小写,第二个单词起首字母大写)
#Go 的保留关键字和预定义标识符:
Go 为了简化代码编译过程中对代码的解析,其保留关键字只有 25 个:
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
其还提供了 36 个预定义标识符,包括基础数据类型和系统内嵌函数:
append | bool | byte | cap | close | complex |
complex64 | complex128 | uint16 | copy | flase | float32 |
float64 | imag | int | int8 | int16 | int32 |
uint32 | int64 | iota | len | make | new |
nil | panic | uint64 | println | real | |
recover | string | true | uint | uint8 | uintprt |