Devin.KR

Pascal 시작하기 - 프로그램의 뼈대

개발자KR 조회 0

이 장에서 배우는 것

이 책은 작은 동네 카페의 계산대 프로그램을 장마다 조금씩 키워 가며 Object Pascal 을 배운다. 첫 장에서는 프로그램을 키우기 전에, 가장 작은 프로그램 하나가 어떤 뼈대로 이루어지는지부터 확인한다. 컴파일러가 설치되어 있는지 확인하고, 파일 하나를 컴파일해서 실행하고, 화면에 글자를 찍는 데까지 간다. 프로그래밍 경험이 없어도 따라올 수 있도록 용어는 나올 때마다 풀어 쓴다.

  • Pascal, Object Pascal, Delphi, Free Pascal 이 서로 어떤 관계인지 설명할 수 있다.
  • 터미널에서 fpc 가 설치되어 있는지 확인하고 main.pas 를 컴파일해 실행할 수 있다.
  • program, uses, begin, end. 로 이루어진 프로그램의 뼈대를 읽고 쓸 수 있다.
  • 모든 파일 첫머리에 놓는 {$mode objfpc}{$H+} 가 무엇을 정하는지 말할 수 있다.
  • WriteLn 과 Format 으로 글자와 숫자를 출력할 수 있다.

문제 상황

동네 카페 사장님이 계산대 프로그램을 만들어 달라고 했다고 하자. 지금은 메뉴 가격을 종이에 적어 두고 계산기를 두드린다. 처음부터 주문, 재고, 정산을 모두 만들 수는 없다. 우선 해야 할 일은 훨씬 작다. 가게 이름과 메뉴 가격이 화면에 나오는 프로그램을 만들어 보는 것이다.

그런데 막상 시작하면 질문이 연달아 나온다. Pascal 이라는 이름과 Delphi 라는 이름을 둘 다 들었는데 같은 것인가. 인터넷에서 찾은 예제는 첫 줄이 서로 다른데 어느 쪽을 따라야 하는가. 컴파일러는 무엇이고 어디서 받는가. 이 장은 이 질문들에 답하고, 이후 모든 장의 출발점이 될 파일 하나를 만든다.

Pascal 계열 이름 정리

이름이 여러 개라 처음에는 헷갈리지만 관계는 단순하다. 언어 이름이 있고, 그 언어를 다루는 제품 이름이 있다.

Pascal 에서 Object Pascal 이 나왔고, Delphi 와 Free Pascal 은 Object Pascal 을 다루는 서로 다른 도구다.
Pascal 계열 이름이 가리키는 대상
이름종류한 줄 설명
Pascal언어1970년 무렵 니클라우스 비르트(Niklaus Wirth)가 설계한 언어. 읽기 쉬운 구조가 특징이다.
Object Pascal언어Pascal 에 클래스와 객체 기능을 더한 언어. 이 책이 배우는 언어다.
Delphi상용 개발 도구Embarcadero 가 만든 개발 환경. 안에서 Object Pascal 을 쓴다.
Free Pascal무료 컴파일러오픈 소스 컴파일러. 명령 이름은 fpc 이고 Object Pascal 을 번역한다.

컴파일러(compiler)는 사람이 쓴 소스 코드를 컴퓨터가 직접 실행할 수 있는 파일로 번역하는 프로그램이다. 이 책은 Free Pascal 3.2 의 fpc 를 쓴다. 무료이고, macOS 와 Linux 에서 터미널 명령 하나로 컴파일할 수 있기 때문이다.

Delphi 와 다른 점. Free Pascal 은 Delphi 방언도 지원하지만, 문법 모드를 파일마다 직접 지정해야 한다. 이 책은 objfpc 모드를 쓴다. Delphi 에서 만든 코드를 가져와 쓸 때는 이 모드 차이 때문에 몇 군데를 손봐야 할 수 있다. 그런 곳은 해당 장에서 짧게 짚는다.

fpc 설치 확인

터미널을 열고 다음 명령을 입력한다. 컴파일러 버전만 출력하는 명령이다.

fpc -iV

