目 录CONTENT

文章目录
go

【Go】常量

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

常量基础

常量的初始化:在编译期间完成初始化,初始化表达式必须能够在编译期间求出结果,同时已经被初始化的常量可以作为其它常量初始化表达式的一部分。在程序的整个生命周期中,常量的值保持不变,

常量的声明方式与变量不同,使用const​关键字声明常量,const支持单行声明多个常量,以及代码块形式聚合声明。声明常量时也可以省略类型,由编译器自动判断类型。

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

常量的类型仅限于基本数据类型:

  • 数值类型
  • 字符串类型
  • 布尔类型

常量的名称应采用驼峰命名法,导出的常量首字母大写,未导出的常量首字母小写,应避免使用其它变成语言的命名约定来命名Go常量,以下是反例:

const MAX_PACKET_SIZE = 512
const kMaxBufferSize = 1024
const KMaxUsersPergroup = 500

对于操作系统级别、具有夸编程语言共识或标注的常量,允许保留最初的名字,这些名字通常源于各种操作系统API手册及变成语言的标准库参考手册。

Go常量的创新

无类型常量

在Go中,即便两个类型拥有相同的存储结构,但在声明时使用了不同的类型,这两个类型也不能同时进行运算,这一规则在常量中同样适用:

type myInt int
const n myInt = 13
const m int = n + 5 // 编译器错误:cannot use n + 5 (type myInt) as type int in const initializer
func main() {
    var a int = 5
    fmt.Println(a + n) // 编译器错误:invalid operation: a + n (mismatched types int and myInt)
}

在有类型常量与变量混合运算时,只能通过显式类型转换:

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
}

为了让常量可以方便地参与运算,Go中使用无类型常量解决这一问题。和变量一样,无类型常量在初始化时,根据初始值决定一个默认类型,并在参与运算时,自动进行隐式转型,转换为对应的类型后参与运算。

隐式转型

编译器会根据上下文中的类型信息,自动将无类型常量转换为相应的类型后参与计算,由于转型的对象是常量,因此不会引发类型安全问题,编译器可以确保转型安全。

如果编译器在尝试进行隐式转型时发现无法将常亮转换为目标类型,也会产生报错:

const m = 1333333333
var k int8 = 1
j := k + m // 编译器错误:constant 1333333333 overflows int8

有了无类型常量和隐式转型,我们在很多情况下,不需要在表达式中进行显式类型转换,使用无类型常量是一种惯用法。

枚举

Go原生没有提供枚举类型,我们可以使用const代码块定义的常量集合实现枚举。和其他语言中的枚举不同,如果常量没有显式复制,Go不会自动为其指定值,但引入了“隐式重复前一个非空表达式”和iota关键字。

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

以上代码定义的常量中,后两行没有显式赋予初始值,Go编译器会自动使用上一行的初始化表达式,等价于:

const (
    Apple, Banana = 11, 22
    Strawberry, Grape  = 11, 22 // 使用上一行的初始化表达式
    Pear, Watermelon  = 11, 22  // 使用上一行的初始化表达式
)

由于常量的值往往都是唯一的,单纯的重复可能并不能实现我们的需求,Go在此基础上引入了iota关键字。

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, 显式指定初始值为iota
    starvationThresholdNs = 1e6 // iota = 4, 显式指定初始值,使用指定的值
)

如果希望枚举常量从iota = 1​开始,可以参考Go标准库中的做法,使用空白标识符忽略iota的第一个值:

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

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

const (
    _ = iota // 0
    Pin1
    Pin2
    Pin3
    _
    Pin5    // 5   
)

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

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

0

评论区