포인터와 참조 타입

이미 돌려준 칸을 계속 가리키고 있으면 어떻게 될까?

포인터 타입

값이 주소인 변수

포인터(pointer) 타입의 변수가 갖는 값은 메모리 주소이거나 nil입니다. nil은 아무것도 가리키지 않는다는 뜻입니다. 포인터로는 힙(heap)의 칸에 닿을 수 있습니다. 힙은 4강에서 본, 실행 중에 필요할 때 칸을 받아 쓰는 곳입니다.

1강에서 포인터는 값이 든 메모리 칸의 위치를 담는 값이라고 했습니다. 힙의 칸에는 이름이 없어 포인터로만 닿습니다. 7080과 206은 교재의 값입니다.

포인터 연산

*를 붙이면 그 주소의 값

포인터의 기본 연산은 둘입니다. 대입은 포인터에 주소를 넣습니다. 역참조(dereferencing)는 포인터에 든 주소로 가서 거기 든 값을 가져옵니다. C++에서는 *로 적습니다.

*를 붙이지 않으면 주소 자체를 다룹니다. 주소를 거쳐 값에 닿는 것을 간접 주소 지정(indirect addressing)이라고 합니다.

문제 ① 허상 포인터

돌려준 칸을 아직 가리킵니다

힙의 칸은 다 쓰면 돌려줍니다. 4강에서 본 해제입니다. 그래도 포인터에는 그 주소가 그대로 남습니다. 이미 해제된 칸을 가리키는 포인터가 허상 포인터(dangling pointer)입니다. 그 칸으로 읽거나 쓰면 결과를 예측할 수 없습니다.

예시입니다. 815는 그 칸을 다시 쓰게 된 다른 데이터를 나타냅니다.

문제 ② 메모리 누수

아무도 가리키지 않는 칸

p1이 가리키던 칸을 두고 p1을 새 칸으로 돌리면, 처음 칸은 아무도 가리키지 않습니다. 자리는 차지하는데 닿을 길이 없습니다. 이런 칸이 가비지(garbage)이고, 이렇게 칸을 잃어버리는 일이 메모리 누수(memory leakage)입니다.

교재는 이런 칸을 손실된 힙 동적 변수(lost heap-dynamic variable)라고 부릅니다. 값은 예시입니다.

C와 C++의 포인터

유연한 만큼 조심해서

C와 C++의 포인터는 언제 어디서 할당된 변수든 가리킬 수 있습니다. &로 변수의 주소를 얻고 *로 역참조합니다. 포인터에 정수를 더하거나 빼서 다른 칸으로 옮기는 포인터 산술(pointer arithmetic)도 됩니다.

예시 코드입니다. p + 1은 칸 하나만큼 옮긴다는 뜻이라 100이 104가 됩니다. void *는 어떤 타입이든 가리키지만, 특정 타입으로 바꿔야 역참조할 수 있습니다.

참조 타입

포인터를 대신하는 타입

참조 타입(reference type)은 포인터와 비슷한 일을 합니다. C++의 참조는 포인터의 특별한 종류이고, 주로 함수의 매개변수에 씁니다. 자바는 참조를 넓혀 포인터를 완전히 대체했고, 자바의 참조는 주소가 아니라 객체를 가리킵니다. C#에는 둘 다 있습니다.

매개변수는 함수가 값을 넘겨받는 변수입니다. 객체는 값과 기능을 묶은 덩어리이고, 11강에서 다룹니다.

다음 편

닿을 길 없는 가비지는 누가 치울까요?

8편버려진 칸은 누가 치울까? 5강 목록