본문 바로가기
Work/개발 노트

[Go언어] for 루프에서 포인터 사용 시 동일 객체 참조 문제

by ★용호★ 2020. 11. 17.

문제

아래와 같이 for 루프를 사용했다.

 

for _, data := range sampleDataSlice {
    GetStore().storeOne(&data)
}

위 코드를 실행하면 저장된 모든 데이터가 동일한 값을 가지게 된다.

 

원인

위 코드에서 sampleDataSlice는 slice 이기 때문에 for 루프의 변수로 인덱스와 현 루프의 객체를 변수에 할당한다. 이 때, 인덱스를 사용하지 않고 그 뒤의 변수를 사용하게 될 경우 변수에는 슬라이스에 포함된 현재 요소가 지역 변수에 할당된다.

 

그러므로 변수에 할당되는 값은 계속 변경되지만 storeOne 함수로 전달되는 변수의 주소는 동일하기 때문에 storeOne 함수 안에서 사용한 변수는 결과적으로 제일 마지막에 지역변수에 할당했던 객체를 동일하게 가르키게 된다.

 

지역 변수의 주소 값을 storeOne 함수로 전달하기 때문에 다음번 루프에서도 동일한 변수의 주소를 함수의 인자로 전달하는 것과 같다.

 

해결

해결방법은 간단한데 지역 변수를 사용하지 않고 원본 객체 그대로를 함수 인자로 넘겨주면 된다.

 

for i := range sampleDataSlice {
    GetStore().storeOne(&sampleDataSlice[i])
}

 

slice가 아닌 map인 경우에는 아래와 같이 for 루프의 지역변수가 아닌 for 루프 안에서 지역변수를 선언하게 되면 반복될 때마다 새로운 지역변수를 사용하기 때문에 참조 문제를 해결할 수 있다.

 

for key := range sampleDataMap {
	data := sampleDataMap[key]
    GetStore().storeOne(&data)
}

 

아래와 같이 코드 작성하여 실행해보면 눈으로 확인이 가능하다.

 

fmt.Printf("======== 정상 포인터 참조 =========\n\n")
for i := range sampleDataSlice {
    fmt.Printf("%p\n", &sampleDataSlice[i])
}

fmt.Printf("======== 동일 포인터 참조 =========\n")
for _, data := range sampleDataSlice {
    fmt.Printf("%p\n", &data)
}
======== 정상 포인터 참조 =========
0xc000148500
0xc0001485f0
0xc000148640
0xc000148690
0xc0001486e0
0xc000148730
0xc000148780
0xc0001487d0
0xc000148820
0xc000148870

======== 동일 포인터 참조 =========
0xc0001488c0
0xc0001488c0
0xc0001488c0
0xc0001488c0
0xc0001488c0
0xc0001488c0
0xc0001488c0
0xc0001488c0
0xc0001488c0
0xc0001488c0

 

 

댓글