Python 을 이용한 웹 개발을 수년간 진행한 적이 있다.
Python 은 모든 변수가 Object 로 되어 있어 딱히 값 타입의 변수가 없다.
그래서 개발 초반에 아무생각없이 List 를 제어하다가 버그를 만난 적이 많다.
>> a = [ 1,2,3 ]
>> b = a
>> b[1] = 4
>> print(a)
[1,4,3]
>> print(b)
[1,4,3]
참고로 Python 은
copy모듈내deepcopy라는 함수가 있다
참조 타입을 잘 이해하는 사람은 당연히 위 내용이 맞다고 생각하지만,
초보자의 입장에서는 저게 왜 저렇게 되는지 이해하기 힘들다.
C# 에서는 call by value 와 call by reference 가 엄연히 분리되어 있기 때문에 이러한 혼동은 오기 쉬울 것으로 보인다.
int[] arr0 = { 1, 2, 3 };
int[] arr1 = arr0;
int[] arr2 = new int[arr0.Length];
arr0.CopyTo(arr2, 0);
arr1[1] = 4;
Console.WriteLine("arr0: " + string.Join(",", arr0));
Console.WriteLine("arr1: " + string.Join(",", arr1));
Console.WriteLine("arr2: " + string.Join(",", arr2));
arr0: 1,4,3
arr1: 1,4,3
arr2: 1,2,3
위 코드와 결과에서 보다시피, 배열을 그냥 불러와 새로 정의하면
새로 정의된 값에는 기 정의한 배열의 heap 주소가 들어가 결국 같은 heap 주소를 바라보게 되어
서로의 값에 대한 변동이 일어나고 만다.
반면에, CopyTo처럼 값 자체를 복사하는 함수를 사용하면 두 배열은 분리되는 것을 확인할 수 있다.
아래는 각 코드를 그림으로 표현 및 설명한 내용이다.


stack 에 저장된 heap memory 주소를 직접 출력해 확인하고 싶었는데, 뭔가 IDE 설정 때문인지 빌드 설정 때문인지 안된다. Unsafe 기능을 없애면 된다고 하는데 찾아서 해봐도 해당 기능을 찾기가 쉽지 않다 😑
나중에 진짜 해당 내용에 대한 디버깅이 필요할 경우 다시 찾아보기로 한다.
[정정] 얕은 복사 vs 깊은 복사
얕은 복사는 배열/리스트의 각 슬롯 값을 "딱 한 겹만" 그대로 복사하는 것입니다. 깊은 복사는 슬롯 값이 참조(포인터)일 경우, 그 참조가 가리키는 실제 객체까지 새로 복제하는 것입니다.
CopyTo, Array.Clone, List.CopyTo 등은 전부 얕은 복사입니다.
제가 예시로 든 int[]는 요소가 값 타입이라 슬롯에 값 자체가 저장되어 있습니다. 즉 복사할 때 따라가야 할 "참조 대상"이 애초에 없으므로, 얕은 복사만으로도 이미 완전히 독립적인 복사본이 됩니다. 그래서 마치 깊은 복사처럼 보인 것이지, 실제로 깊은 복사가 일어난 게 아닙니다.
반대로 요소가 class(참조 타입)라면, 얕은 복사는 참조만 복사하므로 복사본과 원본이 같은 객체를 공유합니다. 복사본에서 객체 내부 값을 바꾸면 원본도 같이 바뀝니다. 이때만 얕은/깊은 복사의 차이가 실질적으로 드러납니다.
(참고: string은 참조 타입이지만 불변(immutable)이라 값 타입처럼 안전하게 동작하는 예외입니다.)