Go 基础语法总结 | 豆包MarsCode AI刷题

45 阅读9分钟

引言

Go 语言(Golang)是 Google 于 2009 年发布的一门编译型语言,以简洁、高效和内置并发支持为核心特点。它被广泛应用于后端服务开发、微服务架构和分布式系统中,例如 Docker 和 Kubernetes 都是基于 Go 开发的。本文旨在介绍 Golang 的基本语法和常用特性,帮助有编程基础的同学快速入门 Golang 。

一、 基本结构

Go 程序的入口是 main 函数,文件必须以 package 声明开头,并使用 import 引入必要的包。以下是一个经典的 "Hello, World!" 程序:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

要点:

  • package main:指定程序的入口点。
  • import:用于引入标准库或第三方包。
  • func main():程序从这里开始执行。

二、 变量和常量

Go 语言中的变量可以通过两种方式声明:显式声明和隐式声明。

  1. 显式声明

    显式声明使用 var 关键字,明确地定义变量的类型。以下是一个示例:

    var age int = 25
    

    在这个例子中,age 是一个整型变量,赋值为 25。显式声明的优势是代码更加清晰,尤其在处理复杂数据类型时。

    在 Golang 中,变量的类型在变量名之后,这点与很多编程语言不同,需要注意。

  2. 隐式声明

    在某些情况下,Go 语言允许通过短变量声明(:=)的方式自动推导变量的类型。例如:

    name := "Alice"
    

    这里的 name 会被推导为字符串类型。这种方式简化了代码,但需要注意它只能在函数内使用,而不能在包级别的作用域中声明变量。

  3. 默认值

    如果变量声明时没有初始化值,Go 会为其分配一个默认值。例如,整数的默认值是 0,布尔值的默认值是 false,字符串的默认值是空字符串 ""。以下代码展示了默认值的使用:

    var count int
    fmt.Println(count) // 输出 0
    
  4. 多变量声明

    Go 支持同时声明多个变量,这在需要初始化多个相关变量时非常方便:

    var x, y, z int = 1, 2, 3
    a, b := 4, "hello"
    

    这种声明方式提高了代码的可读性和编写效率。


与变量不同,常量在声明后值不能更改。在 Go 语言中,常量使用 const 关键字声明。以下是一个基本示例:

const Pi = 3.14

Pi 是一个常量,表示圆周率。它的值在程序运行过程中是固定的。常量的用途通常包括定义不会改变的配置值,例如数学常数、API 地址等。

  1. 显式类型与隐式类型

    常量既可以显式声明类型,也可以隐式推导。例如:

    const a int = 10
    const b = 20.5
    

    在第一行中,常量 a 明确声明为整型,而第二行中,Go 自动推导 b 为浮点型。

  2. 枚举常量

    Go 提供了一种独特的 iota 关键字,用于生成一组枚举值。iota 的值从 0 开始,每次使用时自动递增。以下是一个示例:

    const (
        Sunday = iota // 0
        Monday        // 1
        Tuesday       // 2
    )
    

    这种方式非常适合定义一组有序的常量,例如表示星期几或状态码。


值得注意的是,为了提高代码的清晰度和维护性,Golang 不允许定义不使用的局部变量,例如

package main

func main() {
    var unusedVar int = 10
}

会提示错误declared and not used: unusedVar

如果实在需要定义变量,可以考虑使用“空标识符” (_)当变量需要声明但不需要使用时,可以将其赋值给 _。例如:

result, _ := someFunction()

_ 是 Go 语言的特殊标识符,表示“我不关心这个值”。编译器会忽略赋值给 _ 的任何值,从而避免未使用变量的报错。

三、 Go 的控制结构与换行要求

Go 语言的控制结构包括 ifswitchfor,与其他编程语言类似,但也有其独特之处。Go 没有 whiledo-while,所有循环均通过 for 实现。此外,Go 对代码换行有严格要求:代码块必须与控制结构同行或换行后用大括号紧随其后,否则会报错。这使得 Go 的代码风格更加一致。


1. if 语句

Go 的 if 不需要括号包裹条件表达式,且可以在条件前执行一个短变量声明。

示例代码:

    num := 10

    // 标准写法
    if num%2 == 0 {
        fmt.Println("Even number")
    } else {
        fmt.Println("Odd number")
    }

    // 带短变量声明
    if result := num % 3; result == 0 {
        fmt.Println("Divisible by 3")
    } else {
        fmt.Println("Not divisible by 3")
    }

2. switch 语句

Go 的 switch 默认不需要 break,每个 case 块会自动终止执行。如果需要继续执行下一个 case,可以使用 fallthrough

