파스칼 프로그래밍/기본서
구조
[+/-]구조도
[+/-]간단한 구조.
program 프로그램이름; // 이 소스코드가 프로그램이라는것을 의미한다.
label // Goto문의 지표가 된다.
지표1, 지표2;
uses // 외부유닛의 참조목록이다.
유닛1, 유닛2;
type // 자료형 선언부이다.
형명칭: 자료형;
var // 변수 선언부이다.
변수명: 자료형;
const // 상수 선언부이다.
상수명=상수값;
procedure 프로시저이름; // 프로시저를 만들었다.
begin // 시작
명령;
end; // 끝 - 프로시저
begin // 시작
프로시저이름;
명령; // end의 앞에는 원칙적으로는 ;이 오면 안된다. 이 문제는 나중에 다룬다.
end. // 끝 - 프로그램
예제
[+/-]일반적인 HelloWorld이다.
program Helloworld;
begin
WriteLn('HelloWorld');
end.
단순히 문자열 HelloWorld를 출력하고 끝난다. (begin과 end 사이에 WriteLn만 있는걸 발견할 수 있다.)
설명
[+/-]- Program
- 사용: Program
(input, output)[프로그램이름] 프로그램이 이곳에서 시작한다는 것을해당 파일이 프로그램이라는 것을 의미한다.Input은 입력파일을, Output은 출력파일을 지정한다. 키보드와 모니터로 기본설정이 되어 있다.Program 뒤의 이름은프로그램의 이름을 지정하는 기능을 했다.보통 파일 이름으로 자동설정되므로 신경쓸 필요가 없다.
- begin
- 프로그램, 유닛, 서브루틴의 시작을 알린다.
- 블록의 시작이라는 뜻이다. 이때 begin과 end로 묶인 내부는, 수학연산에서 내용이 마치 괄호로 묶인것과 같은 효과를 냅니다. 2 * 2 + 2 에서 2 + 2 를 먼저 계산하기 위해 2 * ( 2 + 2 ) 처럼 ( ) 를 도입하는 기법과 같다고 생각하면 된다.
- 물론 프로그램, 유닛, 서브루틴의 내용이 한 블럭을 포함한다고 생각할 수도 있다. 단 이 경우 블럭을 생략할 수는 없습ㄴ다.
- end
- 프로그램, 유닛, 서브루틴의 끝을 알린다.
- 블록의 끝이라는 뜻이다. begin 참조.
- ;
- 문장의 끝을 알린다.
- 파스칼 소스코드에서 두칸 이상의 공백으로 보이는 문자들(공백, 아스키 9, 아스키 10)은 아무런 의미도 없다.
- ;을 써야만 그 문장이 끝나게 된다.
- 처음 파스칼을 시작하는 사람이 쉽게 저지르는 실수가 ;를 빼먹는 것이다.
모든 문장은 ;으로 끝나게 된다. 이때 end는 블록 단위이기 때문에 문장이 아니고, end;와 같은 문장은 성립할 수 없다. 따라서 블럭의 마지막 문장에 ;을 쓰지 않는 것은 end까지가 그 문장을 나타내기 때문이다.
- 주석
- 내용에 영향을 끼치지는 않지만 사람이 메모해둘 내용이 있을 때 사용한다.
- { } (* *) : 괄호 가운데의 내용은 컴파일할 때 없는 것으로 간주한다. 각각 시작한것과 쌍을 이루어 끝난다. 단 { }는 컴파일러 지시자와 충돌을 일으킬 수 있으므로 경우에따라 잘 사용해야 한다.
- // : 오른쪽의 내용은 컴파일할 때 없는 것으로 간주한다. 위의 구조란에서 썼다.
구성
[+/-]- 구성 항의 내용물은 본격적으로 파스칼에 대해 서술하기 위한 준비작업이다.
- 구성부분은 건너뛰고 그 다음의 설명을 먼저 읽으며 필요한 부분을 구성 부분에서 참조하는 것이 쉽고 편하다.
식별자
[+/-]식별자는 어떤 대상을 구별할 수 있는 이름이다. 사용자가 정의해 쓰는 모든 변수, 서브루틴, 상수, 자료형 등의 이름을 포함하며 영문자, 숫자, 밑줄만 사용해야 한다.
첫문자는 반드시 알파벳으로 시작하고 63자를 초과할 수 없다.
단 유닛 이름 파일 이름이기 때문에 운영체제에 따라 대소문자를 구분할 수도 있다.
예약어는 식별자로 쓸수 없으며 지시어는 권장되는 기법은 아니지만 쓰일 수는 있다.
이에 대한 자세한 내용은 파스칼 프로그래밍/문법#식별자를 참고하라.
한편 파스칼이 기본적으로 정의하고 있는 자료형, 기본함수, 기본프로시저, 기본상수 등의 명칭이 있다.
기본함수와 기본프로시저에 대해서 자세한 내용은 파스칼 프로그래밍/함수에서 확인하라.
연산자
[+/-]대입연산
[+/-]- := : 대입연산자이다. 왼쪽의 변수에 오른쪽의 값을 대입한다.
var
a, b: Integer;
begin
a := 40; // a = 40
b := a; // b = 40 : 40이 저장된 a의 값을 b에 대입했으므로 b의 값은 40이다.
end
대수연산
[+/-]대수연산의 연산자는 일반적 수학표현과 마찬가지로, 괄호 우선, 곱셈나눗셈 우선이다.
- + : 덧셈
var
intA, intB: Integer;
strA, strB: String;
begin
intA := 1;
intB := intA + 4; // intB = 5
strA := 'Hello';
strB := StrA + 'World'; // strB = 'HelloWorld'
end
- - : 뺄셈. 표현식 앞에 붙어 음수를 표현하기도 한다.
var
a, b: Integer;
begin
a := 100;
b := -a; // b = -100
end
- * : 곱셈
- / : 실수 나눗셈
- div : 정수 나눗셈의 몫
- mod : 정수 나눗셈의 나머지
var
int: Integer;
float: real;
begin
int := 26 div 7; // int = 3 : 몫을 구했다.
int := 26 mod 7; // int = 5 : 나머지를 구했다.
float := 10 / 4; // float = 2.5 : 실수범위에서 나누었다.
{int := 10 / 2;} // 나누면 5로 떨어지므로 int에 대입할 수 있을듯 하지만 /연산자는 실수값을 반환하므로 대입할 수 없다.
int := trunc( 26 / 7 ); // int = 3 : 함수를 이용해 소수점 이하를 버렸다. 따라서 몫을 구한 것과 같다.
end
논리연산
[+/-]논리연산의 결과는 항상 Boolean형이다. 논리연산자의 양쪽에는 Boolean형이 놓여야만 한다. Boolean의 값은 True는 양수, False는 0이라고 생각하면 쉽다.
- not : 부정연산을 하는 단항 연산자이다. 뒤의 값의 역을 반환한다.
- and : 논리곱이다. 양쪽이 모두 참일때만 True값을 반환한다.
- or : 논리합이다. 양쪽이 모두 거짓일때만 False값을 반환한다.
- xor : 배타논리연산이다. 양쪽이 다를때만 True값을 반환한다.
비교연산
[+/-]크기 비교는 서수형 자료형에서만 가능한다. 비교연산의 결과는 항상 Boolean형이다.
var
is_bigger, is_in: Boolean;
begin
is_bigger := 3 > 1; // is_bigger = True
is_bigger := 100 > 100; // is_bigger = False
is_in := 1 in [1, 3, 5, 10]; // is_in = True
end
- = : 같다.
- > : 왼쪽이 더 크다.
- < : 왼쪽이 더 작다.
- >= : 왼쪽이 크거나 같다.
- <= : 왼쪽이 작거나 같다.
- <> : 다르다.
집합 연산
[+/-]- = : 같다.
- > : 오른쪽이 왼쪽의 진부분집합이다.
- < : 왼쪽이 오른쪽의 진부분집합이다.
- >= : 오른쪽이 왼쪽의 부분집합니다.
- <= : 왼쪽이 오른쪽의 부분집합이다.
- <> : 다르다.
- in : 왼쪽의 값이 오른쪽 집합에 포함된다. 반드시 오른쪽에는 집합형이 놓여야 한다.
- - : 차집합을 구한다.
- + : 합집합을 구한다.
- * : 교집합을 구한다.
문자열 연산
[+/-]- + : 문자열을 이어붙일 때 + 를 이용할 수 있다.
비트연산
[+/-]var
Num: Integer;
begin
Num := 5; // 5, 00000101
Num := Num SHL 2; // 20, 00010100, 왼쪽으로 두비트 밀리고 오른쪽이 0으로 채워졌다.
Num := Num SHR 3; // 2, 00000010, 오른쪽으로 세비트 밀리고 왼쪽이 0으로 채워졌다. 마지막 1은 사라졌다.
end
- SHL : 왼쪽으로 밀어낸다.
- SHR : 오른쪽으로 밀어낸다.
예제의 사용을 참고하라.
자료형
[+/-]자료형 정의
[+/-]아래에서는 자료형을 임의로 정의해서 쓰는 경우가 많다.
type
NewInteger = Integer;
위와 같이 정의하면 NewInteger라는 자료형이 새로 생기게 된다.
단순형
[+/-]서수형
[+/-]- 정수형
- 부호있는정수: Integer, 일반적인 정수이다.
- 부호없는정수: Cardinal, 0 이상의 정수만 표현한다.
var
Int1, Int2: Integer;
Card1, Card2: Cardinal;
begin
Int1 := 10;
Card1 := 5;
Int2 := Int1 + Card1; // Integer 형으로 바뀌어 대입된다.
Card := Int1 + Card2; // Cardinal 형으로 바뀌어 대입된다.
Int1 := -3;
Card1 := -10; // Cardinal은 음수를 가질 수 없으므로 컴파일 오류 발생.
Card1 := 3;
Int2 := Int1 + Card1; // 0 저장
Card2 := Int1 + Card1; // 어쨌든 결과값이 0이므로 0이 저장된다.
Int1 := -4;
Card2 := Int1 + Card1; // 범위를 벗어나서 실행 오류 발생.
end
// 범위 불일치는 오류가 일어날 여지가 있으므로 이 예제의 모든 문장에서 경고가 발생한다.
- 논리형 : Boolean, 참/거짓 자료형이다. 참과 거짓은 True와 False라는 상수로 정의되어 있다. 1바이트이다. True나 False값만 가질 수 있다.
- 문자형 : Char, 문자 한개를 가지는 1바이트 자료형이다.
- 열거형 : 어떤 모임에 순서를 부여하기 위해 쓴다.
type
Wiki = (wikipedia, wikibooks, wiktionary, wiknews);
var
Work: Wiki;
begin
Work := wikibooks;
if Work in [wikipedia, wikibooks, wiktionary] then // 열거형은 주로 집합형(Set)과 함께 쓰인다.
writeLn('이 작업은 한국어 위키미디어 프로젝트가 존재한다.');
end
- 범위형 : 범위형은 독립적인 자료형이라기 보다는 다른 자료형의 범위를 필요한 만큼으로 제한하는 것이다. 제한형이라고도 부른다.
type
Alphabet = 'A'..'z';
NumberKey = 0..9;
var
Chr: Alphaber; // 영문자의 대소문자만 가질 수 있다.
IntKey: NumberKey; // 0부터 9까지의 한자리 숫자만 가질 수 있다.
begin
IntKey := 5; // 일반적
IntKey := 10; // 오류, 범위초과
end
실수형
[+/-]Real로 선언한다.
실수 값을 가지며 유효자리와 자릿수 정보를 저장한다. 서수형과 호환성이 없으며, 정수형과 섞어 쓰면 자동으로 실수형으로 바뀌어 처리된다.
자료형끼리 연산을 하면 연산 결과를 포함할 수 있는 자료형으로 자동으로 바뀌게 된다.
var
Int1, Int2: Integer;
Float: Real;
begin
Int1 := 5;
Float := Int1; // 정수에서 실수로 대입은 가능하다.
Int1 := Float; // 오류 발생! 같은경우처럼 보일지 모르나 실수형 자료는 정수에 대입할 수 없다. Float이 가진 값은 15000이지만 실수 형태로 저장되어 있다.
Int2 := Int1 + Float // 위와 같은 이유로 안된다.
Int2 := Int1 / Int1; // 오류 발생! / 연산자는 무조건 실수값을 반환하므로 오류가 발생한다.
Int2 := Int1 div Float; //오류 발생! div 연산자는 정수의 연산만 가능하므로 오류가 발생한다.
Float := Int1 / Int1; // 바른 연산이다.
end
문자열형
[+/-]문자열형은 ' 따옴표 두개로 둘러싸 나타낸다.
그리고 문자의 배열형으로 생각할 수 있다.
var
str: String;
chr: Char;
begin
str := 'Hello World';
chr := str[1]; //chr = 'H'
end
문자열과 문자 간에도 자동 변환이 일어난다.
var
Chr1, Chr2: Char;
Str: String;
begin
Str := Chr1 + Chr2; // 문자를 합치면 무조건 문자열이 된다.
Str := '';
Chr1 := 'a';
Chr2 := Str + Chr1; // 오류가 발생한다. 문자열과 문자의 합은 문자열이다. 실제 값(''+'a'='a')과는 관계없다.
//문자열에서 +는 concat 함수와 같은 역할일 뿐이기 때문에 위와 같은 현상이 일어난다.
end
구조형
[+/-]배열형
[+/-]type
이름 = Array[시작값..끝값] of 자료형;
크기가 정해진 자료형만 배열형으로 만들 수 있다. 따라서 문자열이나 파일, 집합 등은 만들수 없다.
type
IntArray = Array[1..10] of Integer;
var
intMass: IntArray; // intMass[1], intMass[2], ... intMass[10] 까지의 변수를 모두 선언한 것과 같다.
count: Integer;
begin
For count := 1 to 10 do // 반복문을 이용해 배열 전체에 접근한다.
IntMass[count] := 0;
end
다차원 배열은 배열의 배열과 같다.
배열의 크기를 미리 지정해 두지 않고 필요한 만큼 정의해 쓰는 동적 배열을 만들 수도 있다.
다차원배열과 동적 배열에 관한 자세한 것은 파스칼 프로그래밍/문법#배열형을 참고하라.
구조체
[+/-]- Record : 여러개의 자료형을 묶는 틀과 같은 자료형이다.
type
이름 = Record
필드1 : 자료형;
필드2 : 자료형;
필드3 : 자료형;
end;
type
Subject = Record
Pascal, PHP, Assembly : Integer;
end;
Student = Record
Score: Subject;
Name: Array[0..8] of Char;
Grade: Char;
end;
var
Student1, Student2: Student;
begin
Student1.Name := 'student';
with Student1.Score do
begin
readln(Pascal, PHP, Assembly);
if Pascal + PHP + Assembly > 200 then
Student1.Grade := 'A'
else
Student1.Grade := 'F';
end;
Student2.Score := Student1.Score; // Student2의 Score를 한꺼번에 대입했다.
end
집합형
[+/-]Set 으로 다루는 집합형이다. 서수형 자료형을 원소로 가질 수 있다. 수학의 집합을 생각하면 쉽다.
type
이름 = Set of 자료형; //단 자료형의 선택 가능한 수에 제약이 있다.
집합형은 연산의 독특한 성질을 이해해야 쓰기 쉽다.
type
wiki = (wikipedia, wikibooks, wiktionary, wikinews);
wikis = Set of wiki;
var
wikis1, wikis2: wikis;
Check: Boolean;
begin
wikis1 := [wikibooks, wikipedia, wiktionary];
wikis2 := [wikipedia..wikitionary]; //서수형을 다루므로 구간 내의 원소를 한번에 넣을 수 있다.
Check := wikis1=wikis2; // Check = True, 순서에 관계없이 내부 값만 확인한다.
wikis1 := wikis1 + [wiktionary, wikinews]; // wikis1 = [wikipedia..wikinews] 이다. 합집합 연산.
wikis2 := wikis1 - wikis2; // wikis2 = [wikinews] 이다. 차집합 연산.
Check := wikis2 in wikis1 // 오류! in 연산자는 원소를 위한 것이다.
Check := wikibooks in wikis1 // Check = True
Check := wikibooks in wikis2 // Check = False
Check := wikis2 <= wikis1; // Check = True, 부분집합을 크기 비교 연산으로 구할 수 있다.
wikis2 := wikis1*wikis2; // wikis2=[wikinews], 교집합을 구하는 연산이다.
end
포인터
[+/-]파일
[+/-]- File : File of (자료형) 꼴로 선언해 쓴다. File형을 다루는 고유의 함수가 많다.
- Textfile : 텍스트 파일을 제어할 때는 File of Char를 쓰지 않고 Textfile자료형을 쓰는 경우가 많다. Textfile 자료형에서는 WriteLn, ReadLn을 쓸 수 있으며 표준입출력장치를 쓰는것과 같은 효과를 낸다. 자료형을 쓰는 경우가 많다. Textfile 자료형에서는 WriteLn, ReadLn을 쓸 수 있으며 표준입출력장치를 쓰는것과 같은 효과를 낸다.
문법
[+/-]조건문
[+/-]if
[+/-]if문은 Boolean형 자료의 참거짓 값에 따라 프로그램 진행을 분기하고자 할때 쓴다.
if Bool then
명령
else
명령;
Bool이 참값이라면 then 뒤의 명령을, 거짓이라면 else뒤의 명령을 실행할 것이다.
일반적으로는 한 명령 뒤에는 반드시 ; 을 써야 하지만 if문의 then 뒤의 명령에는 ; 를 쓰지 않는다. 왜냐하면 if문 전체가 하나의 문장이기 때문에, if문에는 if문의 종결을 알리는 ; 만 필요하기 때문이다. 따라서 if문에서 ; 은 if문의 맨 끝에 적는다. 물론 else가 존재하지 않는 if문의 경우 then 뒤의 명령 뒤에도 ; 이 붙는다.
여러 명령을 실행하고 싶으시다면, 위에서 설명한 begin과 end를 이용한다.
if Bool then
begin
명령1;
명령2;
명령3;
end
else
begin
명령1;
명령2;
명령3;
end;
역시 then의 begin ~ end 뒤에는 ; 을 쓰지 않았다.
case
[+/-]case 서수형 of
경우1 :
명령1;
경우2 :
명령2;
경우3 :
명령3;
else
명령;
end;
case문은 서수형 자료형의 값에 따라 여러 분기문을 만들때 쓴다. 서수형의 자료값이 각 경우에 해당되면 그 경우의 명령을 실행한다. 만일 조건 중 아무것도 충족하지 못한다면 else의 문장을 실행할 것이다.
반복문
[+/-]for
[+/-]var
Index: Integer;
begin
for Index := 1 to 100 do
WriteLn(Index);
end
위의 for문은 Index에 1부터 100까지 순서대로 대입하고 do 의 다음 문장을 실행하는 것이다. 결과로는 1부터 100까지의 수가 매 줄마다 출력될 것이다.
var
Index: Integer;
begin
for Index := 100 downto 1 do
WriteLn(Index);
end
첫 예제의 거꾸로이다. 100부터 1까지 순서대로 대입했다.
일부 언어와 달리 파스칼에는 1 이외의 차로 for문을 설정할 수는 없다. 하지만 만일 for문을 이용해 2의 차를 얻고싶다면 간단히 실행줄에서
Index*2
를 쓰는것으로 충분한다.
while
[+/-]while문은 조건이 맞으면 반복하는 반복문이다.
while bool do
명령;
bool이 True라면 명령을 실행하고 다시 bool을 검사한다. bool이 False라면 while문 밖으로 빠져나간다.
var
a: Integer;
begin
a := 1;
while a < 100 do
a := a + 1;
end // a = 100
이 예제에서는 a가 100보다 작은지 검사하고, 작다면 a의 숫자를 1씩 늘리고 있다. 반복할수록 a가 점점 커지기 때문에, 조건을 만족하는순간 while문을 빠져나오게 된다.
repeat ~ until
[+/-]repeat ~ until문은 조건이 맞을때까지 반복하는 반복문이다.
repeat
명령1;
명령2;
until bool;
repeat문은 일단 명령1, 명령2를 실행하게 된다. 그리고 until의 bool을 검사해 True면 순환을 빠져나가고, False면 repeat부터 새로 반복하게 된다. while과는 다음과 같은 차이가 있다.
- while문은 조건을 비교하고 반복하지만, repeat~until문은 실행하고 조건을 비교한다.
- while문은 조건이 맞으면 반복하지만, repeat~until문은 조건이 맞으면 빠져나온다.
- while문은 한문장을 실행하지만, repeat~until문은 여러문장을 실행한다.
하지만 세번째 이유 때문에 while 대신 repeat~until을 쓸 필요는 없다. begin과 end로 내용을 묶어주면 된다.
반복문의 프로시저
[+/-]var
Index : Integer;
begin
for count := 1 to 20 do
begin
if Index mod 2 = 0 then
WriteLn(count)
else
Continue;
if Index = 9 then
Break;
if Index = 12 then
Break;
end;
end
- Continue는 반복문의 맨 끝으로 이동하게 한다. 반복문의 끝에 도달했으므로 다시 처음값으로 돌아가 반복문을 계속 실행하게 된다.
- Break는 반복문을 빠져나가게 한다.
위의 실행 결과는 아래와 같다.
2 4 6 8 10 12
Index mod 2 가 0일때만 WriteLn을 썼으므로 짝수만 출력된다는것은 쉽게 파악될 것이다. Index = 9 일 때 Break를 실행하도록 했지만, Index = 9 일때는 이미 앞의 조건문 때문에 continue가 되어 뒤의 문장이 실행되지 않았다. 따라서 Index에는 무사히 10이 대입되었다. Index = 12 일 때는 break가 실행되므로 for문은 아직 끝나지 않았지만 for문을 빠져나오게 된다. while과 repeat문에서도 break와 continue의 용법은 마찬가지이다.
With
[+/-]유닛, 구조체, 클래스 등 하부구조를 가지는 자료를 다룰 때 불필요한 반복을 막기 위해 사용한다.
type
RecSample = Record
Chr1, Chr2: Char;
Int1, Int2: Integer;
end;
TSample = class
Chr1, Chr2: Char;
Int1, Int2: Integer;
end;
var
varRecord: RecSample;
varClass: TSample;
begin
varRecord.Chr1 := 'a';
varRecord.Chr2 := 'b';
with varRecord do begin
Int1 := 1;
Int2 := 2;
end;
varClass := TSample.Create; // 이 부분은 클래스에서 설명한다.
varClass.Chr1 := 'c';
varClass.Int1 := 3;
with varClass do begin
Chr2 := varRecord.Chr2;
Int2 := varRecord.Int2;
end;
varClass.Free; // 이 부분은 클래스에서 설명한다.
end
결과는 다음과 같다.
varRecord Chr1 = 'a' Chr2 = 'b' Int1 = 1 Int2 = 2 varClass Chr1 = 'c' Chr2 = 'b' Int1 = 3 Int2 = 2
With문이 어떤 역할을 하는지 예제를 참고하라.
부프로그램
[+/-]부프로그램은 서브루틴이라고도 한다. 다른 언어에서는 함수(function)로 통합해서 다루는 경우가 있으며, 실제로 개념도 다르지 않다. 인자가 없을때는 호출할때 인자 넣는 공간의 기호인 ( ) 를 생략해도 된다. WriteLn 프로시저가 인자가 있을때와 없을 때 어떻게 쓰였는지 생각해보시면 쉬울 것이다.
함수
[+/-]함수는 함수명에 지정된 부프로그램을 실행한 후 자신의 이름에 연산의 결과를 반환한다.
function 이름(인자: 인자자료형): 자료형;
begin
연산;
연산;
Result := 함수값;
end;
Program Callfunction;
function PlusMinus(a, b: Integer; Operator: Char): Integer;
function Infunction: Integer;
begin
// 서브루틴 안에도 서브루틴을 만들 수 있다.
Result := 함수값;
end // of Infunction
begin
Result := 0; // 기본값을 0으로 지정한다.
case Operator of
'+' :
Result := a + b;
'-' :
Result := a - b;
end; // of case 연산자가 '+'나 '-'가 아니면 기본값으로 남는다.
end; // of PlusMinus
begin
PlusMinus(1, 2, '+'); // 함수는 실행되었고 실제로 하드웨어에서 연산도 일어났다. 하지만 결과를 이용하지 않아 아무런 결과도 나타나지 않다.
WriteLn(PlusMinus(1, 4, '+'));
end.
위와 같이 함수는 변수처럼 자료형만 맞추어 쓰면 어디든지 쓸 수 있다.
프로시저
[+/-]프로시저는 자료형이 지정되지 않고, 반환값을 지정하지 않는 함수와 같다. 단 반환하는 자료형이 없는 함수를 만들 수는 없다.
procedure 이름(인자: 인자자료형);
begin
연산;
연산;
end
프로시저는 함수사용법의 첫번째처럼 그냥 써주면 된다.
범위규칙
[+/-]변수의 지역성을 규정하는 법칙이 있다.
Program 이름;
var
A: 자료형; // 전역변수를 선언한다.
procedure 이름;
var
A, B: 자료형; // 지역변수를 선언한다.
begin
명령; // A를 참조할 경우 프로시저 내의 A를 가리키게 된다. 이름이 중복될 경우 좁은지역의 변수가 우선한다.
end;
begin
명령; // B를 참조할 수 없다. B는 프로시저 내에서만 통용된다.
end.
참조방법
[+/-]함수에 인자를 넘겨줄 때, 넘겨주는 방식에 따라 약간씩 특징이 바뀌게 된다.
값 참조
[+/-]값 참조 방식은 일반적 방식이다. 아무련 표식도 하지 않았을 때 함수는 인자를 자동으로 값참조 방식으로 넘긴다. 서브루틴 내에 인자 이름에 해당하는 지역변수가 있는것과 같다.
- 인자 이름에 값을 대입할 수 있다.
- 인자에 대입한 값은 함수를 빠져나가는 순간 사라진다.
주소 참조
[+/-]주소 참조 방식은 변수의 인자의 포인터를 인자이름의 포인터에 대입시키는 것과 같다.
function 이름(var 인자: 자료형): 자료형;
begin
Result := 반환값;
end;
아래와 같은 차이가 있다.
- 인자 이름에 값을 대입할 수 있다.
- 인자에 대입한 값은 실제의 인자로 넘겨진다.
- 서브루틴 실행 결과가 인자를 변형시켜야 할 때 쓰인다.
- 포인터를 대입시킨것과 같다는 말이 잘 이해가 되지 않는다면 자료형에서 포인터의 설명을 읽어보라.
상수 참조
[+/-]상수 참조 방식은 변수의 값만 인자로 넘겨주는 것과 같다.
function 이름(const 인자: 자료형): 자료형;
begin
Result := 반환값;
end;
인자 이름은 하나의 상수처럼 취급된다.
- 인자에 어떤 값을 대입할 수 없다.
- 서브루틴이 인자의 값만 필요할 때 쓸 수 있다.
- 실행속도가 가장 빠릅니다.
순환호출
[+/-]간혹 재귀호출이 필요할때와 마찬가지로, 두개 이상의 부프로그램을 순환호출해야 할 때도 있다.
procedure Proc1;
begin
Proc2; // 정의되지 않은 식별자 오류
end;
procedure Proc2;
begin
Proc1;
end;
위와 같이 만들게 되면 Proc1에서는 Proc2를 찾을 수 없게 되어 컴파일 할 수 없다.
반대로 만들면 반대의 상황이 발생하므로 역시 불가능한다.
procedure Proc2; forward;
procedure Proc1;
begin
Proc2;
end;
procedure Proc2;
begin
Proc1;
end;
위와 같이 만들면 Proc1에서도 Proc2를 참조할 수 있고, 실제로 Proc2를 호출했을때는 위의 Proc2를 부르게 되지만, 위의 Proc2는 실 선언부인 아래로 이동하게 되므로 문제가 발생하지 않다.
단 위의 예제는 무한순환구조에 빠지게 되므로 그대로 사용하면 안된다.
미리 선언할때는 forward를 붙여둔다.
제어
[+/-]- exit : 서브루틴에서 빠져나갈 때 쓴다. exit를 실행하면 한번에 서브루틴의 end로 이동하게 된다.
컴파일러 지시자
[+/-]유닛
[+/-]파스칼의 유닛은 일부 언어와 달리 소스코드를 보호할 수 있다. 유닛을 컴파일하면 interface 부분은 접근 가능한 텍스트로 남고, implementation 부분은 컴파일된다. 컴파일러는 버전마다 독특한 컴파일된 유닛을 생성하기 때문에 유닛을 컴파일하는 컴파일러와 유닛을 참조하는 컴파일러는 같은 버전이어야 한다.
- interface : 유닛의 외부에서 접근할 수 있는 파일의 머리부분이다.
- implementation : interface에서 선언한 유닛의 내용을 서술한다.
unit Unitname;
interface
var
int1: Integer; // 이 유닛을 참조하면 interface에 선언한 내용은 접근할 수 있다.
procedure Proc1; // interface에서는 이름만 선언해 준다.
implementation
var
int2: Integer; // implementation에 선언한 내용은 유닛 내에서만 접근할 수 있다. 이 기능으로 소스코드를 보호할 수 있다.
procedure Proc1; // interface에서 선언한 부분을 implementation에서 구현한다.
begin
end;
end.
선언부에서 이름만 선언한 것은 앞에서 다루었듯 뒤의 내용에 대해 미리 선언하는 것이다. 유닛은 컴파일하면 구현부에 접근할 수 없기 때문에 구현부와 연결되는 선언부를 만들어 두는 것이다.
흑체 원리
[+/-]흑체 원리는 유닛과 클래스에 공통으로 적용되는 원리이다. 강제하는 원리는 아니지만 외부에서 접근을 보다 명확하게 유지하고 내부 자료를 보호하기 위해 권장하는 기법이다.
- interface에는 상수는 선언하지만 변수를 선언하는 않는다. 변수에 접근할때는 필요한 기능에 적합한 이름을 붙인 프로시저나 함수로 접근한다. (클래스에서 property를 관리하는 기법이 사용자 입장에서 가장 단순하게 구현하는 방법이다.)
클래스
[+/-]클래스는 type에 선언한다.
type
TClassName = class
int1: Integer; // 일반적 변수 선언
private
int2: Integer;
protected
int3: Integer;
public
int4: Integer;
constructor create;
destructor destroy;
end;
type
TClassName = class
private
procedure Proc1; virtual; // private이므로 virtual로 선언할 필요가 없다.
protected
procedure Proc2; virtual;
public
procedure Proc3; virtual;
end;
TClassName2 = class(TClassName)
public
procedure Proc1: override; // private로 선언한것은 override할 수 없다.
procedure Proc2: override;
procedure Proc3: override;
end;
var
ClassExam: TClassName;
procedure Proc4;
begin
ClassExam := TClassName.Create;
Proc1; // Private로 선언한 것을 호출할 수 없다.
Proc2; // Protected로 선언한 것을 호출할 수 없다.
Proc3; // Public으로 선언했으므로 호출할 수 있다.
ClassExam.Destroy; // 일반적으로 TObject를 제거할때는 Free를 호출한다.
end;
클래스는 항상 Constructor로 만들고 Destructor로 제거한다. Destructor로 제거해주지 않으면 클래스의 찌꺼기가 램에 남아 메모리 누수를 일으킵니다.
위키백과에 이 문서와 관련된 정보가 있습니다. 메모리 누수 |
첫번째 경우에서
var
ClassExam: TclassName;
을 선언했다면,
ClassExam.Create;
라고 쓸 수는 없다. ClassExam이 생성자인 Create를 갖고 있기는 하지만, 변수로 선언한 ClassExam에는 단순히 TclassName 속성의 포인터라는 표시를 해 둔 것이기 때문이다. 변수를 선언한 시점에서 ClassExam은 실체가 없는 빈 포인터일 뿐이다. 따라서 저 포인터에 어떤 TClassName의 값을 대응시켜주어야 사용할 수 있고, 그것이 TClassName.Create로 새 클래스를 생성해 대입해 주는 과정이다.
가시성
[+/-]상속성
[+/-]- 클래스는 서로 상속할 수 있다.
type
TClassParent = Class
public
int1: Integer;
procedure Proc1; virtual;
end;
TClassName = Class(TClassParent)
public
procedure Proc2: override;
end;
procedure TclassParent.Proc1;
begin
end;
procedure TclassName.Proc1;
begin
inherited; // inherited 자리에서 이 클래스가 상속한 TClassParent.Proc1이 실행된다.
end;
클래스를 선언할 때 ( ) 안에 클래스의 이름을 적으면 그 클래스의 모든 속성을 상속하게 된다. 위의 예에서 TClassName에는 int1을 선언하지 않았지만 int1을 쓸 수 있다.
같은 이름의 서브루틴을 내용을 상속하며 새로 쓸 때는 override를 이용한다. TClassName에서 TclassParent의 Proc1을 상속했다. inherited로 상속한 함수를 원하는 시점에 실행해주면 이를 이용해 추가처리를 할 수 있다.