1. 变量
1.1 变量的定义
在 Golang 中,定义变量的方式与传统的 C、Java 等语言不同。在 Golang 中,定义变量需要使用 var 关键字,并且类型声明在变量名的后面。例如:
go
复制代码
var a int
var b float32
var c, d float64
在上述代码中,a 被定义为 int 类型,b 被定义为 float32 类型,而 c 和 d 都是 float64 类型。值得注意的是,Golang 允许在一行中定义多个变量,极大地简化了代码的书写。
此外,还可以使用 := 符号来定义并初始化变量。使用 := 时,可以省略 var 关键字,编译器会自动推导变量的类型,例如:
go
复制代码
e, f := 9, 10
var g = "Ricardo"
其中,e 和 f 被推导为 int 类型,而 g 被推导为 string 类型。需要注意的是,Golang 是一种强类型语言,所有变量都必须拥有确定的类型,且变量只能存储与其类型匹配的数据。
1.2 匿名变量
在 Golang 中,使用下划线 _ 作为变量名的是匿名变量。匿名变量是一种特殊的变量,它可以占位但不会保存实际的值。通常用于函数返回多个值但不需要所有值的场景。例如:
go
复制代码
func main() {
_, v, _ := getData()
fmt.Println(v)
}
func getData() (int, int, int) {
return 2, 4, 8
}
在这个例子中,getData 函数返回三个值,但只需要其中的第二个值。通过 _ 占位,不用定义额外的变量来存储不需要的返回值。匿名变量非常适合处理多返回值的场景,可以简化代码,减少无用变量的定义。
1.3 常量
在 Golang 中,常量使用 const 关键字定义,例如:
go
复制代码
const pi = 3.14159
常量的值在定义后不能被修改,且在定义常量时,不能使用 := 赋值操作符。常量通常用于定义程序中的固定值,如数学常数或应用程序的配置参数。
2. 判断语句
在 Java 或 C 中,if 语句通常需要小括号包裹判断条件,而在 Golang 中,if 语句不需要小括号,例如:
go
复制代码
if x > 0 {
fmt.Println("x is positive")
}
Golang 的 if 语句还支持在判断条件前执行一个简单的语句,例如:
go
复制代码
if v := math.Pow(x, n); v < lim {
return v
}
在这里,v 在 if 语句中定义并赋值,其作用域仅限于当前 if 块。
3. 循环
在 Golang 中,for 是唯一的循环结构。for 循环非常灵活,可以实现各种循环模式,包括传统的 for 循环、while 循环和无限循环。与其他语言不同,Golang 的 for 循环不需要小括号。
3.1 基本的 for 循环
最常见的 for 循环形式与其他语言相似,可以包含初始化语句、条件表达式和后置语句:
go
复制代码
for i := 0; i < 10; i++ {
fmt.Println(i)
}
在这个循环中,i 从 0 开始,逐渐增加到 9,循环体每次打印 i 的值。
3.2 省略初始化和后置语句
for 循环的初始化语句和后置语句都是可选的。如果省略它们,for 循环就类似于 while 循环:
go
复制代码
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum) // 输出:1024
在这个示例中,只有条件表达式 sum < 1000 控制循环,循环会持续执行,直到 sum 不小于 1000。
3.3 无限循环
如果完全省略循环条件,for 循环就会变成一个无限循环。这样的循环在编写服务程序时很常见,相当于 while(true):
go
复制代码
for {
// 无限循环
fmt.Println("Running...")
}
这种循环会不断执行,除非在循环体内使用 break 或 return 语句来跳出循环。
3.4 for 循环遍历切片和数组
Golang 提供了 for ... range 语法来遍历数组和切片,非常便捷。range 返回两个值,第一个是索引,第二个是当前索引对应的值:
go
复制代码
slice := []int{1, 2, 3, 4, 5}
for index, value := range slice {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
在这个例子中,每次循环都会打印当前元素的索引和值。如果只需要值而不需要索引,可以使用匿名变量 _ 占位:
go
复制代码
for _, value := range slice {
fmt.Println(value)
}
3.5 for 循环控制语句
-
break:break语句用于立即跳出当前的for循环。go 复制代码 for i := 0; i < 10; i++ { if i == 5 { break } fmt.Println(i) } // 输出 0 到 4,然后退出循环 -
continue:continue语句用于跳过当前循环的剩余代码,并进入下一次循环。go 复制代码 for i := 0; i < 10; i++ { if i%2 == 0 { continue } fmt.Println(i) } // 输出奇数 1, 3, 5, 7, 9
4. 函数
4.1 函数的定义
在 Golang 中,所有函数都以 func 关键字开头,并使用驼峰命名法。Golang 的函数参数类型和返回类型都写在参数名或函数名之后。函数可以接收多个参数和返回多个值,例如:
go
复制代码
func add(x, y int) int {
return x + y
}
当函数的多个参数具有相同类型时,可以只在最后一个参数后写类型。Golang 还支持返回多个值,常用于返回结果和错误信息。例如:
go
复制代码
func swap(x, y string) (string, string) {
return y, x
}
4.2 defer
defer 语句用于延迟函数的执行,直到外层函数返回。defer 常用于释放资源,例如关闭文件或数据库连接。defer 的执行顺序是后进先出(LIFO),类似于栈。例如:
go
复制代码
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
在这段代码中,先打印 hello,再打印 world。defer 提供了类似于 finally 的功能,可以在函数返回后执行必要的清理工作。
5. 指针
Golang 支持指针,用 & 符号获取变量地址,用 * 访问指针指向的值。例如:
go
复制代码
var p *int
p = &a
fmt.Println(*p) // 通过指针 p 访问 a 的值
与 C 不同,Golang 没有指针运算,简化了指针的使用。
6. 数组与切片
6.1 数组
Golang 中的数组大小固定,定义后不能改变。例如:
go
复制代码
var a [10]int
数组是值类型,传递时会进行拷贝,操作不会影响原数组。
6.2 切片
在 Golang 中,切片(Slice)是比数组更常用的动态数据结构。切片类似于数组,但更灵活,其长度可以动态调整,非常适合需要频繁增减元素的场景。切片与数组的不同之处在于,切片不存储实际数据,而是指向底层的数组部分。
6.2.1 切片的创建方式
-
基于数组创建切片 切片可以基于数组创建,指定开始和结束位置(半开区间),例如:
go 复制代码 var arr = [5]int{1, 2, 3, 4, 5} var slice = arr[1:4] // 创建一个切片,包含 arr 的第 1 到第 3 个元素 fmt.Println(slice) // 输出:[2 3 4]在这个例子中,
slice是一个切片,引用了数组arr中的部分元素(下标从 1 到 3)。需要注意的是,切片不会复制数组中的数据,而是指向数组的相应区域,因此对切片的修改会影响原数组。 -
使用
make创建切片 Golang 提供了make函数来动态创建切片,指定切片的长度和容量:go 复制代码 slice := make([]int, 3, 5) // 创建一个长度为 3,容量为 5 的切片 fmt.Println(slice) // 输出:[0 0 0] fmt.Println(len(slice)) // 输出:3 fmt.Println(cap(slice)) // 输出:5使用
make创建的切片可以在容量范围内动态扩展,非常适合需要调整长度的场景。 -
直接定义切片 切片还可以通过字面量的方式直接定义:
go 复制代码 slice := []int{1, 2, 3, 4, 5} fmt.Println(slice) // 输出:[1 2 3 4 5]
6.2.2 切片的特性
-
长度与容量
- 长度(
len) :切片的当前长度,即可以访问的元素数量。 - 容量(
cap) :切片的底层数组容量,表示切片在不重新分配内存的情况下可以扩展的最大长度。 - 切片的长度可以通过
len函数获取,容量可以通过cap函数获取。
- 长度(
-
动态扩展
- 切片可以动态扩展,通过
append函数向切片追加元素。当切片的长度超过容量时,Golang 会自动分配一个新的数组,并将原来的数据复制到新数组中,原切片将指向这个新的底层数组。
go 复制代码 var slice []int slice = append(slice, 1, 2, 3) fmt.Println(slice) // 输出:[1 2 3] - 切片可以动态扩展,通过
-
引用底层数组
- 切片是引用类型,多个切片可以共享同一个底层数组。修改切片的元素会影响到其他引用同一底层数组的切片。
go 复制代码 arr := [5]int{1, 2, 3, 4, 5} slice1 := arr[1:3] slice2 := arr[2:4] slice2[0] = 99 fmt.Println(slice1) // 输出:[2 99] fmt.Println(arr) // 输出:[1 2 99 4 5] -
内置函数
copy- 如果需要复制切片的内容而不共享底层数组,可以使用
copy函数。copy函数会将一个切片的数据复制到另一个切片中,从而实现数据的独立。
go 复制代码 slice1 := []int{1, 2, 3} slice2 := make([]int, len(slice1)) copy(slice2, slice1) slice2[0] = 99 fmt.Println(slice1) // 输出:[1 2 3] fmt.Println(slice2) // 输出:[99 2 3] - 如果需要复制切片的内容而不共享底层数组,可以使用