Go 基础语法:变量、常量、基本类型与类型转换
本课所属:第一模块:Go 语言入门与开发环境 | 课时:m01-l04 | 难度:入门 学完本课你将能够:掌握 Go 语言中变量与常量的声明方式,理解零值机制与类型系统,并能正确使用标准库进行类型转换。
一、学习目标
- 能准确使用
var和:=声明变量,并理解多变量赋值与并行赋值的区别。 - 能解释零值机制,并列出
int、string、bool、float64及引用类型的零值。 - 能使用
const和iota定义枚举常量,并理解无类型常量的隐式转换规则。 - 能使用
strconv包进行字符串与数值之间的显式类型转换。
二、知识讲解
2.1 变量声明:var 与 :=
在 Go 语言中,变量是存储数据的容器。Go 提供了两种主要的变量声明方式:var 关键字和短变量声明 :=。
1. 是什么
- var 声明:显式声明变量类型。语法为 var name type = value。如果省略 value,变量会被赋予该类型的零值。
- := 短声明:隐式推导类型。语法为 name := value。它只能在函数内部使用,且左侧必须至少有一个新变量。
2. 为什么这样设计
Go 强调显式与简洁。var 适合包级变量或需要明确类型的场景;:= 适合局部变量,减少样板代码,提高可读性。
3. 怎么用
// 显式声明
var age int = 18
var name string = "Alice"
// 隐式声明(短变量)
count := 10
isActive := true
// 多变量声明
var x, y int = 1, 2
a, b := 3, 4
2.2 零值机制
Go 中的变量在声明后若未显式初始化,会自动获得该类型的零值(Zero Value)。这避免了未定义行为,是 Go 安全性的体现。
| 类型 | 零值 |
|---|---|
int, int8, int16, int32, int64 |
0 |
float32, float64 |
0.0 |
bool |
false |
string |
"" (空字符串) |
| 指针、切片、映射、通道、函数、接口 | nil |
2.3 常量与 iota
常量在编译时确定,运行期间不可修改。
1. iota 枚举
iota 是一个预声明的标识符,在 const 块中从 0 开始,每行递增 1。
- 注意:iota 仅适用于 const 块,不能在 var 块中使用。
- 规则:iota 在每个 const 块内从 0 开始,按常量定义项(ConstSpec)逐行递增 1;新 const 块会重新从 0 开始,跳过某行可用 _。
2. 无类型常量
Go 支持无类型常量(Untyped Constants),如 3.14 或 100。它们没有具体类型,直到被赋值给变量或用于需要类型的表达式时,才根据上下文确定类型。这使得常量可以隐式转换为兼容类型。
2.4 基本类型与类型转换
Go 是强类型语言,禁止隐式类型转换。不同基本类型之间必须显式转换。
1. 基本类型
- 整数:int, int8, int16, int32, int64, uint, uint8 (别名 byte), uint16, uint32, uint64
- 浮点数:float32, float64
- 复数:complex64, complex128
- 字符:byte (uint8 别名), rune (int32 别名)
- 布尔:bool
- 字符串:string
2. 显式类型转换
语法为 T(v),其中 T 是目标类型,v 是表达式。
- 数值转换:int(f) 会将浮点数截断为整数。
- 字符串转换:使用 strconv 包。例如 strconv.Atoi(string) 将字符串转为 int,strconv.Itoa(int) 将整数转为字符串。
重要规则:常量截断
无类型常量在转换为有类型变量时,如果发生精度丢失(如浮点转整点),编译器会报错。例如 int(3.14) 是非法的,因为 3.14 是无类型浮点常量,直接转 int 会丢失精度。必须先赋给有类型变量,再转换:var f float64 = 3.14; var i int = int(f)。
三、示例代码
示例 1:变量声明、零值与 iota 枚举
本示例演示了 var 与 := 的区别,展示了零值机制,并使用了 iota 定义颜色常量。
package main
import "fmt"
// 包级变量,使用 var 声明,零值为 0
var globalCounter int
func main() {
// 1. 变量声明
var name string // 零值为 ""
var age int = 18
localVar := "Hello" // 短声明,类型推导为 string
fmt.Printf("Name: %q, Age: %d, LocalVar: %s\n", name, age, localVar)
fmt.Printf("Global Counter (zero value): %d\n", globalCounter)
// 2. iota 枚举
type Color uint8
const (
Red Color = iota // 0
Green // 1
Blue // 2
)
fmt.Printf("Red: %d, Green: %d, Blue: %d\n", Red, Green, Blue)
}
运行结果(预期输出):
Name: "", Age: 18, LocalVar: Hello
Global Counter (zero value): 0
Red: 0, Green: 1, Blue: 2
示例 2:类型转换与 strconv 使用
本示例演示了数值类型的显式转换、常量转换的限制,以及使用 strconv 进行字符串与整数的互转。
package main
import (
"fmt"
"strconv"
)
func main() {
// 1. 数值类型显式转换
var f float64 = 3.14
// 注意:不能直接写 var i int = int(3.14),因为无类型常量 3.14 不能直接转 int
// 必须先赋给有类型变量,再转换
var i int = int(f)
fmt.Printf("Float: %f -> Int: %d\n", f, i)
// 2. 字符串与整数转换
strNum := "123"
num, err := strconv.Atoi(strNum)
if err != nil {
fmt.Printf("Error converting %s to int: %v\n", strNum, err)
return
}
num++
backToStr := strconv.Itoa(num)
fmt.Printf("String: %s -> Int: %d -> String: %s\n", strNum, num, backToStr)
// 3. 错误转换示例(注释掉,否则编译失败)
// var badInt int = int(3.14) // 编译错误: cannot convert 3.14 (untyped float constant) to type int
}
运行结果(预期输出):
Float: 3.140000 -> Int: 3
String: 123 -> Int: 124 -> String: 124
四、常见错误与避坑指南
-
错误写法/认知:认为
var i int = 3.14是合法的,因为 Go 允许隐式转换。 - 原因:Go 禁止有类型值之间的隐式转换。3.14是无类型浮点常量,但在赋值给int类型变量时,编译器会检查精度丢失,从而报错cannot use 3.14 (untyped float constant) as int value in variable declaration (truncated)。只有无类型常量在兼容类型间(如var f float64 = 1)才能隐式转换。 - 正确做法:显式转换。var f float64 = 3.14; var i int = int(f)。 -
错误写法/认知:在
var块中使用iota。 - 原因:iota是const块特有的枚举计数器。var块中不支持iota,写var ( A = iota )会导致语法错误syntax error: unexpected newline, expected type。 - 正确做法:iota仅用于const块。包级变量用var (...)分组,但内部不使用iota。 -
错误写法/认知:直接使用
int(3.14)作为常量转换。 - 原因:无类型常量3.14不能直接转换为int,因为会丢失小数部分。编译器会报错cannot convert 3.14 (untyped float constant) to type int。 - 正确做法:先赋给有类型变量,再转换。var f float64 = 3.14; var i int = int(f)。
五、最佳实践
- 优先使用
:=声明局部变量:在函数内部,使用:=可以让代码更简洁,且类型推导通常准确无误。仅在需要明确类型或声明包级变量时使用var。 - 利用零值机制简化初始化:不要显式将变量初始化为
0或"",除非有特定语义。Go 的零值通常是安全的默认值。 iota用于枚举常量:当需要定义一组相关的常量(如状态码、颜色、错误码)时,使用const块配合iota,避免魔法数字。- 显式类型转换:始终使用
T(v)进行类型转换,避免依赖隐式转换(Go 中几乎不存在)。对于数值转换,注意截断风险。 - 使用
strconv进行字符串转换:不要尝试手动解析字符串,使用标准库strconv包,它经过优化且处理了边界情况。
六、练习题
- 概念题:解释零值机制,并给出
int、string、bool、float64的零值,以及哪些类型(指针/切片/映射/通道/函数/接口)的零值是nil。 - 编码题:编写一个函数
convertAndPrint,接收一个float64参数,将其转换为int,然后使用strconv.Itoa将结果转换为字符串,最后打印该字符串。 - 进阶/调试题:以下代码试图将浮点常量直接转换为整数,请指出错误并修复。 ```go package main
import "fmt"
func main() { var discount int = int(3.14) fmt.Printf("Discount: %d\n", discount) } ```
七、练习参考答案
参考答案(点击展开 / 收起)
第 1 题
答案:
零值是 Go 语言中变量在未显式初始化时的默认值。
- int 的零值是 0。
- string 的零值是 ""(空字符串)。
- bool 的零值是 false。
- float64 的零值是 0.0。
- 指针、切片、映射、通道、函数、接口的零值是 nil。
第 2 题
package main
import (
"fmt"
"strconv"
)
func convertAndPrint(f float64) {
// 显式转换为 int,截断小数部分
i := int(f)
// 将 int 转换为 string
s := strconv.Itoa(i)
fmt.Println(s)
}
func main() {
convertAndPrint(3.14) // 输出: 3
}
说明: 函数接收 float64,通过 int(f) 显式转换并截断,再通过 strconv.Itoa 转为字符串打印。
第 3 题
问题所在:
代码 var discount int = int(3.14) 编译错误。原因是无类型常量 3.14 不能直接转换为 int 类型,因为会丢失精度(截断)。Go 编译器不允许这种隐式的精度丢失转换。
修复后的完整代码:
package main
import "fmt"
func main() {
// 正确做法:先赋给有类型变量,再转换
var f float64 = 3.14
var discount int = int(f)
fmt.Printf("Discount: %d\n", discount)
}
八、本课小结
- Go 提供
var和:=两种变量声明方式,:=更简洁但仅限函数内部。 - 所有变量都有明确的零值,引用类型(指针、切片等)的零值为
nil。 iota是const块中的枚举计数器,从 0 开始递增,仅适用于常量。- Go 禁止隐式类型转换,必须使用
T(v)显式转换,且常量转换不允许丢失精度。 strconv包是进行字符串与数值转换的标准工具。
下一课预告:Go 语言中的字符串操作与常用技巧。