系列:Go 语言从入门到进阶
作者:耿雨飞
适用版本:go v1.26.2
前置条件
在开始本章学习之前,请确保:
- 已完成第 1 章的学习,Go 开发环境已搭建完成
- 能够使用
go run、go build编译运行 Go 程序 - 已获取 Go 1.26.2 源码树(
go-go1.26.2目录)
导读
本章将深入 Go 语言的基本语法与数据类型体系。我们从变量声明和常量定义出发,系统地学习 Go 的类型系统,包括整型、浮点型、复数、布尔、字符串等基本类型,然后掌握类型转换规则和运算符体系。
这些内容是后续一切高级特性的基石。本章将大量对照 Go 源码中的类型定义与实现,帮助你从底层理解"语法规则为何如此设计"。
本章将对照 Go 1.26.2 源码中的以下关键路径:
| 源码路径 | 内容说明 |
|---|---|
src/builtin/builtin.go | 所有内建类型、常量与函数的声明 |
src/strconv/doc.go | strconv包概述——字符串与基本类型转换 |
src/strconv/number.go | 数值解析与格式化的核心实现 |
src/runtime/string.go | 字符串在运行时的内部结构与操作 |
src/runtime/utf8.go | 运行时的 UTF-8 编解码实现 |
src/runtime/float.go | 浮点数运行时辅助函数 |
src/runtime/complex.go | 复数除法的运行时实现 |
学习目标
学完本章后,你应当能够:
- 熟练使用
var、:=、const声明变量和常量 - 理解
iota枚举器的工作原理并运用于实际场景 - 说明 Go 各基本数据类型的取值范围和内存大小,并能对照
src/builtin/builtin.go找到其定义 - 区分
byte(uint8)与rune(int32)的用途 - 理解字符串的不可变性和运行时内部结构
- 掌握显式类型转换规则,使用
strconv包进行字符串与数值的互转 - 正确使用各类运算符并理解其优先级
2.1 变量与常量
2.1.1 变量声明方式
Go 提供了多种变量声明方式,每种都有其适用场景。
方式一:var完整声明
varnamestring="Go"varageint=15varpifloat64=3.14159类型可省略(编译器自动推断):
varname="Go"// 推断为 stringvarage=15// 推断为 intvarpi=3.14159// 推断为 float64值也可省略(变量被初始化为该类型的零值):
varnamestring// ""varageint// 0varpifloat64// 0.0方式二:批量声明(声明块)
var(hoststring="localhost"portint=8080debugbool=falsemaxRetryint=3)这种风格在声明包级别变量时特别常见。Go 标准库中大量使用此模式,例如src/strconv/number.go:
// 文件: src/strconv/number.go(第 245-249 行)// ErrRange indicates that a value is out of range for the target type.varErrRange=errors.New("value out of range")// ErrSyntax indicates that a value does not have the right syntax for the target type.varErrSyntax=errors.New("invalid syntax")方式三:短变量声明:=
name:="Go"// 等价于 var name string = "Go"age:=15// 等价于 var age int = 15x,y:=10,20// 同时声明多个变量:=是 Go 中最常用的变量声明方式,但有两个限制:
- 只能在函数内部使用——包级别变量必须使用
var - 左侧至少有一个新变量——否则编译错误
x:=10x:=20// 编译错误:no new variables on left side of :=x,y:=10,20x,z:=30,40// 合法:z 是新变量,x 被重新赋值方式四:new函数
new函数分配内存并返回指针,变量被初始化为零值:
p:=new(int)// p 的类型是 *int,*p == 0fmt.Println(*p)// 输出: 0在 Go 1.26.2 中,new的定义有了扩展,来看源码:
// 文件: src/builtin/builtin.go(第 227-235 行)// The built-in function new allocates a new, initialized variable and returns// a pointer to it. It accepts a single argument, which may be either a type// or an expression.// If the argument is a type T, then new(T) allocates a variable of type T// initialized to its zero value.// Otherwise, the argument is an expression x and new(x) allocates a variable// of the type of x initialized to the value of x. If that value is an untyped// constant, it is first implicitly converted to its default type.funcnew(TypeOrExpr)*Type注意 Go 1.26 对new进行了增强:除了传统的new(Type)用法,现在还支持new(expr)形式——传入一个表达式,返回指向该值副本的指针:
p:=new(42)// p 的类型是 *int,*p == 42s:=new("hello")// s 的类型是 *string,*s == "hello"2.1.2 常量与iota枚举器
常量声明
常量在编译时确定,不可在运行时修改:
constPi=3.14159265358979323846constE=2.71828182845904523536const(StatusOK=200StatusNotFound=404StatusError=500)Go 的常量有一个重要特性——无类型常量(Untyped Constants)。无类型常量拥有比具体类型更高的精度,并且可以灵活地参与不同类型的运算:
constx=1_000_000_000_000_000_000// 无类型整数常量varaint64=x// 合法varbint32=x// 编译错误:溢出 int32 范围consty=3.14// 无类型浮点常量varcfloat32=y// 合法vardfloat64=y// 合法src/builtin/builtin.go中,true和false就是以无类型常量的形式定义的:
// 文件: src/builtin/builtin.go(第 18-22 行)// true and false are the two untyped boolean values.const(true=0==0// Untyped bool.false=0!=0// Untyped bool.)true定义为0 == 0,false定义为0 != 0——这是一种自举(bootstrap)手法,用一个始终为真/假的表达式来定义布尔常量本身。
iota枚举器
iota是 Go 特有的常量生成器,在const声明块中自动递增:
// 文件: src/builtin/builtin.go(第 106-109 行)// iota is a predeclared identifier representing the untyped integer ordinal// number of the current const specification in a (usually parenthesized)// const declaration. It is zero-indexed.constiota=0// Untyped int.基本用法:
const(Sunday=iota// 0Monday// 1Tuesday// 2Wednesday// 3Thursday// 4Friday// 5Saturday// 6)iota的进阶用法:
// 位掩码模式——用于权限系统const(ReadPerm=1<<iota// 1 (1 << 0)WritePerm// 2 (1 << 1)ExecPerm// 4 (1 << 2))// 跳过某些值const(_=iota// 0,用空白标识符跳过KB=1<<(10*iota)// 1 << 10 = 1024MB// 1 << 20 = 1048576GB// 1 << 30 = 1073741824TB// 1 << 40 = 1099511627776)// 同一行的多个 iota 值相同const(bit0,mask0=1<<iota,1<<iota-1// 1, 0bit1,mask1// 2, 1bit2,mask2// 4, 3)关键规则:iota在每个const块的第一行从 0 开始,每一行(不是每个常量)自增 1。不同的const块之间互不影响。
2.1.3 零值机制
Go 的所有变量在声明时如果没有显式初始化,都会被自动赋予该类型的零值(Zero Value)。这是 Go 的一个核心设计原则——不存在"未初始化的变量"。
| 类型 | 零值 |
|---|---|
bool | false |
所有整型(int、int8…uint64) | 0 |
所有浮点型(float32、float64) | 0.0 |
所有复数型(complex64、complex128) | (0+0i) |
string | ""(空字符串) |
指针、func、interface、slice、map、chan | nil |
| 数组 | 每个元素为其类型的零值 |
| 结构体 | 每个字段为其类型的零值 |
nil的定义同样来自src/builtin/builtin.go:
// 文件: src/builtin/builtin.go(第 111-113 行)// nil is a predeclared identifier representing the zero value for a// pointer, channel, func, interface, map, or slice type.varnilType// Type must be a pointer, channel, func, interface, map, or slice type零值机制的实际意义在于——你可以安全地使用未赋值的变量,无需担心"野指针"或"垃圾值":
varsb strings.Builder sb.WriteString("hello")// 零值的 strings.Builder 开箱即用fmt.Println(sb.String())// 输出: hellovarmu sync.Mutex mu.Lock()// 零值的 sync.Mutex 开箱即用mu.Unlock()2.2 基本数据类型
Go 的基本数据类型全部定义在src/builtin/builtin.go中。这个文件是一个特殊的"文档包"——它不包含真正的实现,而是为 Go 的预声明标识符提供文档说明。
// 文件: src/builtin/builtin.go(第 5-11 行)/* Package builtin provides documentation for Go's predeclared identifiers. The items documented here are not actually in package builtin but their descriptions here allow godoc to present documentation for the language's special identifiers. */packagebuiltin2.2.1 整型
Go 提供了丰富的整数类型,分为有符号和无符号两大类。
有符号整型
// 文件: src/builtin/builtin.go(第 40-54 行)// int8 is the set of all signed 8-bit integers.// Range: -128 through 127.typeint8int8// int16 is the set of all signed 16-bit integers.// Range: -32768 through 32767.typeint16int16// int32 is the set of all signed 32-bit integers.// Range: -2147483648 through 2147483647.typeint32int32// int64 is the set of all signed 64-bit integers.// Range: -9223372036854775808 through 9223372036854775807.typeint64int64无符号整型
// 文件: src/builtin/builtin.go(第 24-38 行)