이전에 작성한 델파이 예제(2) - Move를 이용한 구조체에 문자열 대입하기에 이어지는 글이라고 봐도 무방할 것 같다.
모든 데이터의 길이가 일정하다면 편리하지만, 만약 데이터의 길이가 종류에 따라 다르다면 각각의 구조체를 선언하고 그것에 맞는 구조체를 선언해서 데이터를 관리해야하는 부담감이 생긴다. 그럴 때 사용하기 좋은 것이 바로 델파이의 '가변레코드'이다.
가변레코드는 말 그대로 가변적인 길이를 가지는 레코드이다. 물론 동적으로 크기가 변하는 것은 아니고, 접근자에 따라 선언한 멤버 변수 중 하나의 데이터 구조를 가지며 전체 레코드의 크기는 가장 큰 길이로 메모리를 잡게된다.
예를 들어 아래의 코드를 보자
type TInfo = record case Boolean of True : (iPName, iPCode : array[1..5] of Char; iAge : array[1..2] of Char); False : (iAName : array[1..10] of Char; iColor : array[1..5] of Char); end; var aInfo : TInfo;
위의 코드는 가변레코드의 간단한 예제이다. TInfo구조체는 5 + 5+ 2의 12바이트이거나10 + 5의 15바이트의 구조 중 하나를 가지게 된다. 하지만 메모리는 둘(여러 개를 정의할 경우 여러 개 중) 중 가장 큰 크기를 확보하게 되며, 위 예제에서는 15바이트의 크기를 가진 구조체가 된다. 그리고 TInfo형으로 선언한 aInfo에 값을 저장하고 나면 각각의 공간이 아닌 aInfo라는 메모리 하나에 값이 저장되며, 두 가지 방법으로 메모리에 접근이 가능하게 된다.
즉, aInfo.iPName으로 접근할 경우 앞에서부터(현재 가장 앞에 선언되어있으므로) 5바이트만큼을 읽고, aInfo.iAName의 경우 앞에서부터 10바이트를 읽는다고 생각하면 된다.
실제로 작성했던 코드를 가져왔다. (물론 변수명을 약간 수정하였음)(비밀서약서 어제 사인함)(무섭)
unit MarketAverage; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, Packet, ExtCtrls; type PMarket = ^TMarket; TMarket = record case Integer of 1 : (A1 : TSISE_O_A1); // A1형시세 2 : (A3 : TSISE_O_A3); // A3형시세 3 : (C2 : TSISE_O_C2); // C2형시세 end; function MainForm.DataToInfo(var aData : String) : PInfo; var aWorkedData : PMarket; begin New(aWorkedData); aWorkedData := PMarket(aData); iCode := aWorkedData.A3.datacode; if aWorkedData.A3.datacode = 'A3' then aData := aWorkedData.A3.DataTime else if aWorkedData.A3.datacode = 'A1' then aData := aWorkedData.A1.DataTime else if aWorkedData.A3.datacode = 'C2' then aData := aWorkedData.C2.DataTime; end; end;
먼저 Packet이라는 pas파일에 구조체를 정의하였고, 이것을 Uses를 통해 사용할 것임을 명시하였다.
그리고 TMarket이라는 구조체를 선언해 준 부분을 보자. case integer of 로써 여러가지를 선언하였는데 여기서 Boolean은 둘 중 하나, Integer는 여러 개의 변수를 선언하고 싶을 때 사용하는 것으로 생각된다. 단순히 case문을 사용하기 위한 것으로 생각되는데, 혹시 내가 잘못 생각한다면 댓글을 남겨주시길,,,
무튼 내가 작성한 코드에는 실제로 9가지의 시세구조가 있어서 Integer형으로 1부터 9까지 선언해주었다. 물론 위의 코드는 그 중에 3개만 가져온 것이다. 여기서 TSISE_O_A1과 같은 자료형은 Packet에서 정의해두었기 때문에 사용할 수 있었다. (저 자료형 역시 구조체이다).
그럼 위와 같은 코드의 편리함은 무엇이 있을까?
그 아래 DataToInfo라는 함수를 보자. aData라는 String변수를 매개변수로 전달받고 있으며, 방금 선언한 구조체를 가리킬 수있는 PMarket형의 aWorkedData라는 변수를 지역변수로 선언해주었다.
포인터이기 때문에 New키워드를 통해서 새로 메모리를 생성할 수 있으며, 이는 다른 함수 전달해주기 위해 일부러 동적으로 선언하였다. 그리고 aWorkedData에 PMarket형으로 포인터 형변환을 시킨 aData변수를 대입해주었다.
이 부분이 제일 중요하다.
다시 한 번, 현재 aData는 String형 변수이고, aWorkedData는 가변레코더를 가리키는 포인터, 즉 가변레코더의 주소를 가지는 변수이다. 이것을 PMarket이라는 자료형을 붙여서 String변수이지만 TMarket의 형태로 메모리를 접근할 수 있게 형변환 시켜서 TMarket변수에 대입해주고 있는 모습이다.
그렇기 때문에 aData는 단순 문자열이었지만, aWorkedData.A3.datacode 를 통해서 A3구조체 안에 있는 datacode라는 멤버변수처럼 문자열에 접근할 수 있는 것이다. 예를 들어서 A3구조체 안에 datacode가 인덱스 [10..15]의 5바이트라면 aData[10]부터 aData[15]까지를 move나 copy하지 않고도! 심지어 변수명으로! 메모리를 접근할 수 있는 것이다.
게다가 가변형레코드를 써서 좋은 또 다른 점은 데이터의 길이가 다른 것과 상관없이 구조체 변수명으로 접근이 가능하다는 것이다. 위의 코드의 경우 A1, A3, C2 모두 같은 위치에 datacode를 가지고 있다. 그렇기 때문에 if문에서 aWorkedData.A3.datacode로 접근하여 그것이 A3인지 판별하였다. 같은 위치에 있기 때문에 aWorkedData.A3.datacode = aWorkedData.A1.datacode = aWorkedData.C2.datacode일 것이다. (물론 이것은 모든 데이터에서 판별하는 코드부분이 동일한 인덱스에 와야한다는 가정이 필요하다)
그래서 해당코드에 따라 aWorkedData.(코드).DataTime으로 실제 DataTime의 값을 받아올 수 있다.
만약에 A1은 code - DataTime
A3는 code - code2 - code3 - DataTime
C2는 code - datainfo - DataTime - LastTime 과 같이 다른 메모리 구조로 되어있어도 상관없이 DataTime에 접근할 수 있다. 세상에. 얼마나 편한지.
물론 with를 통해서 code값에 따라 한 줄로 정의할 수 있다면 더할나위없이 좋겠지만 코더가 보는 문자열과 델파이가 처리하는 변수, 문자열은 다르기 때문에 불가능하지 않을까 싶다. (안해봄)(무슨 말인지 모르면 그냥 스킵)
무튼 이렇게 가변레코드를 선언함으로써 다른 길이의 데이터가 들어와도 가변레코드에 포인터 형변환을 통해서 대입을 해주면, code나 key값을 통해서 어떤 구조체인지 판별한 후에 인덱스가 아닌 구조체의 멤버변수명으로 실제 주소값과 상관없이 문자열을 처리할 수 있다는 장점을 가진다. (결론)(이 문장만 읽어도 될듯)
그럼 이만!
'노동자의 삶 > 델파이' 카테고리의 다른 글
델파이에서 Move와 Copy의 차이점 (0) | 2018.04.23 |
---|---|
델파이 실행오류 - 동시에 두 개의 델파이를 켜고 싶을 때 (0) | 2018.02.27 |
델파이 예제(2) - Move를 이용한 구조체에 문자열 대입하기 (0) | 2018.02.14 |
윈도우 업데이트 후 델파이 실행오류 (0) | 2018.02.13 |
델파이 기본적인 연산자와 키워드들 (0) | 2018.02.09 |