目 录CONTENT

文章目录
go

【Go】常量

hxuanyu
2025-12-22 / 0 评论 / 0 点赞 / 88 阅读 / 0 字

常量基础

常量在编译期完成初始化:初始化表达式必须能在编译期间求值,并且已初始化的常量可以作为其他常量初始化表达式的一部分。程序整个生命周期中,常量的值保持不变。

常量使用 const​ 关键字声明。const 支持单行声明多个常量,也支持使用代码块聚合声明。声明常量时可以省略类型,由编译器根据初始值推断(更准确地说,很多情况下会得到“无类型常量”,见后文)。

const PI float64 = 3.14159265358979323846 // 单行常量声明
// 使用代码块形式声明常量
const (
    size int64 = 4096       // 显式指定类型
    i, j, s      = 13, 14, "bar" // 单行声明多个常量,并省略类型
)

常量的取值范围仅限于基本类型及其可表示的值(包括有类型常量与无类型常量):

  • 数值常量(整数、浮点数、复数)
  • 字符串常量
  • 布尔常量
  • rune 常量(本质上是整数常量)

常量的命名建议采用驼峰命名法:导出的常量首字母大写,未导出的常量首字母小写。应避免使用其他编程语言常见的命名约定来命名 Go 常量,以下是反例:

const MAX_PACKET_SIZE = 512
const kMaxBufferSize = 1024
const KMaxUsersPerGroup = 500

对于操作系统级别、跨编程语言具有共识或既定含义的常量,允许保留其原始名称。这些名字通常源于操作系统 API 手册或相关语言/平台的标准库文档。

Go 常量的特性

有类型常量与类型一致性

在 Go 中,即便两个类型拥有相同的底层表示,只要是不同的定义类型(defined type),它们之间也不能直接混合运算;这一规则对常量同样适用:

package main

import "fmt"

type myInt int

const n myInt = 13

func main() {
    var a int = 5
    fmt.Println(a + n) // 编译错误:invalid operation: a + n (mismatched types int and myInt)
}

有类型常量与变量混合运算时,需要显式类型转换:

package main

import "fmt"

type myInt int

const n myInt = 13
const m int = int(n) + 5 // 正确:显式转换

func main() {
    var a int = 5
    fmt.Println(a + int(n)) // 输出:18
    _ = m
}

无类型常量(untyped constant)

为了让常量更方便地参与运算,Go 引入了无类型常量。无类型常量在声明时没有固定类型,只有“种类”(如无类型整数、无类型浮点数、无类型字符串等)。它们在需要具体类型的上下文中,会被转换为目标类型(前提是该值能被目标类型表示)。

例如:

package main

import "fmt"

func main() {
    const n = 13 // 无类型整数常量
    var a int = 5
    fmt.Println(a + n) // n 在此处被转换为 int
}

常量的隐式转换与溢出检查

编译器会根据上下文类型信息,将无类型常量转换为相应类型后参与计算。由于对象是常量,编译器会在编译期进行精确的可表示性检查,确保转换安全;如果目标类型无法表示该常量值,就会报错:

package main

func main() {
    const m = 1333333333
    var k int8 = 1
    _ = k + m // 编译错误:constant 1333333333 overflows int8
}

因此,在很多表达式中不需要显式类型转换,使用无类型常量是一种常见且推荐的写法。

枚举:const + iota

Go 原生没有专门的枚举类型,通常用 const​ 声明块配合 iota​ 来实现枚举。与一些语言不同,Go 不会为未赋值的常量自动递增赋值;但它支持“隐式重复前一个非空初始化表达式”和 iota

隐式重复前一个表达式示例:

const (
    Apple, Banana = 11, 22
    Strawberry, Grape
    Pear, Watermelon
)

等价于:

const (
    Apple, Banana       = 11, 22
    Strawberry, Grape   = 11, 22
    Pear, Watermelon    = 11, 22
)

iota​ 是预定义标识符,表示在同一个 const​ 声明块中每一行(更准确地说:每个常量规范项所在行)的序号,从 0 开始递增。iota 本身也是无类型常量,可参与不同类型的求值过程。

例如 Go 标准库中 sync/mutex.go 的一段常量定义:

// $GOROOT/src/sync/mutex.go
const (
    mutexLocked = 1 << iota // iota = 0, 1 << 0 = 1
    mutexWoken              // iota = 1, 1 << 1 = 2
    mutexStarving           // iota = 2, 1 << 2 = 4
    mutexWaiterShift = iota // iota = 3
    starvationThresholdNs = 1e6 // iota = 4
)

如果希望枚举从 iota = 1 开始,可以使用空白标识符跳过第一个值:

// $GOROOT/src/syscall/net_js.go
const (
    _ = iota
    IPV6_V6ONLY // 1
    SOMAXCONN   // 2
    SO_ERROR    // 3
)

当需要略过某些值时,也可以利用空白标识符:

const (
    _ = iota // 0
    Pin1     // 1
    Pin2     // 2
    Pin3     // 3
    _        // 4
    Pin5     // 5
)

每个 iota​ 的生命周期始于一个 const 声明块的开始,并在其结束时终止。

对于不规则序列的常量值,手动指定往往更合适。此外,iota 在一定程度上会影响代码的直观性与可读性,应当在团队约定下谨慎使用。

0
博主关闭了所有页面的评论