프로그래밍/파스칼/기본서
위키책, 위키책
차례 |
[+/-] 구조
[+/-] 구조도
간단한 구조.
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 Int1; // 오류 발생! 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..wikibooks]; //서수형을 다루므로 구간 내의 원소를 한번에 넣을 수 있다. 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로 상속한 함수를 원하는 시점에 실행해주면 이를 이용해 추가처리를 할 수 있다.