본문 바로가기
Delphi/문법

델파이 포인터 (Pointer) 4편

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

델파이 포인터 (Pointer) 4편

 

 

 

 

4. 포인터 관련 함수

 

지금까지 한 것만으로는 포인터 변수를

어디에 어떻게 쓰는지 상당히 궁금할 것이다.

 

포인터 변수의 실제 응용은 다음에 다루기로 하고 여기서는

일단 포인터 변수에 관련된 나머지 사항에 대해 좀 더 알아 보기로 하자.

 

물론 지금까지 이해한 것만으로도 동적 자료 구조를 대충 구현할 수 있지만,

이 절에서 설명하는 내용을 이해하면 프로그램을 작성할 때 도움이 많이 될 것이다.

 

이 절에서 설명할 내용은 좀 전문적이고 하드웨어나

어셈블리어에 대한 기초지식을 요구하므로, 필요하다면 어셈블러 서적을

참조해 가면서 보기 바란다.

 

 

1) IBM PC 의 메모리 구조

 

포인터 형은 메모리에 저장된 다른 자료가 있는 곳을 가리키는 형이다.

그런데 그 포인터 변수가 어디를 가리킨다는 말은

그 포인터 변수의 값이 그곳의 주소라는 말과 같다.

 

따라서 포인터 변수에 대해 이해를 좀 더 확실히 하려면

IBM PC 라는 기계에서 메모리 주소가 어떻게 구성되어 있는지에 대해 알필요가 있다.

 

IBM PC 의 CPU 인 8086은 최대 1 MB 의 메모리를 가질 수 있다

(8086 의상의 기종인 80286, 80386, 80486도 MS-DOS를 사용할 때는 똑같다.).

 

이 1MB의 메모리를 8086은 세그먼트(segment) 라는 단위로 나누어 관리한다.

세그먼트는 최소 0 바이트 크기에서 최대 64KB 까지의 크기를 가질 수 있다.

또한 한 세그먼트 안에서는 옵셋(offset)이라는 상대적인 위치로

그 안의 어떤 위치를 지정한다.

 

8086이 사용하는 주소는 항상 이 세그먼트와 옵셋의 조합으로 표현이 된다.

그래서 $B000:$0000 이런 식으로 표현을 한다.

주소는 세그먼트 부분이 16 비트, 옵셋 부분이

16 비트로 구성되어 있는데, 이 주소를 가지고 메모리를 참조할 때는

세그먼트 부분을 왼쪽으로 4 비트 쉬프트한 다음 거기에

옵셋을 더한 값이 실제 메모리 주소가 된다.

 

예를 들어

 

$0040:$013C 라는 주소가 있으면

 

세그먼트 : 0040 = 0000 0000 0100 0000

옵셋 : 013C = 0000 0001 0011 1100

---------------------------------

0053C = 0000 0000 0101 0011 1100

 

이와 같이 하여 실제 메모리 주소는 $0053C 가 되는 것이다.

그런데, 이 방법으로는 같은 메모리 주소를 표현하는

세그먼트:옵셋 조합이 하나만이 있는것이 아니다.

 

예를 들어

 

$0050:$003C, $0000:$053C 도 같은 주소를 나타낸다.

어쨌거나 CPU 내에서 메모리의 어떤 주소를 참조하려면

세그먼트와 옵셋을지정해야 한다.

 

그런데 매번 세그먼트와 옵셋을 한꺼번에 지정하려면

너무 부담이 많으므로, 대개 어떤 세그먼트를 고정해 놓고

그 안에서 옵셋만 가지고 일을 한다.

 

그런데 동시에 메모리의 여러 곳에서 작업을 하는 경우가 많으므로

거기에 대배하여 8086 은 세그먼트 값을 저장하는 레지스터를 4개 가지고있다.

 

그것들에는 각각 코드

 

세그먼드(CS),

데이터 세그먼트(DS),

여분 세그먼트(ES),

그리고 스택 세그먼트(SS)라는 이름이 붙어 있다.

 

코드 세그먼트는 현재 수행하고 있는

기계어 프로그램이 들어 있는 세그먼트이다.

 

데이터 세그먼트와 여분 세그먼트는 현재 어떤 처리를 하고 있는

데이터가 들어 있는 것이고, 스택 세그먼트는 프로시져 호출이나

수식 계산 등의 임시 데이타가 들어갈 기억 장소이다.

 

한편 터보 파스칼에서는 각 세그먼트를 다음과 같이 사용한다.

 

- 코드 세그먼트 : 프로그램과 유닛이 컴파일된 코드 및 문자열 상수

- 데이터 세그먼트 : 전역 변수와 형 있는 상수

- 여분 세그먼트 : 고정된 목적은 없고 수시로 사용

- 스택 세그먼트 : 지역 변수와 프로시져 및 함수의 복귀 주소

 

이들에 대한 자세한 내용은 어셈블리어 서적을 참고하기 바라며,

일단은 이정도로만 이해하도록 하자.

 

중요한 것은 전역 변수는 데이터 세그먼트에,

지역 변수는 스택 세그먼트에서 기억 장소를 할당받는다는 것이다.

 

그러면 포인터가 가르키는 동적 변수는 어디에 생길까?