3.2.2 처럼 3.2 로 시작하는 숫자가 나오면 준비가 된 것이다. command not found 같은 메시지가 나오면 설치되지 않은 것이다. macOS 에서는 Homebrew 로 brew install fpc, Debian 계열 Linux 에서는 sudo apt install fp-compiler 로 설치할 수 있다. 배포판마다 패키지 이름이 다를 수 있으니 설치 방법은 Free Pascal 다운로드 안내에서 확인한다. 3.2 보다 낮은 버전이라면 이 책의 예제 일부가 동작하지 않을 수 있다.

프로그램의 뼈대

Pascal 프로그램은 위에서 아래로 읽히는 정해진 순서가 있다. 가장 작은 형태는 다음 네 부분으로 이루어진다.

프로그램은 지시자, program 이름, uses 목록, begin 에서 end. 까지의 본체 순서로 이루어진다.

program 과 uses

program Main; 은 프로그램의 이름을 선언한다. 이름은 영문자와 숫자, 밑줄로 짓고 숫자로 시작할 수 없다. 이 책에서는 모든 장에서 Main 이라는 이름을 쓰고 파일 이름을 main.pas 로 둔다.

uses 는 이 프로그램이 가져다 쓸 유닛(unit) 목록이다. 유닛은 미리 만들어 둔 기능 묶음이다. 이 장에서는 문자열을 서식에 맞춰 만드는 Format 이 들어 있는 SysUtils 하나만 가져온다. 목록의 이름은 쉼표로 구분하고 마지막에 세미콜론을 붙인다. 유닛을 직접 만드는 방법은 따로 다룬다.

begin 과 end.

begin 부터 end. 까지가 프로그램의 본체이며, 실행하면 이 안의 문장이 위에서 아래로 한 줄씩 실행된다. 문장(statement)은 컴퓨터에게 시키는 일 하나다. 문장 끝에는 세미콜론을 붙여 다음 문장과 구분한다. 맨 마지막 end 뒤에는 세미콜론이 아니라 마침표가 온다. 이 마침표는 컴파일러에게 소스 코드가 여기서 끝난다고 알리는 신호이며, 빠지면 컴파일되지 않는다.

Pascal 은 대소문자를 구분하지 않는다. WriteLn 과 writeln 은 같은 이름이다. 이 책에서는 읽기 쉽도록 WriteLn, SysUtils 처럼 단어마다 첫 글자를 대문자로 쓴다.

주석

주석(comment)은 컴파일러가 무시하는 설명글이다. 한 줄 주석은 // 로 시작한다. 여러 줄에 걸친 주석은 중괄호 { ... } 로 감싼다. 같은 중괄호라도 여는 괄호 바로 뒤에 달러 기호가 붙으면 주석이 아니라 컴파일러에게 주는 지시가 된다. 다음 절에서 그 지시를 다룬다.

{$mode objfpc}{$H+}

이 책의 모든 파일은 첫 줄에 {$mode objfpc}{$H+} 를 둔다. 컴파일 지시자(compiler directive)라고 부르며, 컴파일러에게 이 파일을 어떤 규칙으로 번역할지 알려 주는 설정이다. 두 개가 붙어 있다.

첫머리 컴파일 지시자 두 개가 정하는 것
지시자정하는 것이 책에서 필요한 이유
{$mode objfpc}문법 모드클래스, 예외 처리 같은 Object Pascal 기능을 쓰기 위한 모드다.
{$H+}기본 문자열 형식string 이 길이 제한 없는 문자열이 되게 한다. 빼면 255바이트 제한이 있는 옛 방식이 된다.

한글은 UTF-8 로 저장하면 한 글자가 3바이트이므로, 255바이트 제한은 생각보다 금방 넘는다. 그래서 {$H+} 를 항상 붙인다. 중괄호 안에 공백이나 오타가 있으면 지시자로 인식되지 않고 그냥 주석이 되므로 정확히 적어야 한다.

WriteLn 과 Format

WriteLn 은 괄호 안의 내용을 화면에 쓰고 줄을 바꾼다. 쉼표로 여러 값을 이어 쓸 수 있고, 글자는 작은따옴표로 감싸고 숫자는 그대로 적는다. 줄 바꿈 없이 이어서 쓰려면 Write 를 쓰고, 빈 줄 하나를 찍으려면 괄호 없이 WriteLn; 만 쓴다.

