Object Pascal · 기본
Object Pascal 첫걸음
Pascal 시작하기 - 프로그램의 뼈대
Pascal·Object Pascal·Delphi·Free Pascal 의 관계, fpc 설치 확인, program·uses·begin·end. 구조, 컴파일 지시자 {$mode objfpc}{$H+}, WriteLn
개발자KR · 원고 갱신
이 장에서 배우는 것
이 책은 작은 동네 카페의 계산대 프로그램을 장마다 조금씩 키워 가며 Object Pascal 을 배운다. 첫 장에서는 프로그램을 키우기 전에, 가장 작은 프로그램 하나가 어떤 뼈대로 이루어지는지부터 확인한다. 컴파일러가 설치되어 있는지 확인하고, 파일 하나를 컴파일해서 실행하고, 화면에 글자를 찍는 데까지 간다. 프로그래밍 경험이 없어도 따라올 수 있도록 용어는 나올 때마다 풀어 쓴다.
- Pascal, Object Pascal, Delphi, Free Pascal 이 서로 어떤 관계인지 설명할 수 있다.
- 터미널에서
fpc가 설치되어 있는지 확인하고main.pas를 컴파일해 실행할 수 있다. program,uses,begin,end.로 이루어진 프로그램의 뼈대를 읽고 쓸 수 있다.- 모든 파일 첫머리에 놓는
{$mode objfpc}{$H+}가 무엇을 정하는지 말할 수 있다. WriteLn과Format으로 글자와 숫자를 출력할 수 있다.
문제 상황
동네 카페 사장님이 계산대 프로그램을 만들어 달라고 했다고 하자. 지금은 메뉴 가격을 종이에 적어 두고 계산기를 두드린다. 처음부터 주문, 재고, 정산을 모두 만들 수는 없다. 우선 해야 할 일은 훨씬 작다. 가게 이름과 메뉴 가격이 화면에 나오는 프로그램을 만들어 보는 것이다.
그런데 막상 시작하면 질문이 연달아 나온다. Pascal 이라는 이름과 Delphi 라는 이름을 둘 다 들었는데 같은 것인가. 인터넷에서 찾은 예제는 첫 줄이 서로 다른데 어느 쪽을 따라야 하는가. 컴파일러는 무엇이고 어디서 받는가. 이 장은 이 질문들에 답하고, 이후 모든 장의 출발점이 될 파일 하나를 만든다.
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
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, ./main | fpc -iV 로 설치를 먼저 확인한다. |
연습 문제
main.pas를 고쳐서 카페 메뉴 세 가지(이름과 가격)를WriteLn으로 출력해 보라. 가격은 마음대로 정해도 된다.- 카페라테 한 잔이 5000원일 때 세 잔의 합계를
Format과%d로 출력하는 문장을 써라. 합계는 계산식으로 넣는다. - 다음 프로그램에는 오류가 두 군데 있다. 찾아서 고쳐라.
{$mode objfpc}{$H+} program Main; begin WriteLn("영업 시작"); end; - 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 가 없어도 된다.
READER FEEDBACK
질문·의견
내용에 관한 질문이나 더 나은 설명을 위한 의견을 남겨 주세요. 오탈자는 위의 제보 양식이 더 빨리 반영됩니다. 이 댓글은 원래 게시글과 같은 자리에 쌓입니다.
댓글 0
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.