델파이 포인터 (Pointer) 2편
기억 장소의 할당과 해제
1) New 와 Dispose
첫번째에서는 포인터 변수를 선언하기만 하고
그것이 가리키는 것에 대해서는 따로 언급하지 않았다.
그래서 포인터 변수를 선언하면 자동으로 그것이 가리키는 것도 같이 생긴다고
생각하는 사람들이 있는데, 절대로 그렇지 않다.
첫번째에서 p 를 선언하고 곧장 p^ 를 썼지만,
실제로 그렇게 하면 십중팔구 시스템이 폭주할 것이다.
아까도 말했지만 포인터 변수는 다른 어떤 곳을 가리키는 값을 가진다.
포인터 변수를 처음 선언했을 때 그 값이 무엇인지 알 수 없다.
언뜻 생각하면 처음 선언했을 때 nil 이 될 것 같지만,
실제로는 어떤 값을 가지고 있으나 이보다 더욱 중요한 것은,
실제로 p 가 가리키는 것 (p^) 이 존재하지 않는다는 점이다.
우리가 포인터 변수를 선언한다는 것은
다른 어떤 것을 가리키는 그 값 (p 의 내용) 을 담을 수 있는
기억장소를 만드는 것이지,
그 포인터가 가리키는 것 (p^ 의 내용) 에 해당하는
기억장소를 선언하는 것은 아니다.
예를 들어
var
p : ^Integer;
라는 선언으로 생기는 것은 p 뿐이다.
우리가 var 선언으로 만드는 정적 변수는 프로그램이
시작되기 전에 컴파일 단계에서 기억장소를 다 할당받지만,
p^ 와 같이 포인터를 통해 사용되는 동적변수는 프로그램이
시작되어 수행 중에 기억장소를 할당받아야 한다.
동적변수에 기억장소를 할당하려면
표준 프로시져인 New 를 사용하여 한다.
procedure New ( var p : Pointer );
New 프로시져는 인자로 들어온 포인터 변수에 대응되는
동적 변수를 하나만들고 포인터가 그 동적 변수를 가리키도록 해준다.
따라서 포인터를 선언한 다음 그 포인터를 가지고
무슨 작업을 하려면 항상 New 프로시져로 그 포인터가
가리킬 동적 변수를 만들어 주어야 한다.
(항상 그런 것은 아니다. 만약 메모리의 특정위치를
사용할 경우는 New 를 사용해서는 안된다.)
New 에 의해 만들어지는 동적 변수의 형은 p 의 기본형에 따른다.
예를 들어
var
p : ^Integer;
q : ^String[20];
begin
New(p);
New(q);
p^ := 100;
q^ := 'Turbo Pascal';
:
프로그램의 수행 결과 p^ 는 2 바이트 크기의 Integer 형이 되지만,
q^ 는 21 바이트 크기의 String[20] 형이 된다.
동적 변수는 만들 수 있을 뿐만 아니라 없앨 수도 있다.
표준 프로시져인 Dispose 는 인자로 들어온
포인터 변수가 가리키는 동적변수를 없앤다.
procedure Dispose ( var p : Pointer );
Dispose 가 수행된 다음 p 의 값은 어떤 값인지 알 수 없으며,
p^ 은 기억장소가 없어진 상태이므로 사용할 수 없다.
만약 p^ 를 사용하고자 한다면 다시 New 를 사용하여
동적변수를 다시 할당받아야만 한다.
2) GetMem 과 FreeMem
앞에서 본 New 와 Dispose 는 인자로 들어온
포인터 변수의 기본형에 따라서 일을 처리했다.
하지만 첫번째에서도 말했듯이 터보 파스칼에는
기본형이 없는 Pointer 형이 있는데,
이 Pointer 형을 가지고 기억장소를 할당받으려면
대체 얼마만큼을 주어야 할지를 알 수 없다.
따라서 Pointer 형의 포인터가 가리키는 동적변수를
다루기 위해서 GetMem 과 FreeMem 이란 프로시져가 준비되어 있다.
procedure GetMem ( var p : Pointer; Size : Word );
procedure FreeMem ( var p : Pointer; Size : Word );
먼저 GetMem 은 Size 바이트 크기인 동적 변수를 만들어서
그것을 Pointer 형의 인자 p 가 가리키도록 한다.
주의할 것은 변수의 크기를 프로그래머가 지정한다는 점이다.
Size 는 Word 형인데, 지정할 수 있는 최대 크기는 65520 이다.
FreeMem 은 반대로 p 가 가리키는 동적 변수를 없앤다.
그런데 여기서도 Size를 지정하는데, 그것은 p^ 의 크기와 똑같아야 한다.
만일 숫자가 틀리면 그만큼의 기억장소를 사용할 수 없게 되거나,
아니면 동적 변수 영역이 엉망이 되어 버린다.
GetMem 의 사용법을 익히기 위해 다음 예제를 보자.
program TesetGetMem;
var
s : String;
p : Pointer;
n : Word;
begin
Write('Type string : ');
ReadLn(s);
n := Length(s) + 1; { 'ABCDE' = 5 + 1 = 6 }
GetMem(p, n);
Move(s, p^, n);
WriteLn('p^ = ', String(p^));
WriteLn('Size of p^ = ', n);
FreeMem(p, n);
end. { of main }
이 프로그램은 우선 문자열 s 를 읽어 들이고,
그 문자열의
실제 길이만큼 의 실제 문자열 변수의 길이 =
문자열의 길이를 나타내는 1 바이트 +
실제 문자열의 길이 동적 변수를 만든 다음
거기에 문자열의 내용을 복사한다.
그 다음 p^ 의 내용을 문자열로 형 변환하여
출력하고 나서 그 동적 변수를 없앤다.
이 방법은 실제로 문자열에 저장된 문자들의 갯수만큼만
기억장소를 차지하므로 기억장소를 절약할 수 있다.
여기서 p^ 의 내용을 형변환 하는 것은 p 가 실제로
어떤 형이 정해진 포인터 변수가 아니기 때문에
p^ 의 내용이 어떤 형인지 알 수가 없다.
때문에 p^ 를 사용하기 위해서는
반드시 형변환을 해 주어야만 한다.
만약 실제 p^ 의크기보다 형변환을 거친 p^ 의 크기가 크게 되면
동적변수의 영역은 엉망이 되거나
시스템이 폭주하게 될 것이다
3편 계속...
'Delphi > 문법' 카테고리의 다른 글
델파이 포인터 (Pointer) 4편 (0) | 2023.08.08 |
---|---|
델파이 포인터 (Pointer) 3편 (0) | 2023.08.07 |
델파이 포인터 (Pointer) 1편 (0) | 2023.08.03 |
런타임시에 컴퍼넌트 이동,크기조절 (0) | 2023.06.13 |
델파이 속성(Property) 이해하기 객체 지향 프로그래밍에서의 활용과 장점 (3) | 2023.06.01 |
댓글