强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Go 语言完全指南 / 04 - 变量与类型:基本类型、零值、类型推导、常量

04 - 变量与类型

4.1 基本类型

Go 是静态类型语言,所有变量在编译期都有确定的类型。

整数类型

类型大小范围说明
int81 字节-128 ~ 127有符号
int162 字节-32768 ~ 32767有符号
int324 字节-2^31 ~ 2^31-1有符号,等同 rune
int648 字节-2^63 ~ 2^63-1有符号
uint81 字节0 ~ 255无符号,等同 byte
uint162 字节0 ~ 65535无符号
uint324 字节0 ~ 2^32-1无符号
uint648 字节0 ~ 2^64-1无符号
int平台相关平台相关32 或 64 位
uint平台相关平台相关32 或 64 位
uintptr平台相关指针运算用

浮点与复数

类型大小说明
float324 字节IEEE 754 单精度
float648 字节IEEE 754 双精度(推荐)
complex648 字节float32 实部 + float32 虚部
complex12816 字节float64 实部 + float64 虚部

其他基本类型

类型说明示例
bool布尔值true, false
stringUTF-8 字符串"你好"
byteuint8 的别名'A'
runeint32 的别名,表示 Unicode 码点'中'

4.2 变量声明

var 声明

package main

import "fmt"

// 包级变量
var globalVar string = "I'm global"
var packageLevel = "type inferred"

func main() {
    // 完整声明
    var name string = "Go"
    var age int = 15
    var pi float64 = 3.14159
    var active bool = true

    // 类型推导
    var language = "Go"   // 自动推导为 string
    var version = 1.24    // 自动推导为 float64

    // 批量声明
    var (
        firstName = "Rob"
        lastName  = "Pike"
        height    = 180
    )

    fmt.Println(name, age, pi, active)
    fmt.Println(language, version)
    fmt.Println(firstName, lastName, height)
}

短变量声明 :=

func main() {
    // := 只能在函数内使用
    name := "Go"
    age := 15
    pi := 3.14159
    active := true

    // 多变量同时声明
    x, y := 10, 20
    fmt.Println(x, y)

    // 交换两个变量
    x, y = y, x
    fmt.Println(x, y) // 20, 10

    // 函数多返回值
    name, err := os.Hostname()
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(name)
}

变量声明对比

语法作用域是否可省略类型使用场景
var x int函数/包级明确类型时
var x = 10函数/包级类型推导
var x int = 10函数/包级完整声明
x := 10仅函数内局部变量(推荐)

⚠️ 注意

  • := 只能在函数内使用,包级变量必须用 var
  • := 至少要有一个新变量,否则编译错误
  • 变量声明后必须使用,否则编译错误

4.3 零值(Zero Value)

Go 中声明但未赋值的变量有默认零值:

类型零值说明
int, float640, 0.0数值零
boolfalse
string""空字符串
pointernil空指针
slicenil空切片(可用)
mapnil空 map(不可写,需 make)
channelnil空通道
funcnil空函数
interfacenil空接口
struct字段全为零值递归零值
package main

import "fmt"

type Person struct {
    Name string
    Age  int
    Active bool
}

func main() {
    var i int
    var f float64
    var b bool
    var s string
    var p *int
    var sl []int
    var m map[string]int
    var ch chan int
    var fn func()
    var person Person

    fmt.Printf("int:      %v\n", i)        // 0
    fmt.Printf("float64:  %v\n", f)        // 0
    fmt.Printf("bool:     %v\n", b)        // false
    fmt.Printf("string:   %q\n", s)        // ""
    fmt.Printf("pointer:  %v\n", p)        // <nil>
    fmt.Printf("slice:    %v, len=%d\n", sl, len(sl)) // [], len=0
    fmt.Printf("map:      %v\n", m)        // map[]
    fmt.Printf("chan:     %v\n", ch)        // <nil>
    fmt.Printf("func:     %v\n", fn)        // <nil>
    fmt.Printf("struct:   %+v\n", person)   // {Name: Age:0 Active:false}
}

💡 技巧:Go 的零值设计使得很多场景不需要显式初始化。例如 var buf bytes.Buffer 直接可用,不需要构造函数。

4.4 类型转换

Go 没有隐式类型转换,所有转换必须显式进行。

package main

import (
    "fmt"
    "math"
    "strconv"
)

func main() {
    // 数值类型转换
    var i int = 42
    var f float64 = float64(i)
    var u uint = uint(f)
    fmt.Println(i, f, u) // 42 42 42

    // 精度损失
    var bigFloat float64 = 3.99
    var intVal int = int(bigFloat) // 截断,不是四舍五入
    fmt.Println(intVal) // 3

    // 数值溢出(编译器不一定报错)
    var big int64 = math.MaxInt64
    var small int32 = int32(big) // 溢出,结果不可预期
    fmt.Println(small)

    // string ↔ []byte
    str := "Hello, 世界"
    bytes := []byte(str)
    str2 := string(bytes)
    fmt.Println(bytes, str2)

    // string ↔ 数值
    s := "12345"
    n, err := strconv.Atoi(s)      // string → int
    if err != nil { panic(err) }
    s2 := strconv.Itoa(n)           // int → string
    fmt.Println(n, s2)

    // 更多 strconv 转换
    f2, _ := strconv.ParseFloat("3.14", 64)
    b2, _ := strconv.ParseBool("true")
    fmt.Println(f2, b2)
}