동적 변수가 만들어지는 영역은 힙 (heap) 이라고 부르며,

이 부분은 파스칼 프로그램이나 데이터가 기억되는

기억장소와는 별도로 만들어진다.

 

그리고 포인터가 가리키는 값은 이 힙 내의 주소가 된다.

이러한 기억장소 배치를 그림으로 나타내면 다음과 같다.

 

높은 주소

----- 힙 -

-------------------

스 택 -

- SS

- 데 이 타 - DS

- 코 드 -

낮은 주소

----------------------- CS

 

우리가 var 로 선언하는 포인터 변수

그 자체는 전역 변수이거나 지역 변수이므로

데이타 세그먼트 또는 스택 세그먼트에 저장된다.

 

그리고 그 포인터 변수의 값은 힙 중에 있는

어떤 동적 변수의 주소 값이 된다.

 

힙 영역은 이미 동적 변수로 할당된 영역과

그렇지 않은 자유 영역으로 구분될 수 있다.

 

New 나 GetMem 은 힙에서 적당한 크기의 기억장소를 잘라서

동적 변수로 만들고 포인터 변수에 그 주소 값을 저장하는 일은 한다.

 

Dispose나 FreeMem은 포인터 변수가 가리키는

힙의 주소부터 시작하는 동적 변수를해제하여 자유 영역으로 만든다.

 

 

 

2) @ 연산자와 Addr 함수

지금까지는 포인터를 써서 무슨 일을 하려면

New 나 GetMem을 써서 동적변수를 새로 만들어야 했다.

 

원래 표준 파스칼에서는 포인터는 힙에 있는 동적변수만을 가리킬 수 있다.

그러나 터보 파스칼에서는 힙 영역 뿐만 아니라

어디에 있는 변수든지 포인터를 통해 엑세스할 수 있다.

 

그것은 바로 @ 연산자가 있기 때문이다.

@ 연산자는 피연산자의 기억장치 주소를 돌려 준다.

 

즉 다음과 같이 하면

 

a : Integer;

p : ^Integer;

begin

p := @a;

 

이제 p 는 a 가 있는 기억 장소를 가리키므로

p^ 는 a 와 완전히 동일하다.

 

즉 p^ 에 무슨 일을 하면 그것은 a 에 한 것과 같이 된다.

이것을 잘 생각하면 @ 연산자는 ^ 의 반대로 볼 수 있다.

 

즉 p 와 p^ 사이에 성립하는 관계는

@a 와 a 사이에 성립하는 관계와 같다.

 

@ 연산자의 피연산자는 모든 종류의 변수와 형 있는 상수,

그리고 프로시져와 함수 이름이 될 수 있다.

 

요컨대 기억 장치 안에서 자리를 차지하고

있는것들이면 된다는 얘기다.

그러나 상수나 문자열 등은 여기에 쓸 수 없다.

 

한편 Addr 함수는 다음과 같은 형식을 지닌다.

 

function Addr( x ) : Pointer;

 

Addr 함수는 @ 연산자와 같다.

즉 Addr(x) 는 @x 와 같은 결과를 낸다.

 

똑같은 일을 하는 연산자와 함수를 왜 따로 만들었는지

좀 이상하긴 하지만, 어쨌든 이 두가지는 같다고 기억하기 바란다.

 

 

3) 주소 관련 함수

터보 파스칼에는 주소 관련 함수로 Addr 외에도

Ptr, Seg, Ofs 가 있다.

 

function Ptr ( Seg, Ofs : Word ) : Pointer;

function Seg ( x ) : Word;

function Ofs ( x ) : Word;

 

먼저 Ptr 은 세그먼트와 옵셋을 받아서

그 주소에 해당하는 포인터 값을 만들어 낸다.

 

예를 들어

 

var

p : ^Byte; begin

p := Ptr($0400, $003C);

 

를 수행하면 p 의 값은 $0400:$003C 를 가리키는 포인터 값이다.

 

따라서 p^는 $0040:$003C 번지에 있는 한 바이트가 된다.

 

Seg 와 Ofs 는 각각 인자로 주어진 변수의 주소에서

세그먼트와 옵셋 값을돌려 주는 함수이다.

 

이들은 나중에 나오겠지만 파스칼 프로그램 안에서

기계어나 어셈블리 수준의 저급한 동작을 할 때나 사용되므로

그런 것이 있나 보다 하고 넘어가기로 하자.

 

다음으로, CSeg, DSeg, SSeg, SPtr 의 네 함수가 있다.

 

function CSeg : Word;

function DSeg : Word;

function SSeg : Word;

function SPtr : Word;

 

이들은 각각 코드

 

세그먼트 레지스터(CS),

데이터 세그먼트 레지스터(DS),

스택 세그먼트 레지스터(SS),

스택 포인터(SP)의 현재 값을 돌려 준다.

 

5편계속...

728x90
반응형

'Delphi > 문법' 카테고리의 다른 글

델파이 포인터 (Pointer) 6편  (0) 2023.08.10
델파이 포인터 (Pointer) 5편  (0) 2023.08.09
델파이 포인터 (Pointer) 3편  (0) 2023.08.07
델파이 포인터 (Pointer) 2편  (0) 2023.08.04
델파이 포인터 (Pointer) 1편  (0) 2023.08.03

댓글