Contents

Go语言基础语法快速入门

Go语言基础语法快速入门:开启你的Go编程之旅

Go(又称Golang)是Google开发的一种开源编程语言,以其简洁、高效和强大的并发处理能力而备受开发者青睐。无论你是编程新手,还是希望扩展技能树的资深开发者,Go语言都是一个绝佳的选择。

这篇博客将带你快速掌握Go语言的基础语法,为你后续的深入学习打下坚实的基础。

1. 万物之始:Hello, World

让我们从每个程序员都熟悉的Hello, World开始,一窥Go程序的基本结构。

package main

import "fmt"

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

这段代码虽短,却包含了Go语言的核心组件:

  • package main: 每个Go程序都由包(package)组成。package main定义了该文件属于main包,表明它是一个可独立执行的程序。
  • import "fmt": import关键字用于导入其他包。fmt是Go语言的标准库之一,提供了格式化的输入输出功能,例如打印到控制台。
  • func main() { ... }: func关键字用于声明一个函数。main函数是程序的入口点,程序会从这里开始执行。
  • fmt.Println("Hello, World"): 调用fmt包中的Println函数,将字符串"Hello, World"输出到控制台,并自动换行。

2. 变量与常量:程序数据的基石

在Go中,我们使用变量和常量来存储和管理数据。

变量(Variables)

变量是值的存储位置,其值在程序运行期间可以改变。Go语言是静态类型语言,意味着在声明变量时必须指定其类型。

声明变量的方式:

  1. var 关键字(标准声明)

    // 声明一个名为 age 的整数变量
    var age int
    age = 30 // 赋值
    
    // 声明并同时初始化
    var name string = "Alice"
  2. 类型推断

    如果在声明时就进行初始化,Go可以自动推断变量的类型,此时可以省略类型说明。

    var score = 100 // Go 会推断 score 是 int 类型
  3. 短变量声明 :=

    在函数内部,可以使用更简洁的 := 操作符进行声明和初始化。这是最常用的方式。

    // 只能在函数内部使用
    country := "USA"
    isReady := true

常量(Constants)

常量的值在编译时就已经确定,程序运行期间不可更改。

const PI = 3.14159
const AppName = "My Awesome App"

3. 基础数据类型

Go语言内置了丰富的数据类型,主要分为以下几类:

  • 布尔型 (bool): 值为 truefalse
  • 数值类型:
    • 整数 (int): 包括 int, int8, int16, int32, int64 (有符号) 和 uint, uint8, uint16, uint32, uint64 (无符号)。
    • 浮点数 (float): float32, float64
    • 复数 (complex): complex64, complex128
  • 字符串 (string): 一个由字节组成的序列,通常用于表示文本。字符串是不可变的。
var isActive bool = true
var temperature float64 = 98.6
var message string = "学习Go语言很有趣!"

4. 控制流程:指挥程序的执行路径

控制流程语句决定了代码的执行顺序。

if-else 条件语句

if-else 用于根据条件执行不同的代码块。

score := 85

if score >= 90 {
    fmt.Println("优秀")
} else if score >= 60 {
    fmt.Println("及格")
} else {
    fmt.Println("需要努力")
}

特色语法if 语句可以包含一个简短的初始化语句,其声明的变量作用域仅限于该if-else块。

if num := 10; num%2 == 0 {
    fmt.Println(num, "是偶数")
} else {
    fmt.Println(num, "是奇数")
}

好的 👍 我来帮你把 数组、切片、map、字符串、channel 的遍历 示例也补充进来,保持和你现有的笔记风格一致。


for 循环

Go 语言只有一种循环结构——for 循环,但它有多种形式。


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

  1. 类似 while 的循环
sum := 1
for sum < 100 {
    sum += sum
}
fmt.Println(sum)

  1. 无限循环
for {
    // 无限循环,需要使用 break 来退出
    break
}

  1. 遍历数组 / 切片
nums := []int{10, 20, 30, 40}
for i, v := range nums {
    fmt.Println("索引:", i, "值:", v)
}

如果只关心值,可以写成:

for _, v := range nums {
    fmt.Println(v)
}

  1. 遍历 map
m := map[string]int{"a": 1, "b": 2, "c": 3}
for k, v := range m {
    fmt.Println("键:", k, "值:", v)
}

  1. 遍历字符串

Go 的字符串是 UTF-8 编码,for range 会自动按 字符 (rune) 遍历,而不是字节:

s := "你好Go"
for i, r := range s {
    fmt.Printf("索引 %d 字符 %c\n", i, r)
}

  1. 遍历 channel
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)

for v := range ch {
    fmt.Println(v)
}

switch 选择语句

switch 是一个更清晰的多路分支选择。Go的switch更加灵活,它会自动在每个case后添加break

day := "Sunday"

switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
    fmt.Println("工作日")
case "Saturday", "Sunday":
    fmt.Println("周末")
default:
    fmt.Println("无效的输入")
}

switch 同样支持初始化语句,并且case后面可以跟表达式。

5. 函数(Functions)

函数是执行特定任务的代码块,是组织和重用代码的基本方式。

// 接收两个整数并返回它们的和
func add(a int, b int) int {
    return a + b
}

// 调用函数
result := add(5, 3) // result is 8

多返回值是Go语言的一大特色,常用于返回结果和错误信息。

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为零")
    }
    return a / b, nil // nil 表示没有错误
}

quotient, err := divide(10, 2)
if err != nil {
    fmt.Println("错误:", err)
} else {
    fmt.Println("商是:", quotient)
}

函数内定义函数

