강좌 첫화면으로 최근 글 보기(Post)
갈래별로 보기 categories




  3.9. 함수에 관한 문법 정리



3.9. 함수에 관한 문법 정리


지금까지 여러분은 C++언어에서 사용하는 중요한 개념들을 배웠습니다. 자료형, 변수, 상수, 연산자, 제어문, 기억부류, 배열, 포인터까지 배웠습니다. 이들을 이용해서 함수를 만들어나가는 것이 C++언어를 공부하는 사람들이 할 일입니다. 이제 마지막으로 함수를 만들기 위해서는 어떠한 점을 주의해야 하는지 알아보겠습니다.
사실 그동안 많은 프로그램을 예제로 작성했고, 이를 통해서 이미 C++언어와 친숙해졌습니다. 또한 함수를 만드는 일이 그리 어렵지 않다는 것도 잘 알고 있습니다. 그러나 좀더 깊이 들어가면 함수를 만들기 위한 규칙이 매우 까다롭다는 것을 알게 될 겁니다. 함수와 관련한 내용을 정리해보는 것으로 C++언어와 C++컴파일러 프로그램의 문법정리를 마치도록 하겠습니다.

3.9.1. main() 함수에 대하여


C++언어 프로그램의 시작은 main() 함수로부터 시작합니다.

C++은 함수들의 모임입니다. 사실상 C++이라는 언어는 모두가 다 함수입니다. 그리고 C++에는 프로그램을 실행하기 위해서 반드시 하나의 특수한 함수가 필요합니다. 바로 main이라는 함수입니다. C++언어로 짠 모든 프로그램에는 반드시 main()이라는 이름을 가진 함수가 들어가야 합니다. 그리고 모든 프로그램의 시작은 main() 함수로부터 시작합니다.
주의할 점은 C++언어에서 사용하는 함수이름은 대소문자를 구별한다는 점을 기억해두어야 합니다. 즉 main, Main, mAin, MAIN... 등은 다 다른 이름입니다.
그리고 함수에는 부모와 자식관계가 형성됩니다. 즉 어떤 함수 내에서 또 다른 함수를 호출해서 사용할 경우 호출을 한 함수는 parent process가 되고 호출을 받은 함수는 child process가 되는 겁니다.

main() 함수의 parent process는 운영체제의 명령어입니다.

그렇다면 C++의 함수 중에서 가장 윗어른에 해당하는 함수는 main() 함수가 될 겁니다. 그럼 main() 함수 위의 parent process가 있을까요? 있습니다. 바로 운영체제의 명령어입니다. 즉 DOS나 윈도95/98입니다. 보통 도스 명령어가 main() 함수의 parent process가 됩니다. 도스에서 main() 함수를 부르는 셈이니까요.

함수에 전달해주는 값은 인수이고 함수가 돌려주는 계산결과는 복귀값이라고 합니다.

이때 어떤 함수는 나름대로 한 가지 일을 처리해줍니다. 그 일이 중요하거나 중요하지 않거나 상관 없이 최소한 한 가지 이상의 일을 처리해줍니다. 그리고 그 결과를 돌려줍니다. 이때 돌려주는 값을 결과값, 또는 반환값, 복귀값이라고 합니다.
때로는 함수가 어떤 일을 처리하기 위해서 인수를 제공해주어야 하는 경우가 있습니다. 예를 들어서 a 더하기 b를 계산해서 출력하라고 명령을 내릴 경우에는 더하기를 할 줄 아는 함수에게 a와 b의 값이 얼마인지 알려주어야 합니다. 더하기 함수에 a와 b의 값을 함께 넣어주면서 더해달라고 요구해야 하는데, 이때 함수에게 넣어주는 값이 바로 인수입니다.

main() 함수는 argc와 argv라는 인수를 주로 사용합니다.

main() 함수는 argc와 argv라는 인수를 주로 사용하지만 다른 함수는 다양한 형태의 인수를 사용합니다. 또 인수의 숫자도 함수마다 각기 다릅니다.

C++에서는 void 함수를 제공합니다.