Format 은 서식 문자열 안의 표시 자리를 뒤에 주는 값으로 채워 새 문자열을 만든다. 값은 대괄호 [ ] 안에 쉼표로 나열한다. 이 장에서 쓰는 표시 자리는 두 가지다.

  • %d: 정수를 넣는다.
  • %.2f: 소수를 소수점 아래 두 자리로 고정해 넣는다.

소수를 그대로 출력하면 자릿수가 환경에 따라 달라질 수 있으므로, 이 책은 소수를 출력할 때 항상 Format 으로 자릿수를 고정한다. 서식의 나머지 규칙은 문자열을 다루는 장에서 자세히 다룬다.

완성 코드

아래 내용을 main.pas 로 저장한다. 한글이 들어 있으므로 UTF-8 로 저장해야 한다.

main.pas

{$mode objfpc}{$H+}
program Main;

uses
  SysUtils;

begin
  // 가게 이름
  WriteLn('=== 모퉁이 카페 계산대 ===');
  WriteLn;

  // WriteLn 에 값을 쉼표로 이어 쓴다
  WriteLn('메뉴 1: 아메리카노 ', 4500, '원');

  // Format 으로 문장을 만들어 출력한다
  WriteLn(Format('메뉴 2: 카페라테 %d원', [5000]));
  WriteLn(Format('아메리카노 2잔: %d원', [4500 * 2]));

  // 소수는 자릿수를 고정한다
  WriteLn(Format('부가세 제외 단가: %.2f원', [4500 / 1.1]));

  WriteLn('영업 준비 끝');
end.

줄별 해설

  • {$mode objfpc}{$H+}: 문법 모드와 문자열 형식을 정하는 지시자다. 파일 첫머리에 둔다.
  • program Main;: 프로그램 이름 선언이다. 세미콜론으로 끝난다.
  • uses SysUtils;: Format 이 들어 있는 유닛을 가져온다. 이 줄이 없으면 Format 을 찾을 수 없다는 오류가 난다.
  • begin: 본체의 시작이다.
  • WriteLn('=== 모퉁이 카페 계산대 ===');: 글자를 출력하고 줄을 바꾼다.
  • WriteLn;: 아무 값도 주지 않으면 빈 줄이 하나 나온다.
  • WriteLn('메뉴 1: 아메리카노 ', 4500, '원');: 글자, 숫자, 글자를 이어 출력한다. 숫자 4500 은 따옴표 없이 쓴다.
  • Format('메뉴 2: 카페라테 %d원', [5000]): %d 자리에 5000 이 들어간 문자열을 만들고, 바깥의 WriteLn 이 그것을 출력한다.
  • [4500 * 2]: 대괄호 안에서는 계산식도 쓸 수 있다. 결과 9000 이 %d 에 들어간다.
  • [4500 / 1.1]: / 는 나눗셈이고 결과는 소수다. %.2f 가 소수점 아래 두 자리로 반올림해 4090.91 을 만든다. 부가세 10%가 포함된 4500원에서 부가세를 뺀 값을 어림해 본 것이다.
  • end.: 본체의 끝이자 소스 코드의 끝이다. 마침표를 잊지 않는다.

실행 결과

같은 폴더에서 컴파일하고 실행한다.

fpc main.pas
./main

fpc main.pas 는 컴파일러 버전과 컴파일한 줄 수 같은 진행 메시지를 몇 줄 출력하고, 경고 없이 끝나면 같은 폴더에 실행 파일 main 을 만든다. 진행 메시지의 내용은 환경마다 다르다. ./main 의 출력은 다음과 같다.

=== 모퉁이 카페 계산대 ===

메뉴 1: 아메리카노 4500원
메뉴 2: 카페라테 5000원
아메리카노 2잔: 9000원
부가세 제외 단가: 4090.91원
영업 준비 끝

컴파일하면 main.o 라는 중간 파일도 생긴다. 실행에는 필요 없으니 지워도 된다.

실무에서 자주 틀리는 것

마지막 end 뒤에 세미콜론을 붙인다

본체 안의 문장은 세미콜론으로 끝나므로 습관적으로 맨 끝에도 붙이기 쉽다.

begin
  WriteLn('영업 시작');
end;

프로그램의 마지막 end 는 마침표여야 한다. 세미콜론을 쓰면 컴파일러가 파일 끝까지 소스를 읽고도 끝 표시를 찾지 못해 오류를 낸다.

begin
  WriteLn('영업 시작');
