go语言语法基础全面总结

Go(又称 Golang)是一门静态类型、编译型语言,主打简洁、高效、并发安全,语法设计贴近工程实践,避免冗余特性。以下是其核心语法的全面梳理,适合入门者系统学习,也可作为日常参考。

一、基础语法:程序结构与变量常量

1. 程序入口与包声明

Go 程序的执行入口是 main 包下的 main() 函数,必须满足以下规则:

  • 每个文件属于一个包(package 包名 声明在文件第一行);
  • 只有 main 包能包含 main() 函数(程序入口);
  • 导入依赖包使用 import,支持单行 / 多行导入。

go

复制代码
// 单行导入
import "fmt"
// 多行导入(推荐,更清晰)
import (
    "fmt"
    "math"
)

// 程序入口:无参数、无返回值
func main() {
    fmt.Println("Hello, Go!") // 输出:Hello, Go!
}

2. 变量声明

Go 变量声明有 4 种方式,核心原则:强类型、声明即初始化(默认零值)

声明方式 语法示例 适用场景
完整声明 var 变量名 类型 = 值 类型明确,需显式指定
类型推导(推荐) var 变量名 = 值 编译器自动推导类型,简洁
短变量声明(仅函数内) 变量名 := 值 函数内快速声明,最常用
批量声明 var (a int = 1; b string = "test") 多变量集中声明,减少重复代码

零值规则:未显式赋值的变量会被赋予默认零值(int=0、string=""、bool=false、指针 = nil 等)。

go

复制代码
func main() {
    var a int = 10          // 完整声明
    var b = "hello"         // 类型推导(string 类型)
    c := 3.14               // 短变量声明(float64 类型)
    var (
        d bool              // 零值:false
        e []int             // 零值:nil(切片)
    )
    fmt.Println(a, b, c, d, e) // 输出:10 hello 3.14 false []
}

3. 常量声明

使用 const 关键字,值必须是编译期可确定的常量(数字、字符串、布尔值),支持批量声明和枚举。

go

复制代码
// 基本常量
const PI = 3.14159
const NAME string = "Go"

// 批量声明
const (
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
)

// iota 枚举(自增常量,默认从 0 开始,每行+1)
const (
    A = iota // 0
    B = iota // 1
    C = 100  // 手动赋值,打破自增
    D = iota // 3(恢复自增,继续计数)
)

二、数据类型:值类型与引用类型

Go 数据类型分为 值类型 (拷贝传递)和 引用类型(指针传递),核心类型如下:

1. 基本类型(值类型)

类型类别 具体类型 说明
整数类型 int8/int16/int32/int64、uint8/uint16... int 随系统位数(32/64 位),uint 同理
浮点类型 float32(精度约 6 位)、float64(默认) float64 精度更高,推荐优先使用
布尔类型 bool 取值 true/false,不可用 0/1 替代
字符串类型 string 不可变(修改需重新生成),UTF-8 编码
字符类型 rune(别名 int32,表示 Unicode 字符) 区别于 byte(uint8,ASCII 字符)

go

复制代码
func main() {
    var age int = 25
    var weight float64 = 68.5
    var isStudent bool = true
    var name string = "张三"
    var char rune = '中' // 注意:单引号表示字符,双引号表示字符串
    
    fmt.Printf("%T %T %T %T %T\n", age, weight, isStudent, name, char)
    // 输出:int float64 bool string int32
}

2. 复合类型

(1)数组(值类型)

固定长度、同类型元素的集合,长度是类型的一部分([3]int[5]int 是不同类型)。

go

复制代码
// 声明方式
var arr1 [3]int = [3]int{1, 2, 3} // 完整声明
arr2 := [3]int{4, 5, 6}           // 类型推导
arr3 := [...]int{7, 8, 9}         // 自动推导长度

// 访问与修改
fmt.Println(arr1[0]) // 输出:1
arr2[1] = 10
fmt.Println(arr2) // 输出:[4 10 6]
(2)切片(引用类型)

动态长度的 "动态数组",基于数组实现,核心结构:指针+长度+容量,声明时无需指定长度。

go

复制代码
// 声明方式
var s1 []int               // 零值:nil
s2 := []int{1, 2, 3}       // 直接初始化
s3 := make([]int, 3, 5)    // make(类型, 长度, 容量):长度 3,容量 5

