Go 语言完全指南 / 04 - 变量与类型:基本类型、零值、类型推导、常量
04 - 变量与类型
4.1 基本类型
Go 是静态类型语言,所有变量在编译期都有确定的类型。
整数类型
| 类型 | 大小 | 范围 | 说明 |
|---|---|---|---|
int8 | 1 字节 | -128 ~ 127 | 有符号 |
int16 | 2 字节 | -32768 ~ 32767 | 有符号 |
int32 | 4 字节 | -2^31 ~ 2^31-1 | 有符号,等同 rune |
int64 | 8 字节 | -2^63 ~ 2^63-1 | 有符号 |
uint8 | 1 字节 | 0 ~ 255 | 无符号,等同 byte |
uint16 | 2 字节 | 0 ~ 65535 | 无符号 |
uint32 | 4 字节 | 0 ~ 2^32-1 | 无符号 |
uint64 | 8 字节 | 0 ~ 2^64-1 | 无符号 |
int | 平台相关 | 平台相关 | 32 或 64 位 |
uint | 平台相关 | 平台相关 | 32 或 64 位 |
uintptr | 平台相关 | — | 指针运算用 |
浮点与复数
| 类型 | 大小 | 说明 |
|---|---|---|
float32 | 4 字节 | IEEE 754 单精度 |
float64 | 8 字节 | IEEE 754 双精度(推荐) |
complex64 | 8 字节 | float32 实部 + float32 虚部 |
complex128 | 16 字节 | float64 实部 + float64 虚部 |
其他基本类型
| 类型 | 说明 | 示例 |
|---|---|---|
bool | 布尔值 | true, false |
string | UTF-8 字符串 | "你好" |
byte | uint8 的别名 | 'A' |
rune | int32 的别名,表示 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, float64 | 0, 0.0 | 数值零 |
bool | false | 假 |
string | "" | 空字符串 |
pointer | nil | 空指针 |
slice | nil | 空切片(可用) |
map | nil | 空 map(不可写,需 make) |
channel | nil | 空通道 |
func | nil | 空函数 |
interface | nil | 空接口 |
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)
}
🏢 业务场景
- 温度转换器:用类型定义+方法,实现类型安全的单位转换
- 权限系统:用 iota + 位运算实现权限组合
- 配置解析:字符串转数值,处理用户输入
- 枚举状态:用 iota 定义订单状态、用户角色