常量折叠发生在编译期,go编译器将const表达式在编译阶段直接计算为字面量,如const x = 1 + 2 * 3 → 7;仅当所有操作数均为编译期常量(含len("abc")等)时生效,混入var变量或非常量函数调用即失效。

常量折叠发生在编译期,不是运行时
Go 编译器会在编译阶段直接把 const 表达式算出结果,不生成任何运行时计算逻辑。比如 const x = 1 + 2 * 3,最终生成的二进制里根本不存在加法或乘法指令,只有数字 7 的字面量。
这意味着:只要所有操作数都是编译期已知常量(包括内置函数如 len("abc")、unsafe.Sizeof(int64(0))),整个表达式就会被折叠。
- 支持的运算符限于基本算术、位运算、比较(但比较结果只能用于条件编译,如
//go:build) -
math.Pi这类包级变量不算常量,不能参与折叠;必须用const Pi = 3.141592653589793才行 - 字符串拼接也支持折叠:
const s = "hello" + " " + "world"→"hello world"
哪些地方会触发常量折叠失效
一旦表达式里混入非常量,整条表达式就“掉出”折叠范围,变成运行时求值——哪怕看起来很静态。
- 使用
var定义的变量,哪怕值没变过,也不算常量:var n = 5; const x = n * 2❌ 编译失败 - 函数调用(包括
len作用于变量):const s = "a"; const l = len(s)✅,但var s = "a"; const l = len(s)❌ - 类型转换中若源值不是常量,也不行:
const i = int64(1 ✅,但 <code>const j = int64(1 ❌(溢出,且右操作数虽是字面量,但左移超限导致未定义)
错误信息通常是:constant <code>1 overflows int 或 invalid operation,而不是“不能折叠”这类提示。
立即学习“go语言免费学习笔记(深入)”;
和 go build -gcflags="-m" 一起看优化效果
想确认某段常量是否真被折叠了,最直接的方式是让编译器“说话”。加 -m 参数能打印内联、常量传播等决策:
go build -gcflags="-m -m" main.go
输出中如果看到类似 const <code>x = 42 (constant folded) 或 inlining call to <code>foo,说明折叠成功;如果只显示 move of <code>y to stack,那大概率没折叠。
- 叠加
-m -m -m可看到更底层细节,比如 SSA 阶段是否把常量提成 immediate - 注意:
-m输出依赖函数是否可导出、是否跨包调用,私有函数更容易被内联+折叠 - 交叉编译(如
GOOS=js go build)可能关闭部分优化,折叠行为不一定一致
别指望靠常量折叠绕过类型系统限制
常量折叠不会改变 Go 的类型检查时机和规则。它只是“算得早”,不是“查得松”。
-
const b = true; var _ int = b依然报错:cannot convert <code>b(untyped bool constant) to int - 无类型常量(如
123、"hello")在赋值时才确定类型,折叠不影响这个过程 - 接口赋值、方法调用、channel 操作等涉及运行时语义的地方,常量折叠完全不介入
真正容易被忽略的是:常量折叠后,值虽然确定了,但它的“类型上下文”仍由原始声明决定。比如 const x uint8 = 255; const y = x + 1,y 不是 uint8(0),而是溢出错误——因为 x + 1 是无类型整数运算,上限是平台 int 范围,256 没问题;但如果你写 const y uint8 = x + 1,才触发溢出检查。