C++은 C에서 발전된 것입니다. 초기의 C를 pre-ANSI C라고 말하며 후기의 C를 ANSI C라고 말합니다. C++은 당연히 후기의 C 즉, ANSI C를 사용합니다. 이런 이유로 C++은 함수에 관하여 초기의 C와는 다른 몇 가지 특징을 가집니다.
첫번째로 void 함수를 제공합니다. 예를 들어 main 함수를 작성하려면 pre-ANSI C에서는 아래와 같이 합니다.

main()
{
......
}

main 함수는 매개변수와 리턴값이 없으므로 위와 같이 작성합니다. 그러나 C++에서는 void라는 키워드를 써서 아래와 같이 나타냅니다.

void main(void)
{
......
}

이를 통해서 매개변수와 복귀값이 없음을 나타냅니다.

C++에서는 함수의 매개변수를 괄호 안에 적습니다.

두번째로 Pre-ANSI C는 함수이름과 본체 사이에 매개변수를 나열하지만 C++에서는 함수의 매개변수를 괄호 안에 적는 점이 다릅니다.

보기.1: pre-ANSI C

swap(pa, pb)
double *pa, *pb;
{
double temp;
temp = *pa;
temp = *pa;
*pa = *pb;
*pb = temp;
}

보기.2: C++

void swap(double *pa, double *pb)
{
double temp;
*pa = *pb;
*pb = temp;
}

이와 같이 C++에서는 괄호 안에서 각 매개변수마다 자료형을 따로 지정하고 있습니다. 이렇게 함으로써 함수를 호출했을 때 컴파일러가 매개변수의 자료형을 검사해줍니다. 예를 들어서 swap(&a, &b); 이라고 써야할 것을 실수로 번지 연산자 &를 빠뜨려서 swap(a, b); 라고 썼다면 C++에서는 에러가 발생하지만, pre-ANSI C에서는 에러가 발생하지 않습니다. 에러가 발생하지 않으면 더 좋은 것이 아니냐고 생각할 수도 있는데, 실은 그 반대입니다. 자신도 모르는 심각한 문제를 안고 있는 프로그램을 만든 것이기 때문입니다.

C+에서는 함수 원형에 의한 선언을 지원합니다.

또한 C++에서는 매개변수의 자료형 검사를 하기 때문에, 프로그램 내에 작성한 함수가 여러 개 있을 경우 그 그 함수가 미리 정의되어 있어야 합니다. 예를 들어서 a() 함수가 b() 함수를 호출하기 위해서는 b() 함수에 대해서 미리 알려주어야 합니다. 즉 a()가 b()를 호출할 때는 미리 알려준 정보를 가지고 호출하므로 Pre-ANSI C에서는 b() 함수가 a() 함수보다 먼저 정의되어야 합니다.
그러나 이런 식으로 함수를 순서대로 나열하기란 매우 껄끄러운 일입니다. 그래서 C++에서는 함수 원형(function prototype)에 의한 함수 선언을 지원하고 있습니다. 함수를 선언하는 방법은 함수 원형에다 ; 기호를 붙여서 프로그램의 앞부분에 두면 됩니다.

예를 들어 jegob() 함수를 선언하려면, 아래와 같이 써주면 됩니다.

void jegob(double *n);

이와 같이 함수 원형을 써줌으로써 함수를 미리 선언해두면 함수의 본체는 프로그램의 어디에 있더라도 상관 없습니다. 때문에 가능하면 프로그램 안에서 사용하는 모든 함수는 프로그램 첫부분에 함수선언을 해두는 것이 바람직합니다. 단 main() 함수만은 예외로 함수 선언이 전혀 필요없는데 그 이유는 main() 함수는 예약된 함수이름이기 때문입니다.

main() 함수는 운영체제에서 사용하는 매개변수를 사용할 수 있습니다.

main() 함수는 도스 또는 윈도95/98에서 실행하는 프로그램이나 다름 없기 때문에 도스에서 사용하는 외부명령어와 매개변수를 사용할 수 있게 되어 있습니다. 도스상태에서 써주는 명령을 명령행이라고 하는데 실행파일 이름 뒤에 명령행 인자를 써줄 수 있습니다. 즉 매개변수를 쓸 수 있습니다. 매개변수는 각 인수마다 하나 이상의 공백이나 탭문자로 나누어야 함이 물론입니다. 이때 따옴표인 "" 사이에 들어 있는 공백은 하나의 문자열로 인식하기 때문에 "" 사이의 모든 문자를 하나의 매개변수로 인식합니다. 단 "" 자체는 매개변수에 포함되지 않고 단지 매개변수를 구별하는 작용만 합니다.

