第 4/41 课 · Go 语言入门与开发环境 入门 m01-l04

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

四、常见错误与避坑指南

  1. 错误写法/认知:认为 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)。

  2. 错误写法/认知:在 var 块中使用 iota。 - 原因:iota 是 const 块特有的枚举计数器。var 块中不支持 iota,写 var ( A = iota ) 会导致语法错误 syntax error: unexpected newline, expected type。 - 正确做法:iota 仅用于 const 块。包级变量用 var (...) 分组,但内部不使用 iota。

  3. 错误写法/认知:直接使用 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)。

五、最佳实践

  1. 优先使用 := 声明局部变量:在函数内部,使用 := 可以让代码更简洁,且类型推导通常准确无误。仅在需要明确类型或声明包级变量时使用 var。
  2. 利用零值机制简化初始化:不要显式将变量初始化为 0 或 "",除非有特定语义。Go 的零值通常是安全的默认值。
  3. iota 用于枚举常量:当需要定义一组相关的常量(如状态码、颜色、错误码)时,使用 const 块配合 iota,避免魔法数字。
  4. 显式类型转换:始终使用 T(v) 进行类型转换,避免依赖隐式转换(Go 中几乎不存在)。对于数值转换,注意截断风险。
  5. 使用 strconv 进行字符串转换:不要尝试手动解析字符串,使用标准库 strconv 包,它经过优化且处理了边界情况。

六、练习题

  1. 概念题:解释零值机制,并给出 int、string、bool、float64 的零值,以及哪些类型(指针/切片/映射/通道/函数/接口)的零值是 nil。
  2. 编码题:编写一个函数 convertAndPrint,接收一个 float64 参数,将其转换为 int,然后使用 strconv.Itoa 将结果转换为字符串,最后打印该字符串。
  3. 进阶/调试题:以下代码试图将浮点常量直接转换为整数,请指出错误并修复。 ```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 语言中的字符串操作与常用技巧。

问题反馈

本站是纯前端静态站,没有后端也没有账号系统,反馈走 GitHub Issues、讨论区或邮件。

粘贴到 issue 或邮件里能帮我更快定位问题,其中不含你输入的任何内容

其它:查看已有反馈 · 邮件反馈(无需 GitHub 账号):278975598@qq.com