Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
355 views
in Technique[技术] by (71.8m points)

go - Pointer value set to zero after being changed in a struct method

Here is my code:

type Cake struct {
    weight      int
    value       int
    costBenefit float32
}

func (c *Cake) SetCostBenefit() float32 {
    if c.costBenefit == 0 {
        c.costBenefit = float32(c.value) / float32(c.weight)
    }

    return c.costBenefit
}

func main() {
    capacity := 20
    cakes := []Cake{{weight: 7, value: 160}, {weight: 3, value: 90}, {weight: 2, value: 15}}

    result := maxDuffelBagValue(cakes, capacity)
    fmt.Printf("Max capacity %d", result)
}

func maxDuffelBagValue(cakes []Cake, capacity int) int {
    calcCostBenefit(&cakes)

    for _, c := range cakes {
        fmt.Printf("Value Cake cost benefit 2: %v 
", c.costBenefit)
    }

    return 0
}

func calcCostBenefit(cakes *[]Cake) {
    for _, c := range *cakes {
        c.SetCostBenefit()
        fmt.Printf("Value Cake cost benefit 1: %v 
", c.costBenefit)
    }
}

As you can see above, I have a struct method that sets the CostBenefit property of the struct Cake. As I'm sending the array of cakes to the method calcCostBenefit, any change within the method should reflect the array in the outside (caller method). But actually, it doesn't happen. Here's the output:

Value Cake cost benefit 1: 22.857143
Value Cake cost benefit 1: 30
Value Cake cost benefit 1: 7.5
Value Cake cost benefit 2: 0
Value Cake cost benefit 2: 0
Value Cake cost benefit 2: 0 

The values get reset to zero and I don't know why. I've tried several changes in the code, but nothing worked. What am I missing here? It's driving me crazy nothing being able to spot something that might be so obvious and simple.

question from:https://stackoverflow.com/questions/65837558/pointer-value-set-to-zero-after-being-changed-in-a-struct-method

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

In the provided example, the for range loop copies the value(s) of the underlying slice's array item into a new variable (c) — which is subsequently printed out in your example at:

fmt.Printf("Value Cake cost benefit 1: %v 
", c.costBenefit)

You can obtain a pointer to an entry in the slice by using c := &cakes[i]. Note the difference between slices and arrays, particularly that when slices are copied, the underlying array remains the same:

... As mentioned earlier, re-slicing a slice doesn't make a copy of the underlying array. The full array will be kept in memory until it is no longer referenced. Occasionally this can cause the program to hold all the data in memory when only a small piece of it is needed. ...

https://blog.golang.org/slices-intro#TOC_6.

Thus, there isn’t a need to pass the slice by reference if you're only attempting to change the underlying values in the array, referenced by the slice.

So, this:

func maxDuffelBagValue(cakes []Cake, capacity int) int {

    calcCostBenefit(&cakes)

    for _, c := range cakes {
        fmt.Printf("Value Cake cost benefit 2: %v 
", c.costBenefit)
    }

    return 0
}

func calcCostBenefit(cakes *[]Cake) {
    for _, c := range *cakes {
        c.SetCostBenefit()
        fmt.Printf("Value Cake cost benefit 1: %v 
", c.costBenefit)
    }
}

Becomes this:

func maxDuffelBagValue(cakes []Cake, capacity int) int {
    calcCostBenefit(cakes)

    for _, c := range cakes {
        fmt.Printf("Value Cake cost benefit 2: %f 
", c.costBenefit)
    }

    return 0
}

func calcCostBenefit(cakes []Cake) {
    for i, _ := range cakes {
        c := &cakes[i]
        c.SetCostBenefit()
        fmt.Printf("Value Cake cost benefit 1: %v 
", c.costBenefit)
    }
}

Output:

Value Cake cost benefit 1: 22.857143 
Value Cake cost benefit 1: 30 
Value Cake cost benefit 1: 7.5 
Value Cake cost benefit 2: 22.857143 
Value Cake cost benefit 2: 30.000000 
Value Cake cost benefit 2: 7.500000 

NB

The following also works:

func calcCostBenefit(cakes *[]Cake) {
    for idx, _ := range *cakes {
    c := &(*cakes)[idx]
        c.SetCostBenefit()
        fmt.Printf("Value Cake cost benefit 1: %v 
", c.costBenefit)
    }
}

... but it's more complicated given the alternative above. Nevertheless, this may be required if you're not able to change method signatures for whatever reason (external packages).

https://play.golang.org/p/7OVw_IH2B0l


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...