// 常用操作
s4 := append(s2, 4, 5)     // 追加元素(容量不足时自动扩容)
s5 := s2[1:3]              // 切片截取:[start:end),左闭右开
fmt.Println(len(s3), cap(s3)) // 输出:3 5(len=长度,cap=容量)
(3)映射(map,引用类型)

无序键值对集合,键必须是 "可比较类型"(int、string、bool 等,不能是切片、map)。

go

复制代码
// 声明与初始化(必须用 make 或直接赋值,否则为 nil,无法添加元素)
m1 := make(map[string]int)       // 空 map
m2 := map[string]string{
    "name": "李四",
    "age":  "28",
}

// 增删改查
m1["score"] = 90                // 新增/修改
fmt.Println(m1["score"])        // 查询:90
delete(m1, "score")             // 删除
v, ok := m1["score"]            // 安全查询(避免键不存在时返回零值)
fmt.Println(v, ok)              // 输出:0 false(ok 表示键是否存在)
(4)结构体(值类型)

自定义复合类型,用于封装多个不同类型的字段。

go

复制代码
// 定义结构体
type Person struct {
    Name string
    Age  int
    Sex  string
}

// 初始化
p1 := Person{Name: "王五", Age: 30, Sex: "男"} // 指定字段名(推荐)
p2 := Person{"赵六", 26, "女"}                // 按字段顺序(不推荐,易出错)

// 访问字段
fmt.Println(p1.Name) // 输出:王五
p2.Age = 27
(5)指针(引用类型)

存储变量内存地址的变量,核心用途:修改函数参数的值(值类型默认拷贝传递)。

go

复制代码
func main() {
    a := 10
    var p *int = &a // &a:取变量 a 的地址;p 是 *int 类型指针
    fmt.Println(*p) // *p:通过指针访问变量的值,输出:10
    
    modify(p)       // 传递指针
    fmt.Println(a)  // 输出:20(变量被修改)
}

// 函数接收指针参数
func modify(x *int) {
    *x = 20 // 修改指针指向的变量值
}

三、流程控制:条件、循环、跳转

1. 条件语句(if-else)

  • 无需括号包裹条件表达式;
  • 条件表达式必须是 bool 类型(不能用 0/1 替代);
  • 支持 "初始化语句"(在条件前声明变量,作用域仅限 if 块)。

go

复制代码
func main() {
    age := 18
    // 基础用法
    if age >= 18 {
        fmt.Println("成年")
    } else {
        fmt.Println("未成年")
    }
    
    // 带初始化语句
    if score := 95; score >= 90 {
        fmt.Println("优秀")
    } else if score >= 80 {
        fmt.Println("良好")
    } else {
        fmt.Println("合格")
    }
}

2. 循环语句(for)

Go 只有 for 一种循环结构,支持三种用法:

(1)普通循环(类似 while)

go

复制代码
i := 0
for i < 5 { // 条件满足时循环
    fmt.Println(i)
    i++
}
(2)经典 for 循环(初始化 + 条件 + 自增)

go

复制代码
for j := 0; j < 3; j++ {
    fmt.Println(j) // 输出:0 1 2
}
(3)无限循环(无条件)

go

复制代码
count := 0
for { // 等价于 for ; ; {}
    count++
    if count == 3 {
        break // 跳出循环
    }
    fmt.Println(count) // 输出:1 2
}
(4)循环遍历(for range)

专门用于遍历数组、切片、map、字符串等,更简洁高效:

go

复制代码
// 遍历切片
s := []int{1, 2, 3}
for index, value := range s {
    fmt.Printf("索引:%d,值:%d\n", index, value)
}

// 遍历 map(无序)
m := map[string]int{"a":1, "b":2}
for key, val := range m {
    fmt.Printf("键:%s,值:%d\n", key, val)
}

// 遍历字符串(按 rune 遍历,支持中文)
str := "Hello 世界"
for i, c := range str {
    fmt.Printf("索引:%d,字符:%c\n", i, c)
}

3. 跳转语句(break、continue、goto)

  • break:跳出当前循环 /switch 块;
  • continue:跳过当前循环迭代,进入下一次;
  • goto:跳转到当前函数内的标签(谨慎使用,避免破坏代码结构)。

go

复制代码
// break 示例(跳出循环)
for i := 0; i < 5; i++ {
    if i == 3 {
        break
    }
    fmt.Println(i) // 输出:0 1 2
}