표: main 함수의 여러 가지 원형

void main(void)
int main(void)
void main(int argc, char **argv, char **env)
void main(int argc, char *argv[], char *env[])
int main(int argc, char **argv, char **env)
void main(int argc, char **argv)
void main(int argc, char *argv[])
int main(int argc, char **argv)
void main(int argc)
int main(int argc)

표에 나타난 내용을 바탕으로 main() 함수의 명령행 인자들을 살펴보겠습니다. 보통은 다음과 같은 형태를 가지고 있습니다.

void main(int argc, char *argv[])


argc는 정수 수치로 명령행 인자의 갯수를 적습니다.

여기에서 argc는 정수 수치로서 명령행 인자의 갯수를 알려줍니다. 그러니까 'copy a.txt b.txt'라는 도스명령어를 사용한다면 명령행인자의 수가 3개인 셈입니다. 첫번째 명령행 인자는 copy라는 도스의 프로그램 이름이고 a.txt와 b.txt는 매개변수로 사용하기 때문입니다.
'c:\dos\format a: /s'라는 명령어도 명령행 인자가 3개입니다. c:\dos\format이라는 경로명이 하나의 인자이고, a:와 /s가 두번째와 세번째 인자가 됩니다. 그러므로 만약 format라는 프로그램을 만든다면 argc를 통해서 3이라는 숫자를 넣어주어야 합니다.

argv는 일차원 문자열 배열로 명령행 인자를 문자열로 넘겨줍니다.

argv는 일차원 문자열 배열입니다. 쉽게 말하면 공백으로 구분되는 명령행 인자를 하나씩 차례대로 넘겨주면 되는 겁니다. argv[0]은 첫번째로 실행되는 프로그램의 경로명이 됩니다. 즉 copy나 c:\dos\format가 argv[0]의 요소가 되는 겁니다. 그렇다면 argv[1]은 사용자가 프로그램의 경로명 다음에 써주는 첫번째 매개변수라는 것을 알 수 있을 겁니다. 즉 a.txt나 a:가 이에 해당합니다. 그리고 argv[2]는 프로그램 경로명 다음의 두번째 인자를 가리키므로 b.txt나 /s에 해당하는 옵션이 여기에 속합니다.
이때 argv[]의 배열크기는 argc에서 정한 정수의 숫자에 좌우됩니다. 그러니까 argc에서 3이라고 지정했으면 변수의 크기는 argv[3]이 됩니다. 따라서 argv[0], argv[1], argv[2]의 세 개를 명령행인자로 사용할 수 있는 겁니다.

예를 들어서 다음과 같은 명령줄이 있다고 합시다.

void main(int argc, char *argv[])

위 예문은 argc라는 정수형 매개변수와 argv라는 문자형 매개변수를 사용한다는 뜻입니다. 만약 argc가 2로 대입된다면 매개변수는 2개가 됩니다. 그 중에서 프로그램 이름 즉, 실행파일 이름이 하나의 매개변수가 되므로 파일 이름 뒤에 또 하나의 매개변수를 사용할 수 있습니다. 그리고 이때 argv[]에 수치를 입력하지 않았기 때문에 매개변수로 사용할 문자열의 길이가 제한되지 않습니다.

<<연습문제>>
다음의 내용대로 실행되도록 소스파일을 만들고 strcopy.cpp로 저장합니다.
도스에서 strcopy를 실행시키면 사용법을 알려주고, strcopy This is test와 같이 문장을 뒤에 붙여서 실행시키면 뒤의 문장을 화면으로 출력해주도록 만듭니다.

<<연습문제 정답>>

// STRCOPY.CPP -- 매개변수로 입력한 문자열을 복사해 보여주는 프로그램
#include
#include