4.5 类型别名与类型定义

package main

import "fmt"

// 类型定义 —— 创建新类型
type Celsius float64
type Fahrenheit float64
type UserID int64

// 类型别名 —— 只是别名,不是新类型
type Byte = uint8
type Rune = int32

// 为自定义类型添加方法
func (c Celsius) ToFahrenheit() Fahrenheit {
    return Fahrenheit(c*9/5 + 32)
}

func (f Fahrenheit) ToCelsius() Celsius {
    return Celsius((f - 32) * 5 / 9)
}

func main() {
    var boiling Celsius = 100
    fmt.Printf("%.1f°C = %.1f°F\n", boiling, boiling.ToFahrenheit())

    // 类型定义:不能隐式转换
    var uid UserID = 123
    var id int64 = int64(uid) // 必须显式转换
    fmt.Println(uid, id)

    // 类型别名:可以隐式转换
    var b Byte = 65
    var u uint8 = b // OK,Byte 就是 uint8
    fmt.Println(b, u)
}

4.6 常量

基本常量

package main

import "fmt"

// 单个常量
const Pi = 3.141592653589793
const E = 2.718281828459045

// 批量声明
const (
    StatusOK       = 200
    StatusNotFound = 404
    StatusError    = 500
)

// 类型常量
const timeout int = 30 // 秒

// 常量可以是编译期计算的表达式
const (
    KB = 1024
    MB = KB * 1024
    GB = MB * 1024
    TB = GB * 1024
)

func main() {
    fmt.Printf("Pi = %.15f\n", Pi)
    fmt.Printf("1 GB = %d bytes\n", GB)
}

iota —— 枚举常量生成器

package main

import "fmt"

// 基本 iota
type Weekday int

const (
    Sunday    Weekday = iota // 0
    Monday                    // 1
    Tuesday                   // 2
    Wednesday                 // 3
    Thursday                  // 4
    Friday                    // 5
    Saturday                  // 6
)

// iota 表达式
type FilePermission int

const (
    Read    FilePermission = 1 << iota // 1 (001)
    Write                              // 2 (010)
    Execute                            // 4 (100)
)

// 跳值
const (
    _  = iota // 跳过 0
    KB = 1 << (10 * iota) // 1 << 10 = 1024
    MB                     // 1 << 20
    GB                     // 1 << 30
    TB                     // 1 << 40
)

// iota 表达式
type Color int

const (
    Red    Color = iota*2 + 1 // 1
    Green                      // 3
    Blue                       // 5
)

func main() {
    fmt.Println(Monday)    // 1
    fmt.Println(Read | Write | Execute) // 7
    fmt.Println(KB, MB, GB, TB)
    fmt.Println(Red, Green, Blue)
}

无类型常量

const x = 42         // 无类型常量
const y float64 = 42 // 有类型常量

func main() {
    // 无类型常量可以在任何数值上下文中使用
    var a int = x
    var b float64 = x
    var c complex128 = x
    fmt.Println(a, b, c) // 42 42 (42+0i)

    // 有类型常量不行
    // var d int = y // 编译错误:cannot use y (type float64) as type int
}

4.7 数字字面量

func main() {
    // 十进制
    dec := 42

    // 二进制(Go 1.13+)
    bin := 0b101010

    // 八进制
    oct := 0o52
    oct2 := 052       // 传统八进制(不推荐)

    // 十六进制
    hex := 0x2A

    // 数字分隔符(Go 1.13+)
    million := 1_000_000
    binary := 0b1010_0101
    hex := 0xFF_FF_FF

    fmt.Println(dec, bin, oct, oct2, hex, million, binary)
    // 42 42 42 42 42 1000000 165
    
    // 浮点数
    f1 := 3.14
    f2 := 314e-2     // 科学计数法
    f3 := 0x1p10     // 十六进制浮点(1024)
    f4 := 1_5.0_5    // 带分隔符

    fmt.Println(f1, f2, f3, f4)
}

4.8 字符与字符串

func main() {
    // byte = uint8,表示 ASCII 字符
    var b byte = 'A'
    fmt.Printf("byte: %c, value: %d\n", b, b) // A, 65

    // rune = int32,表示 Unicode 码点
    var r rune = '中'
    fmt.Printf("rune: %c, value: %d, hex: %U\n", r, r, r) // 中, 20013, U+4E2D

    // 字符串是不可变的 UTF-8 字节序列
    s := "Hello, 世界"
    fmt.Println(len(s))          // 13(字节数,不是字符数)
    fmt.Println(len([]rune(s)))  // 9(字符数)

    // 字符串索引是字节
    fmt.Printf("%c\n", s[0])  // H

    // 遍历字符串
    for i, ch := range s {
        fmt.Printf("[%d] %c (%U)\n", i, ch, ch)
    }

    // 字符串拼接
    s1 := "Hello"
    s2 := "World"
    s3 := s1 + ", " + s2
    fmt.Println(s3)

    // 原始字符串(反引号)
    raw := `这是
一个
多行
字符串
支持\转义不生效: \n \t`
    fmt.Println(raw)
}

🏢 业务场景

  1. 温度转换器:用类型定义+方法,实现类型安全的单位转换
  2. 权限系统:用 iota + 位运算实现权限组合
  3. 配置解析:字符串转数值,处理用户输入
  4. 枚举状态:用 iota 定义订单状态、用户角色

📖 扩展阅读