转到:附加big.Int循环以切片意外结果

The following Go program produces 1,2,3,4 in followed by 5,5,5,5. I was expecting 1,2,3,4 in both cases. What am I doing wrong?

package main

import (
    "fmt"
    "math/big"
)

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 {
        fmt.Println(i.String())
        primes = append(primes, i)
        i.Add(&i, &one)
    }
    return
}

func main() {
    primes := primesLessThan(big.NewInt(5))
    for _, p := range primes {
        fmt.Println(p.String())
    }
}

Update: the following code snippet illustrates the unexpected side effects of the shallow copy described in the responses. The output of the following snippet is 3, 3

one := big.NewInt(1)
two := big.NewInt(2)

one = two // Shallow copy. Question: how do I do a deep copy?

one.SetInt64(3) // Side-effect: also changes two

fmt.Println(one.String())
fmt.Println(two.String())

It's because of the way the object stores its internal data.

Take this example:

package main

import "fmt"

type foo struct {
    value int
}

func bar() (r []foo) {
    var f foo
    for i := 0; i < 5; i++ {
        f.value = i
        r = append(r, f)
    }
    return
}

func main() {
    for _, v := range bar() {
        fmt.Println(v)
    }
}

The output will be the expected

{0}
{1}
{2}
{3}
{4}

The issue in your example is that big.Int stores its value in a slice, and slices are pointers. So when a copy of a big.Int is created, the new copy contains a new pointer to the same slice in memory. A shallow copies is created rather than a deep copy.

See https://golang.org/src/math/big/int.go?s=388:468#L8 for how bit.Int is declared, then see https://golang.org/src/math/big/nat.go#L25 for how nat is declared.

Here is a solution that uses big.Int

package main

import (
    "fmt"
    "math/big"
)

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 {
        var result big.Int
        result.Set(&i)
        fmt.Println(result.String())
        primes = append(primes, result)
        i.Add(&i, &one)
    }
    return
}

func main() {
    primes := primesLessThan(big.NewInt(5))
    for _, p := range primes {
        fmt.Println(p.String())
    }
}

What we see... Your primes []big.Int is acting like an array of pointers. By storing i in each slot of primes you are essentially storing the exact same pointer in each spot. That's why it's printing 5 four times.

Why we see this... Due to the internals of big.Int, each strict object of big.Int has an internal slice. Slices are implemented as pointers because they are really just fancy arrays. So, while you may have a big.Int object, the object has a pointer to a slice.

When copying a big.Int object you are copying this internal slice pointer.

@GarMan is right and I also agree it has to do with shallow vs. deep copies.

The @GarMan solution is fine but you can do simpler doing the string conversion at the append() level for primes []string:

primes = append(primes, i.String())