본문 바로가기
Delphi/문법

델파이 포인터 (Pointer) 5편

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

델파이 포인터 (Pointer) 5편

728x90

 

 

 

 

5. 변수의 주소 지정

 

변수를 어떤 특정 주소에 위치시키거나 또는 어느 주소를 특정 자료형 단위 (워드, 바이트)로

다루는 방법에 대해서 살펴보기로 한다.

 

absolute

 

터보 파스칼에서는 메모리의 주소를 변수를 사용하여

선언할 때부터 지정할 수 있도록 하는 absolute 라는 예약어를 제공하고 있다.

 

다음의 선언문을 살펴보자.

 

var

 Keyboard : Byte absolute $0000:$0417;

 

이와 같이 선언하면 Keyboard 라는 변수는 1바이트를 차지하는 변수로써,

그 위치는 세그먼트 $0000 에서 옵셋 $0417 에 위치하게 된다.

 

이 선언문은 다음과 같이 선언될 수 있다.

 

var

  Keyboard : ^Byte;

begin

  Keyboard := Ptr($0000, $0417);

 

absolute 를 이용하면 포인터 변수도 아니지만

포인터 변수처럼 주소를 접근할 수있다.

 

다음 프로그램을 실행시키면 매우 흥미있는 결과가 나올 것이다.

 

program AbsoluteScreenAccess;

 

type

  ScrnCell = record ch : Char;

  Attrib : Byte;

end;

ScrnMap = array[1..25, 1..80] of ScrnCell;

 

var

Scrn : ScrnMap absolute $B800:0;

{ 배열을 $B800:0 에서부터 위치시킨다. 단색의 모노크롬일 경우는 $B000:0 이고,

EGA 나 VGA 칼라 그래픽 카드의 경우는 $B800:0 이다. }

 

procedure FillScreen(c: Char);

var

  x, y : Byte;

begin

  for y := 1 to 25 do

    for x := 1 to 80 do

    begin

      Scrn[y][x].ch := c;

      Scrn[y][x].Attrib := Random(10);

    end;

end;

 

{ of FillScreen }

 

begin

  FillScreen(Chr(219));

end.

 

절대변수는 명시된 주소 뿐만아니라 다른 변수의 주소로도 지정할 수 있다.

 

var

  s80 : String[80];

  Len : Byte absolute s80;

 

이 경우 변수 Len 은 변수 s80 의 시작 주소와 같은 주소에 지정된다.

그런데 s80 이 문자열형 변수이므로

Len 은 s80 의 길이, 즉 Length(s80) 과 같은 값을 가지게된다.

 

물론 이 방법은 별로 권장할 만한 것은 못된다.

 

Mem, MemW, MemL

 

이 변수는 메모리 전체를 하나의 일차원 배열처럼

다룰 경우 사용하는 변수로써,

 

Mem [ 세그먼트 : 옵셋 ] ..................

1바이트의 배열 MemW[ 세그먼트 : 옵셋 ] ..................

2 바이트 씩의 배열 MemL[ 세그먼트 : 옵셋 ] ..................

4 바이트 씩의 배열 이 배열은 일반적인 배열의 선언과는

 

약간 다르며 (터보 파스칼에서 미리 정의 해놓은 배열이다),

시스템을 직접 다루거나, 수행 속도를 빠르게 하게 위해서 프로그램 안에서

메모리의 주소를 직접 참조할 경우 유용하게 사용될 수 있다.

 

Mem[$B800:$0010) := $A0;

x := Mem[$B800:$0010];

과 같이 하면 $B800:$0010 번지를 엑세스하는 것이 된다.

 

그러나 우리가 프로그램에서 선언한 자료를 이렇게 다루는 것은 좋지 않고,

대개 메모리의 고정된 주소에 있는 어떤 값을 읽거나 쓸 때 이것을 사용한다.

 

예를 들어 바이오스 데이터 영역 중 $0000:$0449 에는현재

화면 모드의 상태를 나타내는 값이 들어 있다.

 

따라서 이 값을 알아 내려면

 

CurrentMode := Mem[$0000:$0449];

와 같이 하면 된다.

 

물론 이렇게 하는 것보다는 Crt 유닛의 TextMode 변수를 쓰는것이

훨씬 간단하지만, 표준 유닛에서 준비되어 있지 않는 경우에는 이렇게 밖에 할수 없다.

 

한편, Mem 배열은 Byte 형이지만,

MemW 는 Word 형을, MemL 은 LongInt 형의 데이터를

메모리에서 직접 엑세스하기 위해 준비되어 있다.

 

program MemAccess;

 

procedure FillScreen(c: Char);

var

  x, y : Byte;

begin

  for y := 1 to 25 do

    for x := 1 to 80 do

    begin

      Mem[$B800:(y - 1) * 160 + (x - 1) * 2 ] := Ord(c);

      Mem[$B800:(y - 1) * 160 + (x - 1) * 2 + 1] := Random(10);

    end;

end;

 

{ of FillScreen }

 

begin

  FillScreen('*');

end.

 

 

6. 프로시져와 함수 자료형

 

터보 파스칼은 프로시져를 하나의 자료형으로 선언할 수 있게 하고 있다.

