GO 语法 | 豆包MarsCode AI刷题

101 阅读8分钟

1. 变量

1.1 变量的定义

在 Golang 中,定义变量的方式与传统的 C、Java 等语言不同。在 Golang 中,定义变量需要使用 var 关键字,并且类型声明在变量名的后面。例如:

go
复制代码
var a int
var b float32
var c, d float64

在上述代码中,a 被定义为 int 类型,b 被定义为 float32 类型,而 cd 都是 float64 类型。值得注意的是,Golang 允许在一行中定义多个变量,极大地简化了代码的书写。

此外,还可以使用 := 符号来定义并初始化变量。使用 := 时,可以省略 var 关键字,编译器会自动推导变量的类型,例如:

go
复制代码
e, f := 9, 10
var g = "Ricardo"

其中,ef 被推导为 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
}

在这里,vif 语句中定义并赋值,其作用域仅限于当前 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...")
}

这种循环会不断执行,除非在循环体内使用 breakreturn 语句来跳出循环。

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 循环控制语句
  1. breakbreak 语句用于立即跳出当前的 for 循环。

    go
    复制代码
    for i := 0; i < 10; i++ {
        if i == 5 {
            break
        }
        fmt.Println(i)
    }
    // 输出 04,然后退出循环
    
  2. continuecontinue 语句用于跳过当前循环的剩余代码,并进入下一次循环。

    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,再打印 worlddefer 提供了类似于 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 切片的创建方式
  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)。需要注意的是,切片不会复制数组中的数据,而是指向数组的相应区域,因此对切片的修改会影响原数组。

  2. 使用 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 创建的切片可以在容量范围内动态扩展,非常适合需要调整长度的场景。

  3. 直接定义切片 切片还可以通过字面量的方式直接定义:

    go
    复制代码
    slice := []int{1, 2, 3, 4, 5}
    fmt.Println(slice)  // 输出:[1 2 3 4 5]
    
6.2.2 切片的特性
  1. 长度与容量

    • 长度(len :切片的当前长度,即可以访问的元素数量。
    • 容量(cap :切片的底层数组容量,表示切片在不重新分配内存的情况下可以扩展的最大长度。
    • 切片的长度可以通过 len 函数获取,容量可以通过 cap 函数获取。
  2. 动态扩展

    • 切片可以动态扩展,通过 append 函数向切片追加元素。当切片的长度超过容量时,Golang 会自动分配一个新的数组,并将原来的数据复制到新数组中,原切片将指向这个新的底层数组。
    go
    复制代码
    var slice []int
    slice = append(slice, 1, 2, 3)
    fmt.Println(slice)  // 输出:[1 2 3]
    
  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]
    
  4. 内置函数 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]