Go 루프에서 포인터 사용 시 중복값 나오는 이슈

박재훈·2022년 11월 5일
1

GO

목록 보기
5/19

Go로 포인터를 배열/슬라이스/맵 등등의 자료형을 통해 다루다보면 이 문제에 마주한 적이 있을 것이다.

문제가 발생하는 상황은 다음과 같다.

문제 재현

먼저, 재현에 필요한 구조체를 하나 만든다.

type Sample struct {
	Number int
}

단순히 Number라는 int형 변수만을 필드로 가지고 있는 간단한 구조체이다. 예시용으로 만든 것으로 이 자리에 무엇이 오든 딱히 상관 없다.

samples := []Sample{{1}, {2}, {3}}
pointers := []*Sample{}

Sample 타입의 슬라이스를 만든 뒤 3개의 원소를 넣어주었다.
또한 pointers라고 하는 *Sample 타입의 슬라이스를 만들었으며, 이제 여기에 값을 넣어줄 것이다.

for _, sample := range samples {
	fmt.Printf("Number: %d, Address: %p\n", sample.Number, &sample)
	pointers = append(pointers, &sample)
}

fmt.Println()

for _, pointer := range pointers {
	fmt.Printf("Number: %d, Address: %p\n", pointer.Number, pointer)
}

값을 넣어준 뒤 제대로 들어갔는지 체크해보았다. 딱히 문제가 없어보이는데, 출력해보면

Number: 1, Address: 0xc0000ac008
Number: 2, Address: 0xc0000ac008
Number: 3, Address: 0xc0000ac008

Number: 3, Address: 0xc0000ac008
Number: 3, Address: 0xc0000ac008
Number: 3, Address: 0xc0000ac008

??? poiners에 똑같은 값만 들어가있고 주소는 아예 다 똑같다.

사실 알고보면 간단한 문제인데... 루프가 돌면서 sample이라고 하는 변수에 samples의 원소를 하나씩 할당해주기 때문이다.

for _, sample := range samples {

이 루프의 첫줄이 문제였던 것이다!! 매 루프마다 새로운 sample이 생겨나는게 아니라 루프가 시작되면서 하나의 스코프가 지정되고, 그 안에서 만들어진 sample 변수에 samples가 할당된다.
참고로 언더스코어 해제하고 인덱스를 이용하려고 해도 동일한 문제가 발생한다.

그래서 당연히 pointers는 같은 변수들로 가득 차게 되고, 그래서 자연히 맨 마지막 값만이 들어가는 것이다.

해결 방법

그렇다면 어떻게 해결하는가? Go를 쓰는 한 평생 포인터 슬라이스를 못다루는가?

루프 내에서 새 변수를 만들어서 할당해주면 된다. 심지어 같은 이름으로 선언해도 된다.

for _, sample := range samples {
	sample := sample
	fmt.Printf("Number: %d, Address: %p\n", sample.Number, &sample)
	pointers = append(pointers, &sample)
}

fmt.Println()

for _, pointer := range pointers {
	fmt.Printf("Number: %d, Address: %p\n", pointer.Number, pointer)
}

아까와 동일한 코드이며, sample := sample 한 줄만이 추가되었다.
돌려보면 놀랍게도

Number: 1, Address: 0xc00012e008
Number: 2, Address: 0xc00012e018
Number: 3, Address: 0xc00012e030

Number: 1, Address: 0xc00012e008
Number: 2, Address: 0xc00012e018
Number: 3, Address: 0xc00012e030

원하는 결과가 나온다.

profile
코딩 좋아합니다

0개의 댓글