void main(int argc, char *argv[])
{
int n;
if (argc < 2) // 매개변수의 숫자가 하나라면 즉, 실행파일 이름만 입력하면
{ // 사용법을 보여주라고 안내문을 보여준다.
cout<<"String Copy Program v0.9 \n";
cout<<"Usage : strcopy [String] \n";
return; // 리턴을 이용해 프로그램을 빠져나간다.
}
for(n=1; n printf("%s ",argv[n]); // 매개변수로 입력받은 문장을 한 글자씩 화면으로 보여줌
}

**요약: C++언어에서는 반드시 main() 함수를 포함해야 하며 모든 프로그램의 시작은 main() 함수로부터 시작합니다. 또 C++언어는 초창기 문법 규격인 Pre-ANSI C와 최근의 문법 규격인 ANSI C로 구분할 수 있는데, ANSI C 문법규격에서는 main() 함수에 void라는
키워드가 추가되었습니다. 또한 ANSI C에서는 함수의 원형에 의한 선언을 지원하며 인수를 괄호 안에 적습니다.

3.9.2. 함수에 관한 규칙과 관례


그럼 함수와 관련한 문법 규칙이나 관례는 무엇이 있는지 알아보겠습니다.

함수 내부에서 다시 또 다른 함수를 정의할 수 없습니다. 즉 사용자 정의 함수를 만들면서 함수의 본체에 그 안에 또 다시 사용자정의 함수를 만들 수는 없다는 이야기입니다.

만든 함수를 반드시 사용할 필요는 없습니다. 즉 만들어만 놓고 사용을 하지 않아도 된다는 이야기입니다.

함수의 값을 꼭 돌려줄 필요는 없습니다. 즉 복귀값이 없어도 된다는 이야기입니다. 만약 함수 안에서 return문을 한 번도 사용하지 않았다면 void 형으로 간주합니다. 이말은 곧 return문을 사용하지 않은 함수는 반드시 void형 함수이어야 한다는 이야기와 같습니다.

함수형은 함수이름 앞에 지정합니다.

매개변수가 없더라도 함수를 나타내는 기호인 괄호'()'는 생략해선 안됩니다. 만약 괄호를 생략하면 함수이름을 포인터 번지로 해석하기 때문입니다.

보기


getch(); // 매개변수가 없더라도 괄호를 생략하면 안됩니다.

각 매개변수의 자료형은 생략할 수도 있지만 가능한 생략하지 말고 일일히 적어주는 것이 좋습니다. 매개변수 리스트를 적을 때는 자료형을 적고 한 칸 띄고 매개변수이름을 적습니다. 여러 개의 매개변수를 적을 때는 쉼표로 각 매개변수를 구별해줍니다.

보기

(int n, int m)
(char *s, int n)
(void)

C 에서는 언제나 프로그램을 처음 실행할때 맨 먼저 main 함수를 실행합니다. 따라서 최소한 main() 함수는 항상 존재해야 합니다.

C에서 함수 이름 자체를 함수 포인터로 인식합니다.

return 문은 함수의 결과값을 돌려줍니다.

return 문은 함수 내의 나머지 부분에 상관없이 함수의 실행을 강제로 종료시킨다. 즉 함수가 실행되다가 return문을 만나면 그 다음 줄을 실행하지 않고 바로 함수를 빠져나갑니다.

void형 함수 내부에서는 return문이 없는 것이 좋습니다. 즉 return문을 사용하지 않는 것이 좋습니다. void형은 돌려줄 반환값이 없기 때문입니다. 만약 void형 함수가 값을 리턴하도록 하면 컴파일할 때 "void functions may not return a value"라는 경고가 나올 겁니다.

void형 함수를 호출할때는 함수만 단독으로 사용해야 한다.

값에 의한 호출에는 형식매개변수를 제아무리 변경을 하더라도 원래의 실매개변수의 값은 전혀 바뀌지 않는다.

표준 함수는 그에 따르는 헤더를 가지고 있는데 include문을 이용하여 프로그램에 이 헤더파일을 포함시켜야 합니다.

헤더파일에 이미 선언되어 있는 함수는 절대 다시 선언하지 말아야 합니다.

함수를 선언할 때는 반환값의 자료형을 적어주는 것이 좋습니다.

"main"의 끝에서는 항상 "exit(0)"을 실행해야 합니다.

가능하면 while문보다는 for문을 자주 쓰는 것이 좋습니다. for문을 사용하면 프로그램도 더 간결해지지만 조건변수에 증감을 하는 것을 잊어먹기 않기 때문입니다.

필요없는 주석을 달지 않는 것이 좋습니다. 불필요한 주석은 오히려 진짜로 필요한 주석을 눈여겨보지 않게 만들며 혼동만 줍니다.

switch문을 사용할 때는 default라벨을 만들어 사용하는 것이 좋습니다.

main 함수는 복귀값으로 0~255 범위의 int형 수치를 돌려줍니다. 이는 탈출코드(exit code)라고도 부릅니다. 정상적인 프로그램의 종료시에는 0을 도스에 건네주지만 비정상적으로 프로그램이 종료되었을 때는 1 이상의 값을 돌려줍니다.

가능하면 재귀호출은 하지 않는 것이 좋습니다. 재귀(recursion)라는 것은 함수가 직접 또는 간접적으로 자기 자신을 다시 호출 하는 것을 말합니다. 특별한 경우를 제외하면 재귀호출은 좋지 않습니다.

3.9.3. 함수 원형과 본체, 함수의 선언과 정의


함수 원형은 함수의 정의와 선언에 사용할 수 있습니다.

함수의 원형에 대해서는 말씀드렸습니다. 다음과 같이 함수를 정의할 때 사용하는 것을 함수원형이라고 합니다.

void test(int a, int b)


변수 이름이나 변수 자체를 생략한 함수 원형은 함수의 선언에만 사용할 수 있습니다.

함수원형은 함수를 선언하거나 함수를 정의할 때 사용합니다. 그런데 이중에서 변수이름을 생략하고 사용할 경우에는 함수의 정의에 사용할 수 없습니다. 즉 함수의 선언에만 사용할 수 있습니다.

void test(int, int)


그리고 아예 매개변수들의 목록을 생략할 수도 있습니다.

void test()


이런 경우에도 함수의 선언에만 사용할 수 있습니다. 보통 함수를 선언할 때는 매개변수 목록을 아예 생략하는 경우가 많습니다. 볼랜드C++ 계열의 컴파일러는 1패스 방식의 컴파일러이므로 매개변수를 생략하고 컴파일을 하다가 어떤 함수를 만났는데, 그 함수가 무슨 형인지 정보가 주어지지 않았다면 int형으로 간주하고 컴파일합니다.
그러므로 int형이 아닌 함수들은 미리 정보를 주지 않을 경우 문제가 발생할 수 있습니다. 그래서 문제발생을 없애기 위해서 미리 함수에 대한 정보를 주는데 이것을 선언이라고 한다고 말씀드렸습니다.

함수의 선언은 함수의 특징만 알려주는 것이고, 정의는 모든 것을 알려줍니다.

함수의 선언을 통해서 알려주는 것은 함수의 이름과 형, 매개변수의 목록 등에 불과합니다. 즉 컴파일러가 컴파일하다가 함수의 선언부를 만나면 '아하, 이 함수는 이런 형이고 이런 이런 자료형의 변수를 가지고 있구나.'하고 판단합니다. 그 함수가 어떤 일을 어떻게 하는지에 대한 정보는 없는 겁니다.

그리고 나중에 이 함수가 정의된 곳에 가서 본체를 만나면 함수가 이상 없이 제대로 되었는지 판단해서 컴파일을 하는 겁니다. 따라서 선언은 단순하게 함수의 특징만 알려주는 것이고, 정의는 함수의 모든 것을 알려주는 겁니다. 함수의 정의 부분에는 함수의 본체가 함께 들어 있기 때문에 이 함수가 어떤 일을 어떻게 처리하는 함수인지도 알게 되는 겁니다.

**요약: 함수 원형은 함수의 정의와 선언에 사용할 수 있습니다. 단 변수 이름이나 변수 자체를 생략한 함수는 선언에만 사용할 수 있습니다. 선언은 함수의 특징만 알려주며 정의는 함수의 모든 것을 알려주는 행위입니다.





첫줄로(go top, go first line) 문화원첫화면으로(go dal site home) 강좌차림으로(go Chair) 사이트맵으로(go sitemap)




total chairpost