Go常量是编译期确定的类型系统对象,非宏替换;其值由constant.Value封装、不占运行时内存,仅在AST和go/types中以*types.Const形式存在,不可取地址或反射获取。

Go 的常量不是“编译期替换的宏”,而是类型系统中真实存在的、带元数据的对象——它在 go/types 中有明确的运行时表示(types.Const),且其值由 constant.Value 封装,支持任意精度计算。这意味着你不能靠字符串替换或预处理器思维去理解它。
const 声明后,值到底存在哪?
常量值不占运行时内存,也不生成符号地址。它的“存在”体现在 AST 和类型检查阶段:go/parser 解析出 ast.GenDecl 节点,go/types 在类型检查时为每个常量名创建一个 *types.Const 实例,其中 Val 字段是 constant.Value 类型(底层是 big.Int / big.Float / big.Rat 等高精度结构)。你无法在运行时用反射拿到它,也不能取它的地址 —— &Pi 会直接报错 “cannot take address of Pi”。
- 常量值只在编译器内部流转,最终被内联进指令或作为立即数写入目标代码
-
types.Const.Val是唯一能安全读取其原始精度值的途径,比如constant.Int64Val(c.Val)可能 panic,而constant.Val(c.Val)返回 interface{} 更稳妥 - 无类型常量(如
const x = 42)的Val保留完整精度;一旦参与运算或赋值给有类型变量,才触发隐式转换
为什么 1
这是常量表达式求值与类型边界检查的分水岭。前者是纯编译期整数运算,不受目标类型限制;后者涉及已定义类型的溢出规则。
1 是常量位移表达式,所有操作数都是无类型整数常量,编译器用无限精度计算,结果仍是合法常量值(即使超 int64)-
math.MaxInt64 + 1中,math.MaxInt64是int64类型的导出常量,加法操作要求两边同类型,因此1被视为int64,导致整数溢出 —— Go 显式禁止有类型常量的溢出运算 - 验证方式:把
math.MaxInt64 + 1放进 const 块,编译直接报constant 9223372036854775808 overflows int64
types.Const.Val 怎么安全取值?别掉进 constant.Int64Val 的坑
constant.Int64Val 这类函数假设值可无损转成对应类型,但无类型常量可能超出范围或非整数 —— 它不校验,只强制截断或 panic。真正安全的做法是先判断类别再提取。
立即学习“go语言免费学习笔记(深入)”;
- 用
constant.Kind(c.Val)判断是constant.Int、constant.Float还是constant.String - 对整数,优先用
constant.Int64Val仅当constant.ToInt(c.Val)不为 nil 且constant.BitLen≤ 63 - 更通用:调用
constant.Val(c.Val)得到interface{},再用 type switch 处理:int64、*big.Int、string等 - 示例:
if v := constant.Val(c.Val); v != nil { switch x := v.(type) { case int64: ... case *big.Int: ... } }
最易被忽略的是:常量的“类型安全”不来自运行时检查,而来自编译期对上下文类型的严格绑定。一旦你把它传给一个接受 int32 的函数,Go 就已在那一刻决定了精度截断或编译失败 —— 这个决策点不在声明处,而在每次使用处。


