示例代码:

    day := 3
    switch day {
    case 1:
        fmt.Println("Monday")
    case 2:
        fmt.Println("Tuesday")
    default:
        fmt.Println("Other day")

3. for 循环

Go 的 for 是唯一的循环结构,可以实现传统的 for 循环、无限循环和基于范围的循环。

示例代码:

    // 标准 for 循环
    for i := 0; i < 5; i++ {
        fmt.Println("i:", i)
    }

    // 无限循环
    count := 0
    for {
        if count == 3 {
            break
        }
        fmt.Println("Count:", count)
        count++
    }

    // 基于范围的循环
    arr := []int{1, 2, 3, 4}
    for index, value := range arr {
        fmt.Printf("Index: %d, Value: %d\n", index, value)
    }

四、 Go 的数据类型

Go 是强类型语言,提供了丰富的基本数据类型和复合数据类型,能够满足从简单到复杂的各种编程需求。所有变量的类型在编译时需要明确,类型之间不能隐式转换。以下是常见数据类型的分类和使用示例。


1. 基本数据类型

  • 布尔类型bool,只能取值 truefalse
  • 数值类型
    • 整数类型:包括有符号整数(intint8, int16, int32, int64)和无符号整数(uintuint8, uint16, uint32, uint64)。
    • 浮点数类型:float32float64
    • 复数类型:complex64complex128
  • 字符串类型string,一旦声明内容不可变。

2. 复合数据类型

  • 数组:固定长度的同类型元素集合,声明后长度不可更改。var arr [3]int = [3]int{1, 2, 3}
  • 切片:动态大小的数组视图,更常用。slice := []int{4, 5, 6}
  • 映射(Map):键值对的集合,键必须是可比较的类型。myMap := map[string]int{"Alice": 25, "Bob": 30}
  • 结构体(Struct):自定义类型,可以包含多个字段。p := Person{Name: "John", Age: 28}

3. 特殊类型

  • 指针:保存变量的内存地址。var ptr *int = &x
  • 接口:定义方法的集合,用于实现多态。var anything interface{} = "I can be anything"
  • 函数类型:可以将函数作为变量或参数传递。add := func(a, b int) int { return a + b }
  • 空值类型nil 表示未初始化的指针、切片、映射、接口等。

五、函数和方法

函数是独立的逻辑单元,既可以接收输入参数,也可以返回结果。Go 的函数支持多个返回值,还可以使用匿名函数和闭包。

函数的基本形式如下所示:

func 函数名(参数名 参数类型, ...) 返回值类型 {
    // 函数体
    return 返回值
}

此外,Golang还支持匿名函数和闭包,以实现更复杂的功能。

方法是附加到特定类型上的函数,通常用于对结构体或自定义类型的操作。方法可以使用值接收器或指针接收器:

方法的基本形式如下所示:

func (接收器 结构体类型或指针类型) 方法名(参数) 返回值类型 {
    // 方法体
}

由于使用值接收器需要创建对象的副本,在生产过程中,一般使用指针接收器。

六、 Goroutine 和管道

Go 语言以高效的并发能力而闻名,Goroutine管道 是其并发模型的核心组件。它们结合实现了轻量级、高效的并发编程。


1. Goroutine

Goroutine 是 Go 的轻量级线程,每个 Goroutine 都是由 Go 运行时调度的。相比系统线程,Goroutine 的创建和切换开销非常低,可以同时运行数千个 Goroutine,每个 Goroutine 独立执行,都有自己的执行上下文。 要想启动Goroutine,只需使用 go 函数调用()的形式即可。

2. 管道

管道 是 Goroutine 之间进行通信的机制,用于在它们之间安全地传递数据。Channel 遵循 "通过通信共享内存,而非通过共享内存通信" 的理念。

Goroutine 和管道常结合使用,实现多个并发任务之间的通信和同步。

package main

import "fmt"

// 计算平方并发送到管道
func square(nums []int, ch chan int) {
    for _, num := range nums {
        ch <- num * num
    }
    close(ch)
}

func main() {
    nums := []int{1, 2, 3, 4, 5}
    ch := make(chan int)

    // 启动 Goroutine
    go square(nums, ch)

    // 从管道接收数据
    for result := range ch {
        fmt.Println(result)
    }
}

在程序中,square 函数接收一个整数切片 nums 和一个管道 ch,计算每个数字的平方并将结果发送到管道中。主函数启动一个 Goroutine 来并行执行 square 函数,同时创建了一个无缓冲的管道 ch 用于接收计算结果。

总结

Go 语言与其他编程语言有许多相似之处,例如变量声明、函数定义和常见的控制结构(如 if、for、switch)等,它的语法和许多语言如 C、Python 等类似。但与此同时,Go 也有一些独特的特点,例如类型后置于变量名,严格的换行和变量声明要求等,这在其他语言中并不常见。

与传统语言相比,Go 的并发编程机制是其最为突出的特点。Go 通过 Goroutine 和 管道 提供了一种轻量级且高效的并发编程模型。Goroutine 可以并发执行多个任务,而管道则作为它们之间通信的桥梁,使得数据传递更加安全、简便。与其他语言的线程机制相比,Go 的 Goroutine 在启动和切换上有着更低的开销,这使得它特别适合用于高并发、并行计算的应用场景,如 Web 服务、分布式系统等。