Go 语言变量
变量是程序中用来存储数据的容器。Go 语言提供了多种声明和初始化变量的方式。本章将详细介绍 Go 语言中变量的声明、初始化、作用域等核心概念。
📋 变量声明
基本声明语法
var 关键字声明
go
package main
import "fmt"
func main() {
// 1. 声明变量但不初始化(使用零值)
var name string
var age int
var height float64
var isStudent bool
fmt.Printf("姓名: %q\n", name) // ""
fmt.Printf("年龄: %d\n", age) // 0
fmt.Printf("身高: %g\n", height) // 0
fmt.Printf("是否学生: %t\n", isStudent) // false
// 2. 声明时指定类型和初始值
var userName string = "张三"
var userAge int = 25
var userHeight float64 = 1.75
var userIsStudent bool = true
fmt.Printf("用户姓名: %s\n", userName)
fmt.Printf("用户年龄: %d\n", userAge)
fmt.Printf("用户身高: %.2f\n", userHeight)
fmt.Printf("是否学生: %t\n", userIsStudent)
}类型推断
go
func main() {
// 3. 省略类型,由编译器推断
var name = "李四" // 推断为 string
var age = 30 // 推断为 int
var salary = 5000.50 // 推断为 float64
var married = false // 推断为 bool
fmt.Printf("姓名: %s (类型: %T)\n", name, name)
fmt.Printf("年龄: %d (类型: %T)\n", age, age)
fmt.Printf("薪水: %.2f (类型: %T)\n", salary, salary)
fmt.Printf("婚否: %t (类型: %T)\n", married, married)
}多变量声明
同类型多变量声明
go
func main() {
// 1. 同时声明多个同类型变量
var x, y, z int
fmt.Printf("x: %d, y: %d, z: %d\n", x, y, z) // 0, 0, 0
// 2. 同时声明并初始化
var a, b, c int = 1, 2, 3
fmt.Printf("a: %d, b: %d, c: %d\n", a, b, c) // 1, 2, 3
// 3. 类型推断的多变量声明
var name, city = "王五", "北京"
var count, price = 10, 99.99
fmt.Printf("姓名: %s, 城市: %s\n", name, city)
fmt.Printf("数量: %d, 价格: %.2f\n", count, price)
}不同类型多变量声明
go
func main() {
// 使用类型推断声明不同类型的变量
var (
name = "赵六"
age = 28
height = 1.80
married = true
)
fmt.Printf("个人信息:\n")
fmt.Printf(" 姓名: %s\n", name)
fmt.Printf(" 年龄: %d\n", age)
fmt.Printf(" 身高: %.2f\n", height)
fmt.Printf(" 婚否: %t\n", married)
}变量组声明
go
func main() {
// 使用 var() 进行分组声明
var (
// 服务器配置
serverHost string = "localhost"
serverPort int = 8080
serverSSL bool = false
// 数据库配置
dbHost = "127.0.0.1" // string
dbPort = 3306 // int
dbName = "testdb" // string
dbUser = "root" // string
dbPassword = "password" // string
)
fmt.Printf("服务器配置: %s:%d, SSL: %t\n", serverHost, serverPort, serverSSL)
fmt.Printf("数据库配置: %s:%d/%s, 用户: %s\n", dbHost, dbPort, dbName, dbUser)
}⚡ 短变量声明
基本用法
go
func main() {
// 短变量声明 :=(只能在函数内使用)
name := "短变量"
age := 20
price := 19.99
active := true
fmt.Printf("姓名: %s\n", name)
fmt.Printf("年龄: %d\n", age)
fmt.Printf("价格: %.2f\n", price)
fmt.Printf("激活: %t\n", active)
// 多变量短声明
x, y, z := 1, 2, 3
fmt.Printf("坐标: (%d, %d, %d)\n", x, y, z)
// 混合类型短声明
username, userid, score := "admin", 1001, 95.5
fmt.Printf("用户: %s, ID: %d, 分数: %.1f\n", username, userid, score)
}短变量声明的重新赋值
go
func main() {
// 短变量声明的特殊规则:重新声明
name := "原始名称"
fmt.Printf("初始姓名: %s\n", name)
// 如果左侧有新变量,可以重新声明已存在的变量
name, age := "新姓名", 25 // name 被重新赋值,age 是新声明
fmt.Printf("更新后: 姓名 %s, 年龄 %d\n", name, age)
// 但至少要有一个新变量
// name, age := "另一个名称", 30 // 错误!没有新变量
// 这样是正确的
name, age, city := "最终姓名", 30, "上海" // city 是新变量
fmt.Printf("最终: 姓名 %s, 年龄 %d, 城市 %s\n", name, age, city)
}🎯 变量作用域
包级别变量
go
package main
import "fmt"
// 包级别变量(全局变量)
var globalVar = "全局变量"
var (
appName = "我的应用"
appVersion = "1.0.0"
appAuthor = "开发者"
)
func main() {
fmt.Printf("全局变量: %s\n", globalVar)
fmt.Printf("应用信息: %s v%s by %s\n", appName, appVersion, appAuthor)
// 函数内可以访问全局变量
showGlobalVar()
// 局部变量
localVar := "局部变量"
fmt.Printf("局部变量: %s\n", localVar)
// 代码块作用域
{
blockVar := "代码块变量"
fmt.Printf("代码块内: %s\n", blockVar)
fmt.Printf("代码块内访问局部变量: %s\n", localVar)
fmt.Printf("代码块内访问全局变量: %s\n", globalVar)
}
// fmt.Printf("代码块外: %s\n", blockVar) // 错误!超出作用域
}
func showGlobalVar() {
fmt.Printf("函数中的全局变量: %s\n", globalVar)
}变量遮蔽
go
var x = "全局 x"
func main() {
fmt.Printf("全局 x: %s\n", x)
// 局部变量遮蔽全局变量
x := "局部 x"
fmt.Printf("局部 x: %s\n", x)
{
// 代码块变量遮蔽局部变量
x := "代码块 x"
fmt.Printf("代码块 x: %s\n", x)
{
// 更深层的遮蔽
x := "内层代码块 x"
fmt.Printf("内层代码块 x: %s\n", x)
}
fmt.Printf("回到代码块 x: %s\n", x)
}
fmt.Printf("回到局部 x: %s\n", x)
// 访问全局变量(如果需要)
showGlobal()
}
func showGlobal() {
fmt.Printf("函数中访问全局 x: %s\n", x)
}💡 变量初始化
零值初始化
go
func main() {
// Go 语言中所有变量都会被初始化为零值
var intVar int
var floatVar float64
var boolVar bool
var stringVar string
var pointerVar *int
var sliceVar []int
var mapVar map[string]int
var chanVar chan int
var funcVar func()
fmt.Printf("int 零值: %d\n", intVar)
fmt.Printf("float64 零值: %g\n", floatVar)
fmt.Printf("bool 零值: %t\n", boolVar)
fmt.Printf("string 零值: %q\n", stringVar)
fmt.Printf("pointer 零值: %v\n", pointerVar)
fmt.Printf("slice 零值: %v\n", sliceVar)
fmt.Printf("map 零值: %v\n", mapVar)
fmt.Printf("chan 零值: %v\n", chanVar)
fmt.Printf("func 零值: %v\n", funcVar)
}显式初始化
go
func main() {
// 声明时初始化
var name string = "初始值"
var count int = 100
var rate float64 = 0.95
var enabled bool = true
fmt.Printf("初始化的变量:\n")
fmt.Printf(" name: %s\n", name)
fmt.Printf(" count: %d\n", count)
fmt.Printf(" rate: %.2f\n", rate)
fmt.Printf(" enabled: %t\n", enabled)
// 后续赋值
name = "修改后的值"
count = 200
rate = 0.85
enabled = false
fmt.Printf("修改后的变量:\n")
fmt.Printf(" name: %s\n", name)
fmt.Printf(" count: %d\n", count)
fmt.Printf(" rate: %.2f\n", rate)
fmt.Printf(" enabled: %t\n", enabled)
}🔄 变量赋值
基本赋值
go
func main() {
// 声明后赋值
var name string
name = "赋值示例"
var age int
age = 25
fmt.Printf("姓名: %s, 年龄: %d\n", name, age)
// 重新赋值
name = "新姓名"
age = 30
fmt.Printf("更新后: 姓名: %s, 年龄: %d\n", name, age)
}多重赋值
go
func main() {
// 多重赋值(并行赋值)
var a, b, c int
a, b, c = 1, 2, 3
fmt.Printf("a: %d, b: %d, c: %d\n", a, b, c)
// 交换变量值
a, b = b, a
fmt.Printf("交换后 a: %d, b: %d\n", a, b)
// 函数返回多个值的赋值
name, age := getPersonInfo()
fmt.Printf("个人信息: %s, %d岁\n", name, age)
// 忽略某些返回值
name2, _ := getPersonInfo() // 忽略年龄
fmt.Printf("只要姓名: %s\n", name2)
}
func getPersonInfo() (string, int) {
return "张三", 28
}递增和递减
go
func main() {
// 递增递减运算符
var counter int = 10
fmt.Printf("初始值: %d\n", counter)
counter++ // 递增
fmt.Printf("递增后: %d\n", counter)
counter-- // 递减
fmt.Printf("递减后: %d\n", counter)
// 注意:Go 中没有前置递增/递减
// ++counter // 错误
// --counter // 错误
// 也不能用作表达式
// var x = counter++ // 错误
// 只能作为语句使用
counter++
var x = counter // 正确
fmt.Printf("x: %d\n", x)
}🌍 变量的生命周期
局部变量生命周期
go
func main() {
fmt.Println("main 函数开始")
{
fmt.Println("进入代码块")
localVar := "局部变量"
fmt.Printf("创建局部变量: %s\n", localVar)
processVar(localVar)
// localVar 在代码块结束时被销毁
fmt.Println("即将离开代码块")
}
// fmt.Println(localVar) // 错误!变量已不存在
fmt.Println("main 函数结束")
}
func processVar(data string) {
fmt.Printf("处理变量: %s\n", data)
tempVar := "临时变量"
fmt.Printf("临时变量: %s\n", tempVar)
// tempVar 在函数结束时被销毁
}变量逃逸分析
go
// 变量可能逃逸到堆上的情况
func createPointer() *int {
localVar := 42
return &localVar // localVar 逃逸到堆上
}
func main() {
// 局部变量,在栈上
stackVar := 100
fmt.Printf("栈变量: %d\n", stackVar)
// 逃逸变量,在堆上
heapVar := createPointer()
fmt.Printf("堆变量: %d\n", *heapVar)
}🎯 变量命名规范
命名风格
go
package main
import "fmt"
// 导出变量(首字母大写)
var PublicVariable = "公开变量"
// 包内变量(首字母小写)
var privateVariable = "私有变量"
func main() {
// 1. 使用驼峰命名法
var userName string = "用户名"
var userAge int = 25
var isUserActive bool = true
// 2. 常见缩写保持大写
var httpURL string = "http://example.com"
var jsonData string = `{"name": "test"}`
var xmlContent string = "<root></root>"
// 3. 简短的作用域使用简短的名称
for i := 0; i < 5; i++ {
fmt.Printf("索引: %d\n", i)
}
// 4. 布尔变量使用 is/has/can 等前缀
var isReady bool = true
var hasPermission bool = false
var canEdit bool = true
// 5. 避免无意义的名称
// 不好的命名
// var data string
// var info int
// var temp bool
// 好的命名
var customerName string = "客户姓名"
var orderCount int = 10
var paymentReceived bool = true
fmt.Printf("用户信息: %s, %d岁, 活跃: %t\n", userName, userAge, isUserActive)
fmt.Printf("状态: 准备就绪=%t, 有权限=%t, 可编辑=%t\n", isReady, hasPermission, canEdit)
fmt.Printf("业务信息: %s, 订单数=%d, 已付款=%t\n", customerName, orderCount, paymentReceived)
}变量命名最佳实践
go
func main() {
// ✅ 好的命名示例
var (
// 清晰表达用途
maxRetryCount = 3
connectionTimeout = 30
serverAddress = "localhost:8080"
// 布尔变量有明确的判断含义
isConnected = false
hasErrors = false
canRetry = true
shouldLog = true
// 集合使用复数形式
userNames = []string{"张三", "李四"}
errorMessages = []string{}
configSettings = map[string]string{}
// 计数器和索引
userCount = len(userNames)
currentIndex = 0
totalProcessed = 0
)
// ❌ 应该避免的命名
// var d string // 太短,无意义
// var temp int // 太通用
// var data interface{} // 太抽象
// var mgr *Manager // 缩写不清楚
fmt.Printf("服务器: %s, 超时: %d秒\n", serverAddress, connectionTimeout)
fmt.Printf("用户数: %d, 当前索引: %d\n", userCount, currentIndex)
fmt.Printf("连接状态: %t, 可重试: %t\n", isConnected, canRetry)
}🎯 实践示例
用户信息管理系统
go
package main
import (
"fmt"
"time"
)
// 全局配置变量
var (
systemName = "用户管理系统"
systemVersion = "1.0.0"
maxUsers = 1000
)
func main() {
fmt.Printf("=== %s v%s ===\n", systemName, systemVersion)
// 用户信息变量
var (
userID int
userName string
userEmail string
userAge int
isActive bool
lastLoginTime time.Time
)
// 初始化用户信息
userID = 1001
userName = "张三"
userEmail = "zhangsan@example.com"
userAge = 28
isActive = true
lastLoginTime = time.Now()
// 显示用户信息
displayUserInfo(userID, userName, userEmail, userAge, isActive, lastLoginTime)
// 批量创建用户
users := createMultipleUsers()
fmt.Printf("\n创建了 %d 个用户:\n", len(users))
for i, user := range users {
fmt.Printf("%d. %s\n", i+1, user)
}
// 系统统计
var (
totalUsers = len(users) + 1 // 包含第一个用户
activeUsers = 0
inactiveUsers = 0
)
// 简单统计(示例)
if isActive {
activeUsers++
} else {
inactiveUsers++
}
activeUsers += len(users) // 假设批量创建的都是活跃用户
fmt.Printf("\n=== 系统统计 ===\n")
fmt.Printf("总用户数: %d\n", totalUsers)
fmt.Printf("活跃用户: %d\n", activeUsers)
fmt.Printf("非活跃用户: %d\n", inactiveUsers)
fmt.Printf("系统容量: %d (剩余: %d)\n", maxUsers, maxUsers-totalUsers)
}
func displayUserInfo(id int, name, email string, age int, active bool, lastLogin time.Time) {
fmt.Printf("\n=== 用户信息 ===\n")
fmt.Printf("ID: %d\n", id)
fmt.Printf("姓名: %s\n", name)
fmt.Printf("邮箱: %s\n", email)
fmt.Printf("年龄: %d\n", age)
fmt.Printf("状态: %s\n", getActiveStatus(active))
fmt.Printf("最后登录: %s\n", lastLogin.Format("2006-01-02 15:04:05"))
}
func getActiveStatus(active bool) string {
if active {
return "活跃"
}
return "非活跃"
}
func createMultipleUsers() []string {
// 使用短变量声明创建用户列表
users := []string{}
// 批量添加用户
userNames := []string{"李四", "王五", "赵六", "孙七"}
for _, name := range userNames {
userInfo := fmt.Sprintf("%s (ID: %d)", name, 1000+len(users)+2)
users = append(users, userInfo)
}
return users
}🎓 小结
本章我们全面学习了 Go 语言中变量的使用:
- ✅ 变量声明:var 关键字、类型推断、多变量声明
- ✅ 短变量声明::= 操作符的使用和注意事项
- ✅ 变量作用域:全局变量、局部变量、变量遮蔽
- ✅ 变量初始化:零值、显式初始化
- ✅ 变量赋值:基本赋值、多重赋值、递增递减
- ✅ 变量生命周期:栈与堆、逃逸分析
- ✅ 命名规范:命名风格和最佳实践
掌握变量的正确使用是 Go 编程的基础技能,良好的变量命名和作用域管理能够提高代码的可读性和维护性。
接下来,我们将学习 Go 语言常量,了解如何定义和使用不可变的值。
变量使用建议
- 优先使用短变量声明
:=在函数内声明变量 - 使用有意义的变量名,避免无意义的缩写
- 合理利用变量的作用域,避免不必要的全局变量
- 注意变量的生命周期,避免内存泄漏