
Go 的 big.Int 类型在切片中直接 append 会导致所有元素指向同一底层数据,因其实现基于指针式 slice(nat),造成浅拷贝问题;正确做法是每次循环用 Set() 创建独立副本。
go 的 big.int 类型在切片中直接 append 会导致所有元素指向同一底层数据,因其实现基于指针式 slice(nat),造成浅拷贝问题;正确做法是每次循环用 `set()` 创建独立副本。
在 Go 中,big.Int 并非纯值类型——其内部通过 nat(即 []Word)存储大整数的二进制位,而 nat 是一个 slice。根据 Go 的语义,slice 是引用类型,包含指向底层数组的指针、长度和容量。因此,当执行 primes = append(primes, i) 时,虽看似复制了 big.Int 结构体,但其中的 nat 字段仍共享同一底层数组。后续对 i 的修改(如 i.Add(&i, &one))会持续覆写该共享内存,导致已追加到切片中的所有 big.Int 值“意外同步”为最新状态。
这正是原代码输出 1,2,3,4(循环内打印)却最终得到 5,5,5,5(切片遍历时)的根本原因:四个 big.Int 实例的 nat 字段都指向同一片内存,最后一次迭代后 i 变为 5,整个切片里的值也随之“变成”5。
✅ 正确解决方案:使用 Set() 显式创建独立副本
Set() 方法会分配新的 nat 底层数组并复制数值,实现逻辑上的深拷贝(注意:big.Int 无标准 Clone(),Set() 是官方推荐的复制方式):
func primesLessThan(n *big.Int) (primes []big.Int) {
var one big.Int
one.SetInt64(1)
var i big.Int
i.SetInt64(1)
for i.Cmp(n) < 0 {
// ✅ 关键:为每个值创建独立副本
result := new(big.Int).Set(&i)
fmt.Println(result.String())
primes = append(primes, *result) // 或直接 result.Set(&i) 后 append(*result)
i.Add(&i, &one)
}
return
}⚠️ 注意事项:
- 不要依赖赋值 a = b 或 append(slice, b) 复制 big.Int,这是浅拷贝;
- Set() 是线程安全的,且高效(仅复制必要字长);
- 若需频繁复制,可复用 big.Int 实例(如 var tmp big.Int + tmp.Set(&src)),避免高频内存分配;
- 补充说明:你提到的 one = two 问题同理——big.Int 变量本身是结构体,但 = 赋值仅复制字段(含 nat 指针),并非深拷贝;应改用 one.Set(two)。
总结:理解 big.Int 的底层内存模型是规避此类陷阱的关键。在涉及循环追加、缓存或共享场景时,始终显式调用 Set() 构建独立实例,而非依赖结构体字面量复制。


