// goto 示例
func main() {
    fmt.Println("1")
    goto label
    fmt.Println("2") // 不会执行
label:
    fmt.Println("3") // 输出:1 3
}

4. 选择语句(switch)

Go 的 switch 更灵活,支持任意类型,无需 break(默认自动跳出,需穿透用 fallthrough)。

go

复制代码
func main() {
    num := 2
    switch num {
    case 1:
        fmt.Println("一")
    case 2:
        fmt.Println("二")
    case 3:
        fmt.Println("三")
    default:
        fmt.Println("其他")
    }

    // 带初始化语句
    switch score := 85; { // 条件可以是表达式(类似 if-else 链)
    case score >= 90:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    default:
        fmt.Println("合格")
    }
}

四、函数:声明、参数、返回值

1. 函数声明

核心语法:func 函数名(参数列表) (返回值列表) { 函数体 }

go

复制代码
// 无参数、无返回值
func sayHello() {
    fmt.Println("Hello, Go Function!")
}

// 有参数、有返回值(单返回值)
func add(a int, b int) int { // 参数类型相同可简写为 (a, b int)
    return a + b
}

// 多返回值(Go 特色,常用于返回结果+错误)
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为 0") // 返回错误
    }
    return a / b, nil // 无错误返回 nil
}

// 调用函数
func main() {
    sayHello()
    sum := add(10, 20)
    fmt.Println(sum) // 输出:30
    
    res, err := divide(10, 2)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(res) // 输出:5
    }
}

2. 函数进阶特性

(1)命名返回值

返回值声明时指定名称,函数内可直接赋值,return 无需带参数(更简洁)。

go

复制代码
func calc(a, b int) (sum, sub int) {
    sum = a + b
    sub = a - b
    return // 裸返回,自动返回 sum 和 sub
}
(2)可变参数

参数列表最后一个参数用 ...类型 表示,函数内视为切片处理。

go

复制代码
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

// 调用:可传递任意个 int 类型参数
fmt.Println(sum(1, 2, 3))       // 输出:6
fmt.Println(sum(4, 5, 6, 7))    // 输出:22
(3)函数作为参数 / 返回值(高阶函数)

Go 中函数是 "一等公民",可作为参数传递或作为返回值返回。

go

复制代码
// 函数作为参数
func operate(a, b int, f func(int, int) int) int {
    return f(a, b)
}

// 函数作为返回值
func getAddFunc() func(int, int) int {
    return func(a, b int) int {
        return a + b
    }
}

// 调用
addFunc := getAddFunc()
fmt.Println(operate(5, 3, addFunc)) // 输出:8

五、接口与面向对象

Go 没有类和继承,通过 接口(interface) 实现多态,核心思想:鸭子类型("看起来像鸭子,走起来像鸭子,就是鸭子")。

1. 接口声明

接口是方法签名的集合,只定义 "做什么",不定义 "怎么做"(无实现)。

go

复制代码
// 定义接口
type Animal interface {
    Speak() string // 方法签名:无参数,返回 string
    Move() string
}

2. 接口实现

无需显式声明 "实现接口",只要结构体实现了接口的所有方法,就自动实现该接口(非侵入式)。

go

复制代码
// 定义结构体
type Dog struct {
    Name string
}

type Cat struct {
    Name string
}

// Dog 实现 Animal 接口(实现所有方法)
func (d Dog) Speak() string {
    return fmt.Sprintf("%s 汪汪叫", d.Name)
}
func (d Dog) Move() string {
    return fmt.Sprintf("%s 跑起来", d.Name)
}

// Cat 实现 Animal 接口
func (c Cat) Speak() string {
    return fmt.Sprintf("%s 喵喵叫", c.Name)
}
func (c Cat) Move() string {
    return fmt.Sprintf("%s 跳起来", c.Name)
}

3. 接口使用(多态)

接口变量可存储任意实现该接口的结构体实例,调用方法时自动执行对应实现。

go

复制代码
func main() {
    var animal Animal // 接口变量
    animal = Dog{Name: "旺财"}
    fmt.Println(animal.Speak()) // 输出:旺财 汪汪叫
    fmt.Println(animal.Move())  // 输出:旺财 跑起来
    
    animal = Cat{Name: "咪宝"}
    fmt.Println(animal.Speak()) // 输出:咪宝 喵喵叫
}

4. 空接口(interface {})

