Skip to content

03 常量的使用与应用规范

常量

  • 常量(constant)用于表示在程序执行期间其值保持不变的量。
  • 常量使用 const 关键字进行声明,并且只能存储基本类型(如布尔、数字、字符串等)的值。

语法

go
const identifier [type] = value
  • identifier 是常量的名称。
  • type 是可选的常量类型(通常会被自动推断)。
  • value 是常量的值,一旦设置就不能更改。
go
package main

import "fmt"

const PI = 3.14           // 定义浮点型常量
const Greeting string = "Hello, Go!" // 定义字符串常量

func main() {
    fmt.Println("PI:", PI)
    fmt.Println(Greeting)
}

常量与变量的区别

常量 (const)变量 (var)
值在编译时确定值可以在运行时改变
只能使用基本类型(字符串、布尔、数字类型)可以是任何类型,包括结构体、数组、切片等
不能被修改可以在程序执行过程中动态修改
只能使用 const 关键字声明可以使用 var 或简短声明 :=
不能调用函数赋值可以调用函数返回值赋值

多个常量声明

常量可以分组声明,用括号将多条 const 语句包围在一起:

go
const (
    A = 1
    B = 2
    C = 3
)

// 指定类型或初始化不同类型的常量
const (
    X int = 10
    Y float64 = 20.5
    Z = "Go Language"
)

使用 iota 枚举

Go 语言提供了一个名为 iota 的特殊常量,它可以用于简化一系列相关常量的声明,并自动生成递增的值。iota 初始值为 0,每声明一个常量,iota 的值会自动递增。

常见的使用场景:定义枚举值、位移常量等。

go
package main

import "fmt"

// 使用 iota 生成一组枚举常量
const (
    Apple  = iota // Apple  的值是 0
    Banana        // Banana 的值是 1
    Cherry        // Cherry 的值是 2
)

func main() {
    // iota 的值从 0 开始,并在每行自动递增,所以 Apple, Banana, Cherry 分别是 0, 1, 2
    fmt.Println(Apple, Banana, Cherry)  // 输出:0 1 2
}

更复杂的 iota 用法

iota 可以用于生成复杂的常量序列,例如按位移的值(位掩码):KBMBGB 分别表示 1 KB、1 MB 和 1 GB 的字节数,通过 iota 和位运算轻松实现了字节单位的转换。

go
const (
    _  = iota             // 跳过 iota = 0
    KB = 1 << (10 * iota) // 1 左移 10 位,等于 1024
    MB = 1 << (10 * iota) // 1 左移 20 位
    GB = 1 << (10 * iota) // 1 左移 30 位
)

func main() {
    fmt.Println("KB:", KB) // 1024
    fmt.Println("MB:", MB) // 1048576
    fmt.Println("GB:", GB) // 1073741824
}

使用常量定义错误代码、配置值

常量通常用于定义程序中的固定值,例如:

  • 错误代码
  • 配置参数
  • 枚举类型
  • 状态标识
go
package main

import "fmt"

// 定义错误代码常量
const (
    ErrNotFound     = 404
    ErrUnauthorized = 401
    ErrServerError  = 500
)

func main() {
    fmt.Println("Not Found Error Code:", ErrNotFound)
    fmt.Println("Unauthorized Error Code:", ErrUnauthorized)
    fmt.Println("Server Error Code:", ErrServerError)
}

有类型常量

  • 使用 const 声明常量,并且指定常量的类型。
  • 缺点:不简洁。
go
package main

import "fmt"

type MyInt int

const X MyInt = 10

// const x int = 11 + X // 需要显式转换,否则编译错误
const x int = 11 + int(X)

// const y int = X // 需要显式转换,否则编译错误
const y int = int(X)
const z MyInt = X // 或者将两个生命为同一个类型

func main() {
	const PI float64 = 3.14
	fmt.Println("PI:", PI)
	fmt.Println("x:", x)
	fmt.Println("y:", y)
	fmt.Println("z:", z)
	var radius float64 = 5.0
	var perimeter = 2 * PI * radius // 有类型常量 PI 是 float64 类型
	fmt.Printf("Perimeter: %f\n", perimeter)

	var diameter int = 10
	// 以下语句会报错,因为 PI 是 float64 类型,不能直接用于 int 类型的计算
	// var circumference = 2 * PI * diameter
	fmt.Println("diameter:", diameter)
}
bash
 go run typed_constant.go
PI: 3.14
x: 21
y: 10
z: 10
Perimeter: 31.400000
diameter: 10

无类型常量

  • 使用 const 声明常量,不指定常量的类型。
  • 优点:灵活,可以自动推断类型。
go
package main

import "fmt"

const PI = 3.14

// 使用 iota 定义一组无类型整型常量
// 由于 iota 生成的常量没有明确类型,可以根据上下文推断为 int 或其他数值类型,这样可以在多种表达式中灵活使用
const (
	Apple = iota
	Banana
	Cherry
)

func main() {
	var radius float64 = 5.0
	var perimeter = 2 * PI * radius // 无类型常量 PI 被推断为 float64
	fmt.Printf("Perimeter: %f\n", perimeter)

	var diameter int = 10
	var circumference = 2 * PI * float64(diameter) // PI 被推断为 float64 后转换
	fmt.Printf("Circumference: %f\n", circumference)

	fmt.Printf("Apple: %d\t Banana: %d\t Cherry: %d\n", Apple, Banana, Cherry)
}
bash
 go run untyped_constant.go
Perimeter: 31.400000
Circumference: 62.800000
Apple: 0         Banana: 1       Cherry: 2

无类型常量与有类型常量的区别

无类型常量(Untyped Constants)有类型常量(Typed Constants)
在声明时没有指定类型在声明时明确指定了类型
在使用时根据上下文自动推断类型类型固定,不会因为使用场景而改变
可以在不同的上下文中用作不同类型(如 intfloat64只能用于声明类型一致的表达式中
常用于需要灵活转换类型的场景常用于防止类型混淆和确保类型一致性
例如:const Pi = 3.14例如:const Pi float64 = 3.14
go
package main

import "fmt"

// 无类型常量
const (
    A = 10     // 无类型整型常量
    B = 3.14   // 无类型浮点型常量
)

// 有类型常量
const (
    C int     = 10     // 有类型整型常量
    D float64 = 3.14   // 有类型浮点型常量
)

func main() {
    // 无类型常量可以与不同类型的变量混合运算
    var x int = A     // A 自动推断为 int 类型
    var y float64 = A // A 可以被自动推断为 float64 类型

    fmt.Println("x:", x)  // 输出:x: 10
    fmt.Println("y:", y)  // 输出:y: 10.000000

    // 有类型常量只能用于相同类型的变量
    var z int = C     // C 是 int 类型,可以赋值给 int 类型变量
    // var w float64 = C // 这里会报错,因为 C 是 int 类型,不能自动转换为 float64 类型

    var radius float64 = 5.0
    fmt.Println("Circumference:", 2*B*radius)  // B 被推断为 float64 类型
}