2026-06-07 11:08:10 +08:00
|
|
|
|
---
|
2026-06-07 12:14:39 +08:00
|
|
|
|
tags: [go, golang, go基础语法, 常量]
|
|
|
|
|
|
create time: 2026-06-07 15:00
|
2026-06-07 11:08:10 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
# Go 语言常量
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 概述
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
常量是编译期确定的不可变值。本文介绍常量的声明方式、`iota` 枚举技巧,以及常量与变量的关键区别。
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 正文
|
|
|
|
|
|
|
|
|
|
|
|
### 常量 vs 变量:本质区别是什么?
|
|
|
|
|
|
|
|
|
|
|
|
**常量在编译期就确定了值,之后不可修改。** 这意味着编译器可以做更多优化,比如内联替换、死代码消除等。
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
const MaxSize = 1024 // 编译期确定,不可更改
|
|
|
|
|
|
// MaxSize = 2048 // ❌ 编译错误:cannot assign to MaxSize
|
|
|
|
|
|
|
|
|
|
|
|
var BufferSize = 1024 // 运行时可变
|
|
|
|
|
|
BufferSize = 2048 // ✅ 合法
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```
|
2026-06-07 12:14:39 +08:00
|
|
|
|
|
|
|
|
|
|
> [!note] 📝 常量类型限制
|
|
|
|
|
|
> 只能是布尔型、数字型(整数/浮点/复数)、字符串。不能是 slice、map、struct 等复杂类型。
|
|
|
|
|
|
|
|
|
|
|
|
### 声明方式
|
|
|
|
|
|
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
const name type = value // 显式类型
|
|
|
|
|
|
const name = value // 类型推断(更常用)
|
|
|
|
|
|
|
|
|
|
|
|
// 多常量一行声明
|
|
|
|
|
|
const a, b = "hello", "world"
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
### `iota`:Go 的枚举神器
|
|
|
|
|
|
|
|
|
|
|
|
`iota` 是 Go 最优雅的枚举机制之一——它是 `const` 块内的**行索引**,从 0 开始,每行自动 +1。
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
const (
|
2026-06-07 12:14:39 +08:00
|
|
|
|
Sunday = iota // 0
|
|
|
|
|
|
Monday // 1
|
|
|
|
|
|
Tuesday // 2
|
|
|
|
|
|
Wednesday // 3
|
|
|
|
|
|
Thursday // 4
|
|
|
|
|
|
Friday // 5
|
|
|
|
|
|
Saturday // 6
|
2026-06-07 11:08:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!tip] 💡 省略写法
|
|
|
|
|
|
> 同一 `const` 块内,如果后续行的值和上一行一样(都是 `iota`),可以省略右侧表达式:
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
```go
|
2026-06-07 11:08:10 +08:00
|
|
|
|
const (
|
2026-06-07 12:14:39 +08:00
|
|
|
|
Sunday = iota // 0
|
|
|
|
|
|
Monday // 1 ← 自动继承 iota
|
|
|
|
|
|
Tuesday // 2
|
2026-06-07 11:08:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
#### `iota` 的高级用法
|
|
|
|
|
|
|
|
|
|
|
|
当某行出现了**非 iota 的独立值**后,`iota` 继续计数,但其他常量需要重新指定表达式:
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
const (
|
2026-06-07 12:14:39 +08:00
|
|
|
|
a = iota // 0
|
|
|
|
|
|
b // 1
|
|
|
|
|
|
c // 2
|
|
|
|
|
|
d = "ha" // "ha" — 独立值,iota 变成 3
|
|
|
|
|
|
e // "ha" — 继承上一行的值,iota 变成 4
|
|
|
|
|
|
f = 100 // 100 — 独立值,iota 变成 5
|
|
|
|
|
|
g // 100 — 继承,iota 变成 6
|
|
|
|
|
|
h = iota // 6 — 恢复计数
|
|
|
|
|
|
i // 7
|
2026-06-07 11:08:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
```
|
2026-06-07 12:14:39 +08:00
|
|
|
|
|
|
|
|
|
|
#### 位运算枚举
|
|
|
|
|
|
|
|
|
|
|
|
结合位移操作,`iota` 可以优雅地生成幂次值:
|
|
|
|
|
|
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```go
|
|
|
|
|
|
const (
|
2026-06-07 12:14:39 +08:00
|
|
|
|
_ = iota // 0,用 _ 丢弃
|
|
|
|
|
|
KB = 1 << (10 * iota) // 1 << 0 = 1(通常不用)
|
|
|
|
|
|
MB // 1 << 10 = 1024
|
|
|
|
|
|
GB // 1 << 20 = 1048576
|
|
|
|
|
|
TB // 1 << 30 = 1073741824
|
2026-06-07 11:08:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
```
|
2026-06-07 12:14:39 +08:00
|
|
|
|
|
|
|
|
|
|
> [!question] ❓ 思考
|
|
|
|
|
|
> 上面这段代码中,`KB`、`MB`、`GB` 的值分别是多少?为什么 `_` 那行很重要?
|
|
|
|
|
|
|
|
|
|
|
|
### 常量可以用在编译期函数中
|
|
|
|
|
|
|
|
|
|
|
|
`len()`、`cap()`、`unsafe.Sizeof()` 等在常量上下文中是**编译期求值**的:
|
|
|
|
|
|
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
const s = "abc"
|
|
|
|
|
|
const n = len(s) // n == 3,编译期确定
|
|
|
|
|
|
const sz = unsafe.Sizeof(s) // sz == 16(string struct 大小)
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```
|
2026-06-07 12:14:39 +08:00
|
|
|
|
|
|
|
|
|
|
> [!info] ℹ️ 为什么 `sizeof("abc")` 是 16?
|
|
|
|
|
|
> Go 的 `string` 底层是一个结构体:`{ptr *byte, len int}`,两个字段各占 8 字节(64 位平台),所以 `unsafe.Sizeof(string)` 恒为 16。它不包含字符串的实际内容,只包含指向内容的指针和长度。
|
|
|
|
|
|
|
|
|
|
|
|
### 常量分组示例(枚举最佳实践)
|
|
|
|
|
|
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
type StatusCode int
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
const (
|
2026-06-07 12:14:39 +08:00
|
|
|
|
OK StatusCode = 200
|
|
|
|
|
|
NotFound StatusCode = 404
|
|
|
|
|
|
ServerError StatusCode = 500
|
2026-06-07 11:08:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
```
|
2026-06-07 12:14:39 +08:00
|
|
|
|
|
|
|
|
|
|
给枚举值绑定自定义类型(如 `StatusCode`)可以提供类型安全——不同枚举类型之间不能直接比较。
|
|
|
|
|
|
|
|
|
|
|
|
> [!warning] ⚠️ 常见陷阱
|
|
|
|
|
|
> 不带类型的裸 `const` 会隐式转换为 `int`。如果需要类型安全,务必显式声明类型或先定义枚举类型。
|