无任何方法的接口,可存储任意类型的值(类似 Java 的 Object),常用于处理未知类型。

go

复制代码
// 空接口变量
var any interface{}
any = 10          // 存储 int
any = "hello"     // 存储 string
any = Dog{Name: "旺财"} // 存储结构体

// 类型断言(判断空接口存储的实际类型)
val, ok := any.(Dog)
if ok {
    fmt.Println(val.Name) // 输出:旺财
}

六、错误处理

Go 不使用 try-catch 捕获异常,而是通过 显式返回错误(error 类型) 处理错误,核心原则:"错误也是值"。

1. 错误类型与创建

error 是接口类型,定义:type error interface { Error() string },常用 fmt.Errorf()errors.New() 创建错误。

go

复制代码
import (
    "errors"
    "fmt"
)

func checkAge(age int) error {
    if age < 0 || age > 120 {
        // 创建错误:两种方式
        return errors.New("年龄必须在 0-120 之间")
        // return fmt.Errorf("年龄 %d 非法:必须在 0-120 之间", age) // 带格式化信息
    }
    return nil // 无错误返回 nil
}

// 调用:必须检查错误
func main() {
    err := checkAge(150)
    if err != nil {
        fmt.Println("错误:", err) // 输出:错误:年龄必须在 0-120 之间
        return
    }
    fmt.Println("年龄合法")
}

2. 自定义错误(进阶)

通过结构体实现 error 接口,可携带更多错误信息(如错误码)。

go

复制代码
// 自定义错误结构体
type MyError struct {
    Code    int
    Message string
}

// 实现 error 接口的 Error() 方法
func (e *MyError) Error() string {
    return fmt.Sprintf("错误码:%d,信息:%s", e.Code, e.Message)
}

// 返回自定义错误
func doSomething() error {
    return &MyError{Code: 500, Message: "服务器内部错误"}
}

// 调用
err := doSomething()
if err != nil {
    fmt.Println(err) // 输出:错误码:500,信息:服务器内部错误
    // 类型断言获取自定义错误信息
    if myErr, ok := err.(*MyError); ok {
        fmt.Println("错误码:", myErr.Code) // 输出:500
    }
}

七、并发编程(Go 核心特性)

Go 原生支持并发,通过 goroutine(轻量级线程)channel(通道) 实现 "通信顺序进程(CSP)" 模型,简单高效。

1. Goroutine(协程)

  • 轻量级线程,由 Go 运行时(runtime)管理,而非操作系统内核;
  • 启动成本极低(栈初始 2KB,可动态扩容),支持百万级并发;
  • go 关键字启动(后跟函数 / 方法调用)。

go

复制代码
func sayHello(name string) {
    fmt.Printf("Hello, %s\n", name)
}

func main() {
    // 启动 3 个 goroutine
    go sayHello("A")
    go sayHello("B")
    go sayHello("C")
    
    // 主 goroutine 退出后,子 goroutine 会被强制终止,需等待
    time.Sleep(100 * time.Millisecond) // 简单等待(不推荐)
}

2. Channel(通道)

用于 goroutine 间通信,解决数据竞争,实现 "同步 + 传值",分为无缓冲通道有缓冲通道

(1)无缓冲通道(同步通道)
  • 发送操作(ch <- val)会阻塞,直到有 goroutine 接收;
  • 接收操作(val := <-ch)会阻塞,直到有 goroutine 发送。

go

复制代码
func send(ch chan int) {
    ch <- 10 // 发送 10 到通道,阻塞直到接收
    fmt.Println("发送完成")
}

func main() {
    ch := make(chan int) // 无缓冲通道(容量默认 0)
    
    go send(ch)          // 启动发送 goroutine
    
    val := <-ch          // 接收通道数据,阻塞直到发送
    fmt.Println("接收值:", val) // 输出:接收值:10
    
    time.Sleep(100 * time.Millisecond) // 输出:发送完成
}
(2)有缓冲通道(异步通道)
  • 容量 > 0,发送操作仅当缓冲区满时阻塞;
  • 接收操作仅当缓冲区空时阻塞。

go

