초기화 변수

초기화 변수 (Initialized Variables)

Free Pascal에서 선언한 단순 변수는 기본적으로 자동으로 초기화되지 않아요. 변수가 0이라거나 어떤 기본값을 가질 거라 믿으면 안 됩니다. 실제로는 아무 의미 없는 쓰레기 값이 들어 있을 수 있기 때문이죠. 이런 문제를 해결하기 위해 '초기화 변수'라는 개념이 생겼어요. 일반 변수와의 차이는 선언할 때 초기값을 함께 적어 준다는 점이에요.

출처: Initialized variables

본문

이 규칙에는 예외가 두 가지 있어요.

  1. **관리되는 타입(Managed types)**이 대표적인 예외예요. 관리되는 타입은 항상 기본값으로 초기화되는데, 대개 참조 카운트를 0으로 만들거나 포인터 값을 Nil로 설정하는 방식으로 동작해요.
  2. 전역 변수도 0에 해당하는 값으로 초기화돼요.

다만 특정 변수를 0으로 만드는 동작이 때로는 오히려 잘못된 내용을 만들 수도 있어요. 아래 코드를 볼게요.

Type
  TWeekDays = (monday,tuesday,wednesday,thursday,friday,saturday,sunday);
  TWeekend = saturday..sunday;

var
  W : TWeekend;

begin
  Writeln(W);
end.

실행하면 이런 오류가 나요.

Runtime error 107 at $000000000040024A
$000000000040024A
$000000000042BF70
$00000000004001D2

그래서 변수는 사용하기 전에 항상 초기화하는 것을 강력히 권장해요. 변수 선언에서 간단히 해결할 수 있어요. 아래 선언을 볼게요.

Var
  S : String = 'This is an initialized string';

이렇게 하면 뒤에 오는 변수의 값이 지정한 값으로 초기화돼요. 조금 더 나은 방법도 있어요.

Const
  SDefault = 'This is an initialized string';

Var
  S : String = SDefault;

초기화는 배열과 레코드를 초기화할 때도 자주 쓰여요. 배열의 경우 초기화할 요소를 괄호로 감싸고 쉼표로 구분해서 적어야 해요. 초기화되는 요소의 개수는 타입 선언의 요소 개수와 정확히 같아야 합니다. 예시를 볼게요.

Var
  tt : array [1..3] of string[20] = ('ikke', 'gij', 'hij');
  ti : array [1..3] of Longint = (1,2,3);

상수 레코드에서는 초기화하려는 각 요소를 Field: Value 형태로 세미콜론으로 구분하고 괄호로 감싸서 적어요. 초기화하지 않을 필드는 생략할 수 있고, 사실 모든 필드를 건너뛸 수도 있어요. 필드를 건너뛰면 컴파일러가 경고를 내보내요.

Type
  Point = record
    X,Y : Real
    end;
Var
  Origin : Point = (X:0.0; Y:0.0);
  Partial : Point = (X:0.0);
  Empty : Point = ();

위 선언은 이런 경고를 발생시켜요.

iv.pp(7,27) Warning: Some fields coming after "X" were not initialized
iv.pp(8,20) Warning: Some fields coming after "" were not initialized

상수 레코드의 필드 순서는 타입 선언에서의 순서와 같아야 해요. 그렇지 않으면 컴파일 타임 오류가 발생합니다.

주목할 점은, 초기화 변수는 프로그램 시작 시 초기화되는 typed constant와 달리 변수가 유효 범위(scope)에 들어오는 시점에 초기화된다는 거예요. 지역 초기화 변수도 마찬가지로, 루틴이 호출될 때마다 초기화돼요. 그래서 이전 호출에서 바뀐 값도 다시 초기화되면서 원래대로 돌아가게 됩니다.

PChar 같은 초기화된 포인터 타입을 쓸 때는 주의가 필요해요. 아래 예시에서 S는 상수(읽기 전용) 프로그램 데이터 블록을 가리키는 포인터예요. 따라서 문자열 안의 한 문자에 값을 집어넣는 건 동작하지 않아요. S 자체에 값을 대입하는 건 당연히 동작하고요. 첫 번째 루틴은 오류가 나고, 두 번째는 정상이에요.

procedure foo1;
var
  s: PChar = 'PChar';
begin
  s[0] := 'a';
end;

procedure foo2;
var
  s: PChar;
begin
  s := 'PChar';
  s[0] := 'a';
end;

즉, 초기화된 포인터에 문자를 직접 쓰려 하면 문제가 생기니, 값을 통째로 대입하는 방식으로 써야 해요.

더 알아보기