프로시져 형은 특정한 인자를 받아들이는 프로시져나 함수로서,

 

어떤 변수가 프로시져 형으로 선언되면 그 변수에는 특정한 프로시져나

함수를 지정할 수도 있고 다른 프로시져의 인자로 넘겨줄 수도 있다.

 

또 그 변수에 지정된 프로시져나 함수를 호출할수도 있다.

 

{$F+} { 원거리 호출 }

 

program TestProcType;

 

uses

  Graph;

 

type

  PlotProc = procedure (x, y: Integer);

 

var

  Plot : PlotProc;

 

procedure EGAPlot(x, y: Integer);

begin

:

end; { of EGAPlot }

 

procedure HercPlot(x, y: Integer);

begin

:

end;

 

{ of HercPlot }

 

begin

  { of main }

  :

  case GraphicsCard of

    EGA : Plot := EGAplot;

    Herc : Plot := HercPlot;

  end;

  :

  Plot(100, 200); :

end. { of main }

 

위의 프로그램에는 x, y 두 개의 인자를 받아들이는

프로시져 형이 PlotProc 이라는 이름으로 선언되어 있다.

 

Plot 은 PlotProc 형의 프로시져를 그 값으로 가질 수 있는 프로시져 형 변수이다.

 

주 프로그램의 앞 부분에서 GrapicsCard 의 값에

따라서 Plot 에 EGAPlot 또는 HercPlot 프로시져 중 하나를 지정해 준다.

 

그리고 나서 Plot 를 호출하면 앞에서 Plot 에

어느 것이 지정되었는가에 따라 EGAPlot 과 HercPlot 둘 중 하나가 호출된다.

 

만일 이렇게 하지 않고 if 문을 사용한다면

다음 과 같이 해야 할 것이다.

 

if GraphicsCard = EGA then

  EGAPlot(100, 200)

else

  HercPlot(100, 200);

 

Plot 이란 프로시져를 사용하는 곳마다

이렇게 해 주어야 하므로 귀찮기 짝이 없다.

 

이와 같이 프로시져 형은 상황에 따라서

서로 다른 프로시져를 호출해야 할 경우 유용하게 쓰인다.

 

다음으로는 프로시져 형 인자에 대해 알보자.

우리는 프로그램으로 어떤 함수의 정적분을 구하는 함수를 만들 수 있다.

 

고등학교 수학에 나오는 사다리꼴 공식을 쓴다고 해 보자.

 

program Integrate;

 

var

  a, b : Real;

 

function f(x : Real) : Real;

begin

  f := x * x;

end; { of f }

 

function Integral(a, b : Real) : Real;

var

  i : Integer; h, s : Real;

begin s := 0.0;

  h := (b - a) / 100.0;

 

  for i := 1 to 100 do

    s := s + (f((i - 1) * h) + f(i * h)) * h / 2;

 

  Integral := s;

end;

 

{ of Integral }

 

begin

  Write('Enter a and b ==> ');

  ReadLn(a, b);

  WriteLn(a: 5: 2, ', ', b: 5: 2);

  WriteLn('Integral = ', Integral(a, b): 10: 3);

end. { of Integrate }

 

위의 프로그램은

 

b

x?dx

a

 

을 구한다.

 

그런데, 우리가 x?뿐만 아니라 x^3 이나 Sin(x), Log(x) 등의

적분을 구하고 싶으면 어떻게 할까?

 

각 함수마다 모두 적분하는 함수를 따로 만들어 줄 수도

있겠지만 그것은 낭비이다.

 

이럴 때 프로시져 형 인자를 사용하면 이 문제가 간단히 해결된다.

 

{$F+} { 원거리 호출 }

 

program Integrate;

 

type

  IntFunc = function (x : Real) : Real;

 

var

  x, y : Real;

 

function MyFunc(x : Real) : Real;

begin

:

end; { of MyFunc }

 

function Integral(a, b : Real; f : IntFunc) : Real;

begin

: (* 위와 똑같음 *)

end; { of Integral }

 

begin :

  x := Integral(-1.0, 1.0, Sin(x));

  y := Integral(p, q, MyFunc(x)); :

end.

 

위에서 Integral 함수에서 적분할 함수를 프로시져

형 인자로 받았기 때문에 Integral 을 호출할 때 원하는 함수를 지정해 주면

그 함수에 대한 적분이 계산되는것이다.

 

물론 Sin(x) 와 같은 표준함수도 되고,

MyFunc 와 같이 프로그래머가 정의한 함수라도 상관없다.

 

단지 그 함수의 인자와 반환값의 형이 IntFunc 형의 선언과

일치하기만 하면 된다.

 

이것이야 말로

 

b

f(x) dx a

728x90
반응형

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

델파이 포인터 (Pointer) 7편  (0) 2023.08.11
델파이 포인터 (Pointer) 6편  (0) 2023.08.10
델파이 포인터 (Pointer) 4편  (0) 2023.08.08
델파이 포인터 (Pointer) 3편  (0) 2023.08.07
델파이 포인터 (Pointer) 2편  (0) 2023.08.04

댓글