Go 不允许像 Python、JS 那样直接在函数内再定义命名函数, 但是可以通过 匿名函数 + 变量赋值 的方式来实现“函数内函数”,并且可以递归。

// 前序遍历和中序遍历构建二叉树
func buildTree(preorder []int, inorder []int) *TreeNode {
    index := map[int]int{}
    for i, x := range inorder {
        index[x] = i
    }

    // 声明 dfs 的函数类型
    var dfs func(preL, preR, inL, inR int) *TreeNode

    // 定义 dfs 为匿名函数
    dfs = func(preL, preR, inL, inR int) *TreeNode {
        if preL == preR {
            return nil
        }
        leftSize := index[preorder[preL]] - inL
        left := dfs(preL+1, preL+1+leftSize, inL, inL+leftSize)
        right := dfs(preL+1+leftSize, preR, inL+1+leftSize, inR)
        return &TreeNode{preorder[preL], left, right}
    }

    return dfs(0, len(preorder), 0, len(inorder))
}

6. 复合数据类型:组织数据集合

数组 (Array)

数组是固定长度的、包含相同类型元素的序列。

var numbers [3]int // 声明一个长度为3的整数数组
numbers[0] = 1
numbers[1] = 2
numbers[2] = 3

// 声明并初始化
primes := [5]int{2, 3, 5, 7, 11}

切片 (Slice)

切片是对数组一个连续片段的引用,它比数组更常用,因为其长度是可变的。

// 创建一个切片
scores := []int{88, 92, 77}

// 添加元素
scores = append(scores, 95)

// 切片操作
subSlice := scores[1:3] // 包含索引1和2的元素: [92, 77]

映射 (Map)

Map 是一种无序的键值对集合,类似于其他语言中的哈希表或字典。

// 创建一个key为string,value为int的map
ages := make(map[string]int)

ages["Alice"] = 30
ages["Bob"] = 25

fmt.Println("Alice的年龄是:", ages["Alice"])

// 删除键值对
delete(ages, "Bob")

好的 👍 这篇博客已经覆盖了 Go 的基础语法(变量、常量、控制流、函数、数组、切片、map)。为了更完整地作为「Go 语言基础快速入门」,我建议再补充几个 Go 中非常常用的语法点:指针、结构体、方法、接口、并发(goroutine 与 channel)。这样文章就能形成一个比较完整的入门教程。下面我在你的文章基础上继续补充。


7. 指针(Pointers)

Go 语言支持指针,但语法比 C/C++ 更简洁。指针保存的是某个值在内存中的地址。

func main() {
    x := 10
    p := &x // p 保存变量 x 的地址

    fmt.Println("x 的值:", x)
    fmt.Println("x 的地址:", p)
    fmt.Println("通过指针取值:", *p)

    *p = 20 // 修改指针指向的值
    fmt.Println("修改后的 x:", x)
}

要点:

  • & 取变量地址
  • * 通过指针访问值
  • Go 没有指针运算(不像 C 那样可以 p++),更安全

8. 结构体(Struct)

结构体用于定义和组合复杂的数据类型。

type Person struct {
    Name string
    Age  int
}

func main() {
    // 初始化结构体
    p1 := Person{Name: "Alice", Age: 30}
    p2 := Person{"Bob", 25} // 省略字段名,但不推荐

    // 访问字段
    fmt.Println(p1.Name, p1.Age)

    // 取地址修改
    p3 := &Person{Name: "Charlie"}
    p3.Age = 28
    fmt.Println(p3)
}

9. 方法(Methods)

方法是和结构体(或其他自定义类型)绑定的函数。

type Rectangle struct {
    Width, Height float64
}

// 值接收者
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 指针接收者
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    rect := Rectangle{3, 4}
    fmt.Println("面积:", rect.Area())

    rect.Scale(2)
    fmt.Println("缩放后:", rect.Area())
}

规则:值接收者不会修改原始数据,指针接收者可以修改。


10. 接口(Interfaces)

接口定义了一组方法,只要某个类型实现了这些方法,就自动实现了接口(无需显式声明)。

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

func main() {
    var s Shape = Circle{5}
    fmt.Println("圆的面积:", s.Area())
}

接口在 Go 中非常常用,用于解耦和多态。


11. 并发:Goroutine 与 Channel

Go 的最大特色是并发。

Goroutine:轻量级线程

func printMessage(msg string) {
    for i := 0; i < 3; i++ {
        fmt.Println(msg)
    }
}

func main() {
    go printMessage("Hello") // 开启一个新的 goroutine
    printMessage("World")    // 主 goroutine
}

Channel:用于 goroutine 之间通信

func worker(ch chan string) {
    ch <- "任务完成" // 发送消息
}

func main() {
    ch := make(chan string)
    go worker(ch)

    msg := <-ch // 接收消息
    fmt.Println(msg)
}

Channel 保证 goroutine 之间安全通信,避免手动加锁。


12. 错误处理

Go 没有异常机制,使用 返回值 + error 来处理错误。

import "errors"

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("除数不能为零")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Println("结果:", result)
    }
}

13. defer、panic 和 recover

  • defer:函数结束前延迟执行(常用于资源释放)
  • panic:触发运行时错误
  • recover:捕获 panic,防止程序崩溃
func safeDivide(a, b int) {
    defer fmt.Println("结束 safeDivide") // 延迟执行
    if b == 0 {
        panic("除数不能为零") // 抛出异常
    }
    fmt.Println("结果:", a/b)
}

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("捕获错误:", r)
        }
    }()

    safeDivide(10, 0)
}