复制代码
func main() {
    ch := make(chan int, 2) // 有缓冲通道,容量 2
    
    ch <- 1 // 发送成功(缓冲区未满)
    ch <- 2 // 发送成功(缓冲区未满)
    // ch <- 3 // 阻塞(缓冲区满)
    
    fmt.Println(<-ch) // 接收 1,缓冲区剩余 1
    fmt.Println(<-ch) // 接收 2,缓冲区空
}
(3)通道关闭与遍历
  • close(ch) 关闭通道,关闭后不能再发送数据,但可继续接收;
  • for range 遍历通道,直到通道关闭。

go

复制代码
func send(ch chan int) {
    for i := 0; i < 3; i++ {
        ch <- i
    }
    close(ch) // 发送完成后关闭通道
}

func main() {
    ch := make(chan int, 3)
    go send(ch)
    
    // 遍历通道,直到关闭
    for val := range ch {
        fmt.Println(val) // 输出:0 1 2
    }
}

3. sync 包(同步原语)

除了 channel,还可通过 sync 包的工具实现同步(如互斥锁、等待组)。

(1)等待组(sync.WaitGroup)

替代 time.Sleep,优雅等待多个 goroutine 完成。

go

复制代码
func work(id int, wg *sync.WaitGroup) {
    defer wg.Done() // goroutine 完成后调用(计数器-1)
    fmt.Printf("工作 %d 开始\n", id)
    time.Sleep(100 * time.Millisecond)
    fmt.Printf("工作 %d 结束\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 启动 goroutine 前计数器+1
        go work(i, &wg) // 传递指针(避免拷贝)
    }
    
    wg.Wait() // 阻塞,直到计数器为 0
    fmt.Println("所有工作完成")
}
(2)互斥锁(sync.Mutex)

解决多个 goroutine 并发修改共享变量的数据竞争问题。

go

复制代码
var (
    count int
    mutex sync.Mutex // 互斥锁
)

func increment() {
    mutex.Lock()         // 加锁
    defer mutex.Unlock() // 延迟解锁(确保函数退出时释放)
    count++
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }
    wg.Wait()
    fmt.Println("count:", count) // 输出:1000(无数据竞争)
}

八、常用标准库

Go 标准库丰富,以下是入门必备的核心库:

库名 核心功能 常用函数 / 类型
fmt 输入输出格式化 Println()Printf()Scanln()
os 操作系统交互(文件、环境变量等) os.Argsos.Open()os.Exit()
io/ioutil 文件读写(简化版) ioutil.ReadFile()ioutil.WriteFile()
bufio 带缓冲的读写(高效) bufio.NewReader()bufio.Scanner
net/http HTTP 客户端 / 服务端 http.Get()http.HandleFunc()
encoding/json JSON 序列化 / 反序列化 json.Marshal()json.Unmarshal()
time 时间处理 time.Now()time.Sleep()time.Parse()

九、语法核心注意事项

  1. 大小写敏感Namename 是不同变量 / 函数;
  2. 语句结尾无分号:编译器自动添加,一行一个语句(多行语句需用分号分隔);
  3. 变量 / 导入未使用:编译报错(避免冗余代码);
  4. 值类型 vs 引用类型:值类型拷贝传递,引用类型传递指针(修改会影响原数据);
  5. map 和切片必须初始化 :nil map / 切片无法添加元素,需用 make 或直接赋值;
  6. 并发安全:多个 goroutine 操作共享变量时,需用 mutex 或 channel 同步。
相关推荐
No0d1es1 天前
2025年12月 GESP CCF编程能力等级认证Python一级真题
开发语言·python·青少年编程·gesp·ccf
小六子成长记1 天前
C++:map和set重点解析
开发语言·c++
a努力。1 天前
中国电网Java面试被问:分布式缓存的缓存穿透解决方案
java·开发语言·分布式·缓存·postgresql·面试·linq
草莓熊Lotso1 天前
脉脉独家【AI创作者xAMA】| 开启智能创作新时代
android·java·开发语言·c++·人工智能·脉脉
moxiaoran57531 天前
Java设计模式的运用
java·开发语言·设计模式
qq_172805591 天前
Modbus Server 模拟平台之RTU协议
golang·modbus
源代码•宸1 天前
Leetcode—1339. 分裂二叉树的最大乘积【中等】
开发语言·后端·算法·leetcode·golang·dfs
Chasing Aurora1 天前
C++后端开发之旅(一)
java·开发语言·c++
码农水水1 天前
美团Java后端Java面试被问:Kafka的零拷贝技术和PageCache优化
java·开发语言·后端·缓存·面试·kafka·状态模式