루프 반복기에서 포인터 의미 체계를 사용하면 동일한 값을 참조하는 이유는 무엇입니까? [복제]

Nov 24 2020

여러 레이어가있는 버그가 발생한 경우를 보여주는 간단한 예 :

    // Store a list of boxed integers in map and print them.
    // This prints 3 times the int value of 3.
type number struct{ val int }

func setInMap() map[int]*number {
    list := []number{{1}, {2}, {3}}
    m := make(map[int]*number)

    for i, n := range list {
        // when saving the address of n,
        // all values point -> 3
        m[i] = &n
    }

    return m
}

func main() {
    m := setInMap()
    for _, n := range m {
        // Prints 3,3,3 or last set value when pointer
        // instead of 1,2,3
        fmt.Print(n.val, ",")
    }
}

버기 포인터 시맨틱 플레이 그라운드 링크

이를 올바르게 작동 하는 값 의미 버전 을 저장하는 것과 대조하십시오 .

    for i, n := range list {
        // set -> map using value semantics
        // prints all values
            m[i] = n
    }

가치 의미론 플레이 그라운드 링크

답변

Mario Nov 24 2020 at 17:29

그 이유는 for..range구문이 값 의미론을 사용하여 컴파일러가 각 목록 값을 스택 , 즉 int의 내용에 복사 하도록하기 때문입니다 n.

// n is copied into the stack at each iteration
for i, n := range list

나중에 n의 주소를 가져올 때 동일한 스택 반복 변수 의 주소를 저장합니다 .

    // when saving the address of n,
    // in a for..range loop we are taking the address of 
    // same iterated variable n in stack.
    // resulting in the same value stored thrice in the map.
        m[i] = &n

포인터 시맨틱 버전을 수정하려면 for i=다음과 같은 올바른 버전을 사용하십시오 .

// n is now pointing to distinct
// members of the list in heap.
for i := 0; i < len(list); i++ {
     n := &list[i]
     m[i] = n
}

부수적으로, 이것은 계층 적 중첩 객체를 보유하는 n 객체에 대한 비효율적이고 공정한 복사를 없애기 때문에 큰 n 객체에 중요한 효율성 을 향상시킵니다 .

플레이 그라운드 (올바른 버전)