end.

글자를 큰따옴표로 감싼다

다른 언어에서 큰따옴표로 문자열을 쓰던 습관이 남아 있으면 이렇게 쓴다.

WriteLn("카페라테");

Pascal 의 글자는 작은따옴표로 감싼다. 글자 안에 작은따옴표 자체를 넣으려면 두 번 겹쳐 쓴다.

WriteLn('카페라테');
WriteLn('사장님''s 추천');

Format 의 값을 대괄호 없이 넘긴다

WriteLn(Format('라테 %d원', 5000));

Format 의 두 번째 인자는 값의 목록이어야 하므로 값이 하나여도 대괄호로 감싼다. 대괄호가 없으면 형식이 맞지 않는다는 컴파일 오류가 난다.

WriteLn(Format('라테 %d원', [5000]));

지시자 안에 공백을 넣는다

{ $mode objfpc}{ $H+}

여는 중괄호와 달러 기호 사이에 공백이 있으면 지시자가 아니라 평범한 주석으로 처리되고, 컴파일러는 불평 없이 기본 모드로 번역한다. 그러다 뒤의 장에서 클래스 문법을 쓸 때 엉뚱한 오류가 나서 원인을 찾기 어려워진다. 공백 없이 붙여 쓴다.

{$mode objfpc}{$H+}

한눈에 보기

프로그램 뼈대와 출력 도구 요약
요소모양역할과 주의
컴파일 지시자{$mode objfpc}{$H+}파일 첫머리. 공백 없이 쓴다.
프로그램 이름program Main;세미콜론으로 끝낸다.
유닛 가져오기uses SysUtils;Format 을 쓰려면 필요하다.
본체begin ... end.마지막 end 뒤는 마침표다.
줄 바꿔 출력WriteLn(...)글자는 작은따옴표로 감싼다.
서식 출력Format('%d', [값])값은 대괄호 안에 넣는다. %.2f 는 소수 두 자리다.
컴파일과 실행fpc main.pas, ./mainfpc -iV 로 설치를 먼저 확인한다.

연습 문제

  1. main.pas 를 고쳐서 카페 메뉴 세 가지(이름과 가격)를 WriteLn 으로 출력해 보라. 가격은 마음대로 정해도 된다.
  2. 카페라테 한 잔이 5000원일 때 세 잔의 합계를 Format 과 %d 로 출력하는 문장을 써라. 합계는 계산식으로 넣는다.
  3. 다음 프로그램에는 오류가 두 군데 있다. 찾아서 고쳐라.
    {$mode objfpc}{$H+}
    program Main;
    begin
      WriteLn("영업 시작");
    end;
  4. 5000원에 부가세 10%가 포함되어 있다고 할 때, 부가세를 뺀 금액을 소수점 아래 한 자리로 출력하는 문장을 써라. 이 문장은 uses 줄이 필요한가.

정답과 해설

1번. 예시는 다음과 같다. 본체의 begin 과 end. 사이에 쓰면 된다.

WriteLn('아메리카노 ', 4500, '원');
WriteLn('카페라테 ', 5000, '원');
WriteLn('말차라테 ', 5500, '원');

글자는 작은따옴표 안에, 숫자는 따옴표 없이 쓰고 쉼표로 이어 붙인다. 가격 앞의 공백은 글자 안에 넣어야 한다.

2번.

WriteLn(Format('카페라테 3잔: %d원', [5000 * 3]));

출력은 카페라테 3잔: 15000원 이다. 값은 대괄호 안에 두고 계산식을 그대로 써도 된다.

3번. 첫째, 글자를 큰따옴표로 감쌌다. 작은따옴표로 고쳐야 한다. 둘째, 마지막 end 뒤에 세미콜론이 있다. 마침표로 고쳐야 한다.

{$mode objfpc}{$H+}
program Main;
begin
  WriteLn('영업 시작');
end.

4번.

WriteLn(Format('공급가액: %.1f원', [5000 / 1.1]));

5000 을 1.1 로 나누면 4545.4545... 이므로 소수점 아래 한 자리로 고정하면 4545.5 가 출력된다. Format 은 SysUtils 에 들어 있으므로 uses SysUtils; 줄이 필요하다. WriteLn 만 쓰는 프로그램에서는 uses 가 없어도 된다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.