포인터
포인터 (Pointers)
값을 직접 다루는 대신, 값이 저장된 메모리 주소를 들고 다니는 변수도 있어요. 그게 바로 포인터예요. C에 익숙하다면 반가울, 여기서는 어떻게 선언하고 쓰는지 차근차근 볼게요.
출처: 문서
본문
Free Pascal은 포인터 사용을 지원해요. 포인터 타입의 변수는 다른 변수의 데이터가 저장될 수 있는 메모리의 주소를 담아요. 포인터 타입은 다음과 같이 정의할 수 있어요:
Pointer types
-> ^ type identifier
이 다이어그램에서 알 수 있듯이 포인터는 타입이 지정된(typed) 상태예요. 다시 말해 특정한 종류의 데이터를 가리켜요. 이 데이터의 타입은 컴파일 시점에 알아야 해요.
포인터를 역참조(dereference) 하면(변수 이름 뒤에 ^를 붙여서 표시) 그 결과는 마치 변수처럼 동작해요. 이 변수는 포인터 선언에 적힌 타입을 갖고, 포인터 변수가 가리키는 주소에 저장돼요. 다음 예시를 봐 볼게요:
Program pointers;
type
Buffer = String[255];
BufPtr = ^Buffer;
Var B : Buffer;
BP : BufPtr;
PP : Pointer;
etc..
이 예시에서 BP는 Buffer 타입을 가리키는 포인터이고, B는 Buffer 타입의 변수예요. B는 256바이트 메모리를 차지하지만 BP는 4바이트(또는 8바이트)만 차지해요. 주소 하나를 저장할 수 있을 만큼이면 충분하니까요.
표현식
BP^
은 BP의 역참조라고 불러요. 그 결과 타입은 Buffer이므로,
BP^[23]
은 BP가 가리키는 문자열의 23번째 문자를 뜻해요.
주의: Free Pascal은 포인터를 C와 거의 비슷하게 취급해요. 즉 어떤 타입을 가리키는 포인터는 그 타입의 배열인 것처럼 다룰 수 있어요. 이 관점에서 보면 포인터는 배열의 0번째 요소를 가리켜요. 따라서 다음 포인터 선언은
Var p : ^Longint;
다음 배열 선언과 동등하다고 볼 수 있어요:
Var p : array[0..Infinity] of Longint;
차이는 전자가 포인터만을 위한 메모리를 할당하고(배열을 위한 메모리는 아님), 후자가 배열 전체를 위한 메모리를 할당한다는 거예요. 전자를 쓰면 Getmem 함수를 써서 메모리를 수동으로 할당해야 해요. P^ 참조는 곧 p[0]과 같아요. 다음 프로그램이 이 점을 더 명확히 보여줄 거예요:
program PointerArray;
var i : Longint;
p : ^Longint;
pp : array[0..100] of Longint;
begin
for i := 0 to 100 do pp[i] := i; { Fill array }
p := @pp[0]; { Let p point to pp }
for i := 0 to 100 do
if p[i]<>pp[i] then
WriteLn ('Ohoh, problem !')
end.
Free Pascal은 C처럼 포인터 산술도 지원해요. 타입이 지정된 포인터 P에 대해,
Inc(P);
Dec(P);
은 각각 P가 가리키는 타입의 크기만큼 포인터가 가리키는 주소를 늘리거나 줄여요. 예를 들어
Var P : ^Longint;
...
Inc (p);
는 P를 4만큼 늘려요. 4가 longint의 크기이기 때문이죠. 포인터가 타입이 지정되지 않았다면 1바이트 크기로 가정해요(마치 ^byte 포인터인 것처럼).
포인터에는 일반 산술 연산자도 쓸 수 있어요. 다음은 유효한 포인터 산술 연산들이에요:
var p1,p2 : ^Longint;
L : Longint;
begin
P1 := @P2;
P2 := @L;
L := P1-P2;
P1 := P1-4;
P2 := P2+4;
end.
여기서 더하거나 빼는 값은 포인터가 가리키는 타입의 크기와 곱해져요. 위 예시에서 P1은 16바이트만큼 줄어들고, P2는 16만큼 늘어나요.
더 알아보기
- 포인터를 써서 연결 리스트를 만들 때 필요한 전방 타입 선언을 함께 보면 좋아요.
- 절차적 타입(메서드 포인터 등)에 대해서는 절차적 타입(Procedural types) 절에서 다뤄요.