Skip to content

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 语言常量,了解如何定义和使用不可变的值。

变量使用建议

  • 优先使用短变量声明 := 在函数内声明变量
  • 使用有意义的变量名,避免无意义的缩写
  • 合理利用变量的作用域,避免不必要的全局变量
  • 注意变量的生命周期,避免内存泄漏

本站内容仅供学习和研究使用。