Go语言简介
约 2056 字大约 7 分钟
2025-03-27
1.Go语言简介
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。
特点:
- 简洁、快速、安全
- 并行、有趣、开源
- 内存管理、数组安全、编译迅速
2.环境安装
安装包下载地址:https://go.dev/dl/
Windows 环境下载 .msi 后缀的安装包来安装。默认情况下 .msi 文件会安装在 c:\Go 目录下。默认会将 c:\Go\bin 目录添加到 Path 环境变量中。
安装测试:
创建工作目录,创建 test.go 测试文件
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
使用 go run 命令执行以上代码输出结果如下:
3.基本数据类型
分类 | 描述 | |
---|---|---|
布尔型 | var b bool = true。 | |
数字类型 | uint8、uint16、uint32、uint64 | 无符号整型 |
int8、int16、int32、int64 | 有符号整型 | |
float32、float54 | 浮点型 | |
complex64、complex128 | 32、64位实数和虚数 | |
字符串类型 | Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 | |
派生类型 | 指针类型(Pointer) | |
数组类型 | ||
结构化类型(struct) | ||
Channel 类型 | ||
函数类型 | ||
切片类型 | ||
接口类型(interface) | ||
Map 类型 |
4.变量
声明变量的一般形式是使用 var 关键字:
// 声明一个变量
var 变量名 变量类型
// 一次声明多个变量
var 变量名1, 变量名2 type
初始化问题
// 1:没有初始化则为零值,即默认值
var b int
fmt.Print(b) # 为0
// 2:根据值自行判断变量类型
var b = true
值类型和引用类型
值类型:值类型的变量直接指向存在内存中的值,类型有:int、float、bool、string
引用类型:引用类型的变量指向内存中值的内存地址。通过 &i 来获取变量 i 的内存地址,内存地址通常称为指针
5.常量
常量是在程序运行时,不会被修改的量,使用 const 关键字修饰
const c_name [type] = value
const c_name1, c_name2 = value1, value2
6.数组
声明数组格式:
var arrayName [size]dataType
其中,arrayName 是数组的名称,size 是数组的大小,dataType 是数组中元素的数据类型
例子,
var arr [10]float32
初始化数组格式:
// 默认初始化
var numbers [5]int
// 初始化值
var numbers = [5]int{1, 2, 3, 4, 5}
7.指针
指针:指向一个值的内存地址的变量成为指针
// var_name:指针名 var-type:指针类型
// * 号用于指定变量是作为一个指针
var var_name *var-type
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
使用指针的步骤:
- 定义指针变量
- 为指针变量赋值
- 访问指针变量中指向地址的值
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
Go 空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil,也称为空指针。
8.Map集合
定义Map:使用内置函数 make 或 map 关键字
内置函数 make
// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
关键字 map
// 使用字面量创建 Map
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
- 获取元素
// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
- 修改元素
// 修改键值对
m["apple"] = 5
- 获取 Map 长度
// 获取 Map 的长度
len := len(m)
- 遍历 Map
// 遍历 Map
for k, v := range m {
fmt.Printf("key=%s, value=%d\n", k, v)
}
- 删除元素
// 删除键值对
delete(m, "banana")
9.问题
new 和 make 的区别
new | make | |
---|---|---|
用途 | 用于分配类型的零值(零初始化)内存 | 用于引用类型的初始化(切片slice、映射map、通道channel) |
返回 | 该类型的指针 | 已经初始化的引用类型,不是指针 |
分配内存,但不初始化该内存中的内容 | 分配内存,并初始化底层数据结构 |
- new 举例:
var p *int
p = new(int) // 分配内存并返回一个指向 int 类型的指针
fmt.Println(*p) // 输出 0,因为 int 的零值是 0
- make 举例:
// 切片
s := make([]int, 5) // 创建一个长度为 5 的切片,元素的默认值为 0
fmt.Println(s) // 输出 [0 0 0 0 0]
// 映射
m := make(map[string]int) // 创建一个空映射
m["key"] = 10
fmt.Println(m) // 输出 map[key:10]
// 通道
ch := make(chan int, 2) // 创建一个缓冲通道,容量为 2
ch <- 1
ch <- 2
fmt.Println(<-ch) // 输出 1
值传递 和 地址传递 的区别
- 值传递:
- 传递的是变量的副本,函数内部修改副本的值,不会影响原始变量。
- 用于基本类型(如
int
、float
、bool
等)或结构体(struct
)较小、无需修改原始数据的场景
- 地址传递:
- 传递的是变量的地址,函数通过指针修改原始数据的值。
- 用于较大的结构体、数组
数组 和 切片 的区别?
特点 | 数组 | 切片 |
---|---|---|
长度 | 固定长度,在声明时确定,不能更改 | 动态长度,可以增加或缩小 |
类型 | 值类型(传递数组时是值拷贝) | 引用类型(传递切片时是引用传递) |
内存分配 | 数组是连续的内存块,大小固定,无法扩展 | 切片是对底层数组的引用,可以引用不同大小的底层数组 |
修改 | 修改数组元素会影响原始数组,除非是通过切片传递的副本 | 切片是引用类型,修改切片会直接影响原始底层数组 |
创建方式 | 通过 [n]T 声明固定大小的数组 | 通过 []T 创建动态大小的切片,或者通过 make 创建切片 |
容量 | 数组的容量就是它的长度,固定 | 切片有容量(cap)和长度(len)之分,容量大于等于长度 |
例子:
- 数组
package main
import "fmt"
func main() {
var arr [3]int = [3]int{1, 2, 3}
fmt.Println("数组:", arr)
arr[0] = 100 // 修改数组元素
fmt.Println("修改后的数组:", arr)
}
- 切片:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
fmt.Println("切片:", slice)
slice[0] = 100 // 修改切片元素
fmt.Println("修改后的切片:", slice)
}
defer 执行顺序?
- 延迟执行:
defer
声明的语句不会在defer
语句本身所在的地方立即执行,而是会等到包含它的函数执行完毕、即将返回时才执行。 - 后进先出(LIFO)顺序: 当函数中有多个
defer
语句时,这些语句的执行顺序是后进先出(LIFO,Last In, First Out)的。也就是说,最后声明的defer
语句会最先执行。 defer
语句的参数是立即计算的:defer
语句中的参数(例如函数调用中的值或变量)会在defer
声明时计算,而不是在defer
执行时计算。这意味着,即使在函数中修改了这些参数的值,defer
语句中已经计算过的值不会受到影响。
例子:
package main
import "fmt"
func testDefer() {
fmt.Println("Start of testDefer function")
defer fmt.Println("This is deferred 1")
defer fmt.Println("This is deferred 2")
defer fmt.Println("This is deferred 3")
fmt.Println("End of testDefer function")
}
func main() {
testDefer()
}
输出结果:
Start of testDefer function
End of testDefer function
This is deferred 3
This is deferred 2
This is deferred 1