본문 바로가기
Delphi/문법

델파이 포인터 (Pointer) 2편

by MonoSoft 2023. 8. 4.
728x90
반응형

델파이 포인터 (Pointer) 2편

728x90

 

 

기억 장소의 할당과 해제

 

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편 계속...​

728x90
반응형

댓글