본문 바로가기
Delphi/문법

델파이 포인터 (Pointer) 1편

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

델파이 포인터 (Pointer) 1편

728x90

 

 

포인터형을 이용한 자료 구조를 흔히 동적 자료 구조하고 하는데,

이것은 정적인 자료 구조인 배열이나 레코드 등의 자료형의 크기가

컴파일 시에 이미 결정되어 할당되는데 비하여 포인터를 이용한 자료형은

프로그램의 수행 중에 크기를 바꿀 수 있기 때문에 붙여진 이름이다.

 

동적 자료 구조는 흔히 고급 프로그래밍 언어를 배우면서 자칫 넘지 못하는 장애물이다.

여기서는 포인터의 기본적인 개념과 간단한 응용 사례를 다룬다.

 

 

1. 포인터의 개념 

 

 

1) 왜 동적인 자료 구조를 이용해야 하는가?

 

왜 동적인 자료 구조를 이용해야 하는가?

 

이에 대한 대답을 얻기 위해 리스서에 따라 늘어 놓은 것이다.

따라서 리스트는 다음과 같이 하나의 배열로 표현할 수 있을 것이다.

 

var

 List : array[1..ListSize] of SomeObject;

 

하지만 이러한 선언은 몇가지 문제점이 있다.

먼저 우리는 프로그램을 작성하기 전에 ListSize를 결정하여야 하는데,

리스트에 들어갈 목적물의 갯수가 고정되어 있다면 상관이 없지만

그렇지 않고, 수시로 변한다면 어떻게 해야할까?

 

적당히 어림해서 정할 수도 있겠지만 만에 하나라도

선언된 갯수보다많은 목적물이 들어갈 경우에는

프로그램은 수행 에러를 내게 된다.

 

그렇다면, ListSize를 예상되는 목적물의 갯수보다 훨씬 크게,

예를 들어 한 1000 개 정도로 잡으면 이러한 문제를 해결할 수 있을 것 같다.

 

하지만 우리는 한정된 기억 용량을 가지고 있기 때문에

리스트의 크기를 너무 크게 잡을 경우 프로그램은 비효율적이 되고 만다.

 

또한 프로그램의 수행 중에 배열에 들어 있는 목적물 중 하나를 지웠다고해 보자.

 

이 때 배열의 중간에 빈 공간이 생기는데, 배열은 연속적으로 기억되어야 하므로

우리는 일일이 모든 원소를 움직여 이 자리를 채워야 한다.

 

반대로 새로운 목적물을 하나 삽입한다면 마찬가지로

그 뒤에 있는 목적물을 모두 뒤로 밀어내야 한다.

 

이러한 작업은 프로그램의 수행 속도를 크게 떨어뜨리게 될 것이다.

이러한 문제들을 해결하기 위래 우리는 동적인 자료 구조인 포인터형을 배울 필요가 있다.

 

'동적 자료 구조(dynamic data sructure)'란

'정적 자료 구조 (static data structure)'에 대응하는 말이다.

 

정적 자료 구조는 컴파일시에 그 크기와 그 값이 저장될 곳이 결정되는데

비하여 동적 자료 구조는 프로그램 수행 도중에 그 크기와 저장되는

위치가 변경될 수 있다는 데서 붙여진 이름이다.

 

2) 포인터 변수의 선언과 이용 출처 입력

포인터의 선언은 따로 정의된 자료형의 이름이 있는 것이 아니라

그 포인터가 가리키게 될 자료형의 이름 앞에 꺽쇠(^) 표시를 해서 나타낸다.

 

예를 들어 정수형 자료를 가리키는 포인터는 다음과 같이 선언할 수 있다.

 

type

  IntPtr = ^Integer;

 

이제, IntPtr은 정수 하나를 가리키는 포인터 형이다.

물론 IntPtr 형의 변수를 선언할 수도 있다.

 

var

aPtr : IntPtr;

 

이제 aPtr은 정수에 대한 포인터형으로 선언되었다.

 

aPtr이란 변수는 다른 어떤 목적물(여기서는 Integer 형)을 가 리킨다는

사실을 나타내고 있다.

 

그렇다면 aPtr이 가지는 값은 어떤 것일까?

 

우리는 포인터 변수가 가지는 실제 값에 대해서는 알필요가 없다.

 

그것은 단지

어떤 값 - 여기서는 정수값 - 을 가리키는데

