1.2.1 变量与常量 #
变量和常量是编程语言的基础概念。在 Go 语言中,变量和常量的声明和使用有其独特的语法和规则。本节将详细介绍 Go 语言中变量和常量的各种用法。
变量声明 #
1. 使用 var 关键字 #
Go 语言使用 var
关键字声明变量,基本语法为:
var 变量名 类型
基本声明示例:
package main
import "fmt"
func main() {
// 声明单个变量
var name string
var age int
var isStudent bool
fmt.Printf("name: %q, age: %d, isStudent: %t\n", name, age, isStudent)
// 输出: name: "", age: 0, isStudent: false
}
2. 变量初始化 #
可以在声明变量的同时进行初始化:
package main
import "fmt"
func main() {
// 声明并初始化
var name string = "Alice"
var age int = 25
var height float64 = 165.5
fmt.Printf("姓名: %s, 年龄: %d, 身高: %.1f cm\n", name, age, height)
// 批量声明并初始化
var (
username = "bob"
password = "123456"
email = "[email protected]"
)
fmt.Printf("用户名: %s, 密码: %s, 邮箱: %s\n", username, password, email)
}
3. 类型推断 #
Go 编译器可以根据初始值自动推断变量类型:
package main
import "fmt"
func main() {
// 类型推断 - 省略类型声明
var name = "Charlie" // 推断为 string
var age = 30 // 推断为 int
var salary = 50000.50 // 推断为 float64
var isManager = true // 推断为 bool
fmt.Printf("name: %T, age: %T, salary: %T, isManager: %T\n",
name, age, salary, isManager)
// 输出: name: string, age: int, salary: float64, isManager: bool
}
4. 短变量声明 #
在函数内部,可以使用 :=
操作符进行短变量声明:
package main
import "fmt"
func main() {
// 短变量声明(只能在函数内使用)
name := "David"
age := 28
city := "北京"
fmt.Printf("姓名: %s, 年龄: %d, 城市: %s\n", name, age, city)
// 多变量同时声明
x, y, z := 1, 2, 3
fmt.Printf("x: %d, y: %d, z: %d\n", x, y, z)
// 混合赋值(至少有一个新变量)
name, country := "Eva", "中国" // name 重新赋值,country 新声明
fmt.Printf("姓名: %s, 国家: %s\n", name, country)
}
5. 零值概念 #
Go 语言中,所有变量都有零值(zero value):
package main
import "fmt"
func main() {
// 各种类型的零值
var (
intVar int
floatVar float64
boolVar bool
stringVar string
pointerVar *int
sliceVar []int
mapVar map[string]int
chanVar chan int
)
fmt.Printf("int零值: %d\n", intVar) // 0
fmt.Printf("float64零值: %f\n", floatVar) // 0.000000
fmt.Printf("bool零值: %t\n", boolVar) // false
fmt.Printf("string零值: %q\n", stringVar) // ""
fmt.Printf("pointer零值: %v\n", pointerVar) // <nil>
fmt.Printf("slice零值: %v\n", sliceVar) // []
fmt.Printf("map零值: %v\n", mapVar) // map[]
fmt.Printf("channel零值: %v\n", chanVar) // <nil>
}
6. 变量作用域 #
package main
import "fmt"
// 包级别变量
var globalVar = "我是全局变量"
func main() {
// 函数级别变量
localVar := "我是局部变量"
fmt.Println(globalVar)
fmt.Println(localVar)
// 块级作用域
if true {
blockVar := "我是块级变量"
fmt.Println(blockVar)
fmt.Println(localVar) // 可以访问外层变量
}
// fmt.Println(blockVar) // 错误:blockVar 在此作用域不可见
// for 循环中的变量作用域
for i := 0; i < 3; i++ {
fmt.Printf("循环变量 i: %d\n", i)
}
// fmt.Println(i) // 错误:i 在此作用域不可见
}
常量声明 #
1. 基本常量声明 #
使用 const
关键字声明常量:
package main
import "fmt"
func main() {
// 单个常量声明
const pi = 3.14159
const greeting = "Hello, World!"
// 显式类型声明
const maxUsers int = 1000
const version string = "1.0.0"
fmt.Printf("π = %.5f\n", pi)
fmt.Printf("问候语: %s\n", greeting)
fmt.Printf("最大用户数: %d\n", maxUsers)
fmt.Printf("版本: %s\n", version)
}
2. 常量组 #
可以使用括号声明一组常量:
package main
import "fmt"
func main() {
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
const (
Red = "红色"
Green = "绿色"
Blue = "蓝色"
)
fmt.Printf("HTTP状态码 - OK: %d, NotFound: %d, Error: %d\n",
StatusOK, StatusNotFound, StatusError)
fmt.Printf("颜色 - 红: %s, 绿: %s, 蓝: %s\n", Red, Green, Blue)
}
3. iota 枚举器 #
iota
是 Go 语言的常量计数器,用于生成枚举值:
package main
import "fmt"
func main() {
// 基本 iota 使用
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
fmt.Printf("星期日: %d, 星期一: %d, 星期二: %d\n", Sunday, Monday, Tuesday)
// iota 表达式
const (
_ = iota // 0 (忽略)
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
TB // 1 << 40
)
fmt.Printf("KB: %d, MB: %d, GB: %d\n", KB, MB, GB)
// 复杂的 iota 表达式
const (
a = iota * 2 // 0 * 2 = 0
b // 1 * 2 = 2
c // 2 * 2 = 4
d = iota + 10 // 3 + 10 = 13
e // 4 + 10 = 14
)
fmt.Printf("a: %d, b: %d, c: %d, d: %d, e: %d\n", a, b, c, d, e)
}
4. 类型化常量和无类型常量 #
package main
import "fmt"
func main() {
// 无类型常量(更灵活)
const untypedInt = 42
const untypedFloat = 3.14
const untypedString = "hello"
// 类型化常量
const typedInt int = 42
const typedFloat float64 = 3.14
const typedString string = "hello"
// 无类型常量可以赋值给兼容的类型
var i8 int8 = untypedInt
var i16 int16 = untypedInt
var i32 int32 = untypedInt
var i64 int64 = untypedInt
fmt.Printf("i8: %d, i16: %d, i32: %d, i64: %d\n", i8, i16, i32, i64)
// 类型化常量只能赋值给相同类型
var typedVar int = typedInt
// var wrongVar int8 = typedInt // 错误:类型不匹配
fmt.Printf("typedVar: %d\n", typedVar)
}
变量和常量的最佳实践 #
1. 命名规范 #
package main
import "fmt"
// 包级别的导出变量(首字母大写)
var GlobalConfig = "全局配置"
// 包级别的私有变量(首字母小写)
var internalState = "内部状态"
func main() {
// 使用有意义的变量名
userAge := 25
userName := "张三"
isLoggedIn := true
// 避免使用单字母变量名(除了短循环)
for i := 0; i < 5; i++ { // i 在短循环中是可接受的
fmt.Printf("循环 %d\n", i)
}
// 常量使用大写字母和下划线
const MAX_RETRY_COUNT = 3
const DEFAULT_TIMEOUT = 30
fmt.Printf("用户: %s, 年龄: %d, 已登录: %t\n", userName, userAge, isLoggedIn)
fmt.Printf("最大重试次数: %d, 默认超时: %d秒\n", MAX_RETRY_COUNT, DEFAULT_TIMEOUT)
}
2. 变量初始化模式 #
package main
import (
"fmt"
"time"
)
func main() {
// 延迟初始化
var config map[string]string
if needConfig() {
config = make(map[string]string)
config["host"] = "localhost"
config["port"] = "8080"
}
// 条件初始化
var message string
if time.Now().Hour() < 12 {
message = "早上好"
} else {
message = "下午好"
}
// 多重赋值
name, age := getUserInfo()
fmt.Printf("配置: %v\n", config)
fmt.Printf("问候: %s\n", message)
fmt.Printf("用户信息: %s, %d岁\n", name, age)
}
func needConfig() bool {
return true
}
func getUserInfo() (string, int) {
return "李四", 30
}
3. 常量的组织方式 #
package main
import "fmt"
// 相关常量分组
const (
// HTTP 状态码
StatusOK = 200
StatusBadRequest = 400
StatusUnauthorized = 401
StatusNotFound = 404
StatusInternalServerError = 500
)
const (
// 用户角色
RoleAdmin = iota
RoleUser
RoleGuest
)
const (
// 配置常量
DefaultPort = 8080
DefaultHost = "localhost"
DefaultTimeout = 30 * time.Second
MaxConnections = 1000
)
func main() {
fmt.Printf("服务器配置: %s:%d\n", DefaultHost, DefaultPort)
fmt.Printf("用户角色 - 管理员: %d, 普通用户: %d, 访客: %d\n",
RoleAdmin, RoleUser, RoleGuest)
fmt.Printf("HTTP状态码 - 成功: %d, 未找到: %d\n", StatusOK, StatusNotFound)
}
实际应用示例 #
配置管理系统 #
package main
import (
"fmt"
"os"
"strconv"
"time"
)
// 应用配置常量
const (
AppName = "MyWebApp"
AppVersion = "1.2.3"
// 默认配置值
DefaultPort = 8080
DefaultHost = "0.0.0.0"
DefaultTimeout = 30 * time.Second
DefaultMaxRequests = 1000
)
// 配置结构
type Config struct {
Host string
Port int
Timeout time.Duration
MaxRequests int
Debug bool
}
func main() {
// 从环境变量或使用默认值初始化配置
config := loadConfig()
fmt.Printf("应用: %s v%s\n", AppName, AppVersion)
fmt.Printf("配置信息:\n")
fmt.Printf(" 主机: %s\n", config.Host)
fmt.Printf(" 端口: %d\n", config.Port)
fmt.Printf(" 超时: %v\n", config.Timeout)
fmt.Printf(" 最大请求数: %d\n", config.MaxRequests)
fmt.Printf(" 调试模式: %t\n", config.Debug)
}
func loadConfig() Config {
config := Config{
Host: DefaultHost,
Port: DefaultPort,
Timeout: DefaultTimeout,
MaxRequests: DefaultMaxRequests,
Debug: false,
}
// 从环境变量读取配置
if host := os.Getenv("APP_HOST"); host != "" {
config.Host = host
}
if portStr := os.Getenv("APP_PORT"); portStr != "" {
if port, err := strconv.Atoi(portStr); err == nil {
config.Port = port
}
}
if debugStr := os.Getenv("APP_DEBUG"); debugStr == "true" {
config.Debug = true
}
return config
}
状态机实现 #
package main
import "fmt"
// 状态常量
const (
StateIdle = iota
StateRunning
StatePaused
StateStopped
StateError
)
// 状态名称映射
var stateNames = map[int]string{
StateIdle: "空闲",
StateRunning: "运行中",
StatePaused: "已暂停",
StateStopped: "已停止",
StateError: "错误",
}
// 任务结构
type Task struct {
name string
state int
}
func main() {
// 创建任务
task := Task{
name: "数据处理任务",
state: StateIdle,
}
// 状态转换演示
fmt.Printf("任务: %s\n", task.name)
task.start()
task.pause()
task.resume()
task.stop()
}
func (t *Task) start() {
if t.state == StateIdle {
t.state = StateRunning
fmt.Printf("状态变更: %s -> %s\n", stateNames[StateIdle], stateNames[StateRunning])
}
}
func (t *Task) pause() {
if t.state == StateRunning {
t.state = StatePaused
fmt.Printf("状态变更: %s -> %s\n", stateNames[StateRunning], stateNames[StatePaused])
}
}
func (t *Task) resume() {
if t.state == StatePaused {
t.state = StateRunning
fmt.Printf("状态变更: %s -> %s\n", stateNames[StatePaused], stateNames[StateRunning])
}
}
func (t *Task) stop() {
if t.state == StateRunning || t.state == StatePaused {
oldState := t.state
t.state = StateStopped
fmt.Printf("状态变更: %s -> %s\n", stateNames[oldState], stateNames[StateStopped])
}
}
常见错误和注意事项 #
1. 变量遮蔽(Variable Shadowing) #
package main
import "fmt"
var globalVar = "全局变量"
func main() {
fmt.Printf("外层: %s\n", globalVar)
// 变量遮蔽
if true {
globalVar := "局部变量" // 遮蔽了全局变量
fmt.Printf("内层: %s\n", globalVar)
}
fmt.Printf("外层: %s\n", globalVar) // 仍然是全局变量
// 正确的做法
if true {
globalVar = "修改全局变量" // 修改全局变量
fmt.Printf("修改后: %s\n", globalVar)
}
}
2. 短变量声明的陷阱 #
package main
import "fmt"
func main() {
var err error
// 错误:这里创建了新的 err 变量,而不是使用上面声明的
if data, err := readData(); err != nil {
fmt.Printf("错误: %v\n", err)
return
} else {
fmt.Printf("数据: %s\n", data)
}
// 这里的 err 仍然是 nil
fmt.Printf("外层 err: %v\n", err)
// 正确的做法
var data string
data, err = readData()
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Printf("数据: %s\n", data)
}
func readData() (string, error) {
return "测试数据", nil
}
3. 常量的限制 #
package main
import "fmt"
func main() {
// 常量必须在编译时确定
const validConst = 10 * 20 // 正确
// const invalidConst = len("hello") // 错误:len() 不是常量表达式
// const invalidConst2 = time.Now() // 错误:time.Now() 不是常量表达式
// 常量不能修改
const pi = 3.14159
// pi = 3.14 // 错误:不能给常量赋值
fmt.Printf("有效常量: %d\n", validConst)
fmt.Printf("π: %.5f\n", pi)
}
小结 #
本节详细介绍了 Go 语言中变量和常量的声明、初始化和使用方法。主要要点包括:
变量 #
- 使用
var
关键字或:=
短变量声明 - 支持类型推断和零值初始化
- 注意变量作用域和遮蔽问题
常量 #
- 使用
const
关键字声明 - 支持
iota
枚举器生成序列值 - 区分类型化常量和无类型常量
最佳实践 #
- 使用有意义的变量名
- 合理组织常量定义
- 避免变量遮蔽
- 正确使用短变量声明
掌握变量和常量的使用是学习 Go 语言的基础,为后续学习数据类型和控制结构打下坚实基础。
练习题:
- 声明一个包含个人信息的变量组,包括姓名、年龄、城市
- 使用 iota 创建一个表示文件权限的常量组(读、写、执行)
- 编写一个函数,演示变量作用域的不同层级
- 创建一个配置管理的示例,结合环境变量和默认常量
- 实现一个简单的状态机,使用常量表示不同状态