第二章 Go 语言基础语法

开发
Cover

欢迎来到第二章!在成功搭建了开发环境并运行了第一个 Go 程序之后,现在是时候深入了解 Go 语言的内部构造了。Go 的语法设计深受 C 语言影响,但它摒弃了许多繁琐的历史包袱,力求让代码的阅读和编写变得更加清晰、直观。

本章将带你完成以下任务:

  • 掌握 Go 语言的命名规范与可见性规则
  • 学习变量、常量及基本数据类型的声明与使用
  • 理解 Go 语言中安全且精简的指针机制
  • 掌握条件分支与循环控制结构
  • 学习函数的定义及多返回值特性
  • 了解 Go 的包管理机制与文件结构

2.1 名称(Names)

在 Go 语言中,函数名、变量名、常量名、类型名和包名的命名都遵循一个简单的规则:名称必须以字母或下划线开头,后面可以跟任意数量的字母、数字和下划线。

Go 语言区分大小写,heapSortHeapsort 是两个不同的名称。Go 拥有 25 个关键字(如 if, for, func, var 等),这些不能作为自定义名称。

💡 核心机制:大小写决定可见性(Exported vs Unexported) 在 Go 中,没有 publicprivate 这样的访问修饰符。标识符的首字母大小写直接决定了它的可见范围:

  • 首字母大写(如 Println, Name):是导出的,可以被包外的代码访问(相当于 public)。
  • 首字母小写(如 age, calculateSum):是未导出的,只能在包内部使用(相当于 private)。

2.2 变量与常量(Variables & Constants)

变量声明

Go 语言是一门静态强类型语言,变量声明有几种常见方式:

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

// 声明并初始化
var name string = "Gopher"
// 声明但不初始化(会被赋予零值,如整型的 0,布尔型的 false,字符串的 "")
var age int 

2. 短变量声明(推荐用于局部变量) 在函数内部,可以使用 := 进行简短声明,Go 编译器会自动推断类型:

country := "China"  // 自动推断为 string
count := 10         // 自动推断为 int

3. 批量声明

var (
    a int    = 1
    b string = "hello"
)

常量声明

使用 const 关键字声明常量,常量的值在编译期就已确定,运行时不可修改。

const Pi = 3.14159
const (
    StatusOK = 200
    StatusNotFound = 404
)

2.3 基本数据类型(Types)

Go 提供了丰富的基础数据类型:

  • 整型int8, int16, int32, int64 以及对应的无符号整型 uint8 等。最常用的是 int,它在 32 位操作系统上是 32 位,在 64 位系统上是 64 位。
  • 浮点型float32float64(推荐使用 float64 以获得更高精度)。
  • 布尔型bool,只有 truefalse 两个值。
  • 字符串string,Go 中的字符串是不可变的字节序列,原生支持 UTF-8 编码。
var isDone bool = true
var price float64 = 99.99
var greeting string = "你好,Go!"

2.4 指针(Pointers)

如果你有 C/C++ 背景,可能会对指针感到恐惧,但请放心:Go 语言的指针安全且简单,不支持指针运算

指针的值是一个变量的内存地址。

  • 使用 & 获取变量的地址。
  • 使用 * 获取指针指向的变量的值(解引用)。
package main

import "fmt"

func main() {
    x := 10
    p := &x         // p 是一个指向 x 的指针,类型为 *int
    fmt.Println(*p) // 输出 10 (读取指针指向的值)
    
    *p = 20         // 通过指针修改 x 的值
    fmt.Println(x)  // 输出 20
}

2.5 控制结构(Control Structures)

Go 的控制结构极度精简,去除了不必要的括号。

if-else 条件判断

条件表达式不需要用小括号 () 括起来,但大括号 {} 是必须的。可以在条件判断前执行一个简短的初始化语句:

if score := 85; score >= 90 {
    fmt.Println("优秀")
} else if score >= 60 {
    fmt.Println("及格")
} else {
    fmt.Println("不及格")
}

for 循环

Go 语言中只有 for 这一种循环结构,它涵盖了 whiledo-while 的功能。

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

// 2. 类似 while 的循环
n := 1
for n < 5 {
    n *= 2
}

// 3. 无限循环
for {
    fmt.Println("无限循环...")
    break // 使用 break 跳出循环
}

switch 分支

Go 的 switch 极其灵活,默认情况下,匹配到一个 case 并执行完毕后会自动退出,**不需要写 break**

os := "darwin"
switch os {
case "darwin":
    fmt.Println("Mac OS")
case "linux":
    fmt.Println("Linux")
default:
    fmt.Println("Other")
}

2.6 函数(Functions)

函数是 Go 语言的“一等公民”。使用 func 关键字定义,参数类型放在参数名之后。

💡 重点特性:Go 函数支持多返回值,这在处理错误时非常有用。

// 定义一个函数,接收两个整型参数,返回相除的商和余数
func divide(a int, b int) (int, int) {
    quotient := a / b
    remainder := a % b
    return quotient, remainder
}

func main() {
    q, r := divide(10, 3)
    fmt.Printf("商: %d, 余数: %d\n", q, r)
    
    // 如果不需要某个返回值,可以使用空白标识符 "_" 忽略它
    _, r2 := divide(15, 4) 
    fmt.Println("只取余数:", r2)
}

2.7 包和文件(Packages & Files)

Go 语言的代码是通过包(Package)来组织的,包类似于其他语言中的库(Library)或模块(Module)。

  1. 包声明:每个 Go 源文件的开头都必须有一个 package 声明。同一个文件夹下的所有 .go 文件必须属于同一个包。
package mathutils
  1. 导入包:使用 import 关键字导入需要使用的包。
import (
    "fmt"
    "math"
)
  1. main:如果一个项目需要被编译成可执行文件,它必须有一个包含 main() 函数的 main 包。作为程序的入口点。
  2. init 函数:每个包可以包含一个或多个 init() 函数,这些函数会在包被导入时自动执行,通常用于执行包级别的初始化操作。

2.8 小结

本章你学习了 Go 语言最核心的基础语法:

  • 了解了首字母大小写决定可见性这一优雅的设计。
  • 掌握了变量、常量和基本数据类型的声明(特别是实用的 := 语法)。
  • 认识了 Go 中没有运算负担的安全指针
  • 熟悉了精简的 if、万能的 for 和无须 break 的 switch
  • 编写了支持多返回值的函数。
  • 理解了 Go 以为单位组织代码的哲学。

掌握了这些,你已经具备了编写基础 Go 逻辑的能力。下一步,我们将深入探索 Go 语言中更高级的数据结构,如数组、切片(Slice)、映射(Map)以及结构体(Struct)。