레코드 타입

레코드 타입 (Record types)

Free Pascal은 고정 레코드와 가변 부분(variant part)이 있는 레코드를 지원해요. 레코드 타입의 문법 다이어그램은 아래와 같아요.

출처: Record types

본문

다음은 유효한 레코드 타입 선언들이에요.

Type
  Point = Record
          X,Y,Z : Real;
          end;
  RPoint = Record
          Case Boolean of
          False : (X,Y,Z : Real);
          True : (R,theta,phi : Real);
          end;
  BetterRPoint = Record
          Case UsePolar : Boolean of
          False : (X,Y,Z : Real);
          True : (R,theta,phi : Real);
          end;

가변 부분(variant part)은 레코드에서 마지막에 와야 해요. case 문장의 선택적 식별자는 태그 필드(tag field) 값에 접근하는 데 쓰이는데, 이 값은 기본적으로 프로그래머에게 보이지 않아요. 이 식별자로 어떤 variant가 현재 활성화되어 있는지 알 수 있죠. 사실상 레코드에 새 필드를 도입하는 셈이에요.

Remarks: variant 부분은 중첩할 수 있어요. 예를 들어:

Type
  MyRec = Record
          X : Longint;
          Case byte of
            2 : (Y : Longint;
                 case byte of
                 3 : (Z : Longint);
                 );
          end;

레코드 레이아웃과 크기

레코드의 레이아웃과 크기는 다섯 가지 요소의 영향을 받아요.

  • 필드의 크기
  • 플랫폼에 의존하는 필드 타입의 정렬(alignment) 요구사항. 레코드 안의 타입 정렬 요구사항은 별도 변수로 쓸 때와 다를 수 있고, 필드 위치가 타입의 정렬 요구사항에 영향을 줄 수도 있어요.
  • 현재 활성화된 {$ALIGN N} 또는 {$PACKRECORDS N} 설정. (이 둘은 서로를 덮어써서 마지막에 지정된 것이 활성화돼요. 다만 정확히 같은 인자를 받지는 않으니 프로그래머 매뉴얼을 확인하세요.)
  • 현재 활성화된 {$CODEALIGN RECORDMIN=X} 설정
  • 현재 활성화된 {$CODEALIGN RECORDMAX=X} 설정

레코드의 variant 부분 레이아웃과 크기는, variant 부분을 첫 번째 요소가 태그 필드 타입의 필드인 레코드 타입의 필드로 대체한 뒤(태그 필드에 식별자가 선언된 경우) 가장 큰 variant의 요소들을 이어 붙인 것으로 결정돼요.

레코드에서 필드 F2의 오프셋은 이전 필드 F1의 오프셋과 F1 크기의 합을, F2의 요구 정렬 배수로 올림한 값이에요. 이 요구 정렬은 다음과 같이 계산돼요.

  • 요구 정렬은 필드 타입의 기본 정렬로 시작하고, 이 타입이 레코드에 나타난다는 사실과 레코드 안 필드 위치에 따라 조정될 수 있어요.
  • 요구 정렬이 현재 활성화된 {$CODEALIGN RECORDMIN=X}보다 작으면 이 X 값으로 바꿔요.
  • 현재 {$ALIGN N} 또는 {$PACKRECORDS N} 설정이 숫자 값이면: 요구 정렬이 N보다 크면 N으로 바꿔요. 즉 N이 1이면 모든 필드가 서로 바로 옆에 배치돼요.
  • RESET 또는 DEFAULT: 결과 요구 정렬은 타깃에 의존적이에요.
  • C: 요구 정렬은 현재 플랫폼의 공식 ABI 규칙에 따라 조정돼요.
  • POWER/POWERPC, MAC68K: (클래식) Macintosh PowerPC 또는 Macintosh 680x0 플랫폼의 공식 ABI 규칙에 따라 정렬 값이 조정돼요.

레코드의 크기는 레코드 마지막 필드의 오프셋과 이 필드 크기의 합을, 레코드의 요구 정렬 배수로 올림한 값이에요. 레코드의 요구 정렬은 다음과 같이 계산돼요.

  • 요구 정렬은 레이아웃을 잡는 동안 결정된, 가장 큰 정렬을 가진 레코드 필드의 정렬로 설정돼요.
  • 현재 {$ALIGN N} 또는 {$PACKRECORDS N} 설정이 C가 아니고, 요구 정렬이 현재 활성화된 {$CODEALIGN RECORDMAX=X}보다 크면, 요구 정렬은 X로 바뀌어요.
  • 현재 {$ALIGN N} 또는 {$PACKRECORDS N} 설정이 C와 같으면, 요구 정렬은 공식 ABI 규칙에 따라 결정돼요.

Remarks와 예제

Free Pascal은 "packed record"도 지원하는데, 모든 요소가 바이트 정렬된 레코드예요. 결과적으로 아래 두 선언은 동일해요.

     {$PackRecords 1}
     Trec2 = Record
       A : Byte;
       B : Word;
       end;
     {$PackRecords default}

그리고

     Trec2 = Packed Record
       A : Byte;
       B : Word;
       end;

첫 번째 선언 뒤의 {$PackRecords Default}로 기본 설정을 복원한다는 점에 주의하세요!

레코드가 메모리에 배치되는 방식은 플랫폼에 의존적이므로, 플랫폼 간에 호환되는 레이아웃을 보장하는 유일한 방법은 {$PACKRECORDS 1}을 쓰는 것이에요. (모든 필드가 이 플랫폼들에서 같은 의미의 타입으로 선언됐다고 가정할 때요.)

특히 Turbo Pascal 프로그램이 만든 레코드가 담긴 타입 파일을 읽어야 한다면, 제대로 읽는 게 사실상 실패할 가능성이 높아요. Free Pascal의 기본 {$PACKRECORDS N} 설정이 Turbo Pascal과 반드시 호환되지는 않기 때문이죠. 파일을 만든 Turbo Pascal 프로그램에서 쓴 설정에 따라 {$PACKRECORDS 1}이나 {$PACKRECORDS 2}로 바꿀 수 있어요. (16비트 MSDOS와 현재 플랫폼의 타입 정렬 요구사항이 달라 {$PACKRECORDS 2}에서도 실패할 수 있어요.)

Delphi에도 같은 이야기가 적용돼요. 데이터 교환이 보장되려면 생산자와 소비자가 모두 packed record를 쓰거나, 같은 플랫폼에서 같은 {$PACKRECORDS X} 설정을 써야 해요.

더 알아보기

레코드 레이아웃을 다룰 때는 정렬 설정({$ALIGN}/{$PACKRECORDS})을 신경 써야 해요. 외부 데이터 파일이나 다른 언어와 교환할 때는 {$PACKRECORDS 1}을 고려하세요.