쓰일 수 있는 값 - 실제로는메모리의 주소 값 - 이다.

 

그 어떤 값, 즉 포인터가 가리키는 값을 나타내고 싶으면

포인터 변수의 이름 뒤에 꺽쇠(^) 표시를 하면 된다.

 

즉, aPtr^ 은 정수형 포인터 변수인 aPtr 가리키는,

Integer 형의 어떤 목적물을 나타낸다.

 

포인터 형은 Integer와 같은 단순형에 대해서만 정의할 수 있는 것이 아니고

배열이나 레코드, 문자열 등 어떤 자료형도 가리킬 수 있다.

 

사실 정수나 실수 하나에 대한 포인터는 별로 쓸모가 없고

대개 레코드나 배열에 대한 포인터가 많이 사용된다.

 

예를 들어

 

type

  CharArray = array[1..100] of Char;

  CharArrPtr = ^CharArray;

  Person = record Name : String[10];

  Age : Integer;

end;

 

  PersonPtr = ^Person;

 

var

  p : CharArrPtr;

  q : PersionPtr;

 

이 경우 p^ 는 CharArray 형, q^ 는 Person 형이 되므로

다음과 같은 변수 참조가 가능하다.

 

p^[i] p^ (Char 형 배열) 의 i 번째 원소 q^.Age q^ (Person 형 레코드) 의

Age 필드 한편 어떤 경우에는 포인터가 아무 것도 가리키지 않음을 나타낼 수도 있다.

 

파스칼에서는 포인터가 아무 것도 가리키지 않음을 나타내기 위해

nil 이란 예약어를 준비하고 있다.

 

즉 포인터의 값이 nil 이면 그것은 아무것도 가리키지 않는 포인터이다.

 

aPtr := nil; 포인터가 아무 것도 가리키지 않는다면

그것이 '가리키는 것' 이란 말을 쓸수가 없다.

 

따라서 위와 같이 aPtr 이 nil 일 때 aPtr^ 이란 표현은 무의미하다.

 

3) Pointer 형

앞에서 설명한 포인터형은 어떤 기본형을 가리키는 형으로 정의되었다.

 

예를 들어 Integer 에 대한 포인터와 Real 에 대한

포인터는 서로 다르므로 이들 사이에는 호환성이 없다.

 

예를 들어

 

var

  p : ^Integer;

  q : ^Real;

begin

  p := q; (* 컴파일 에러 *)

 

터보 파스칼에서는 이런 문제점을 해결하기 위해 어떤 형의 포인터와도

호환인 Pointer 라는 특별한 형을 준비해 놓고 있다.

 

var

  p : Pointer;

 

p 가 Pointer 형으로 선언되면 이 포인터는 어떤 포인터

변수와도 호환이므로 어디에나 마구 쓸 수 있다.

 

특히 이 Pointer 형이 유용한 경우는 포인터 변수를 가지고

무슨 일을 하는 프로시져나 함수의 형식 인자를 선언할 때이다.

 

procedure Something(var p: Pointer);

...

 

이런 식으로 선언해 놓으면 Something 은 모든 종류의 포인터 변수를

모두 받아들일 수 있으므로 편리하다.

 

Pointer 형 변수가 가리키는 동적 변수도 p^ 와 같은 형식으로 나타낼 수있다.

 

그런데, Pointer 형은 포인터가 가리키는 기본형이 정해진 것이 아니고

아무거나 마구 가리킬 수 있기 때문에 p^ 의 형이 정확히 무엇이라고 말할 수없다.

 

이 때문에 p^ 을 가지고 무슨 일을 하려면

항상 형 변환(type casting)을 해 주어야만 한다.

 

예를 들어 n 이 정수형 변수일 때

 

n := Integer(p^) + 1; 는 p^ 가 가리키는 값을 정수로

해석하여 거기에 1 을 더한 값을 n 에 저장하라는 뜻이다.

 

그러나 이 경우 p^ 에 들어 있는 값이 정수라는 것을 확실히 하여야 한다.

 

그것은 프로그래머의 책임이다.

Pointer 형 변수는 단지 메모리의 어디인가에 있는 목적물을 가르키기만 할 뿐이지

 

그 목적물이 어떤 형의 데이터인지는 모른다.

 

그 목적물의 데이터형을 결정하는 일은 프로그래머의 책임이다.

 

2편 계속...

728x90
반응형

댓글