C++ 프로그래밍, 함수 ( Functions )
함수 선언 위치
함수의 선언은 해당 함수 호출부보다 먼저 선언되어야 한다. 예를 들어, main() 함수에서 다른 함수를 호출하려면 호출되는 함수는 main()함수보다 먼저 선언되어야 한다
#include <iostream> using namespace std; //함수는 해당 함수의 호출부보다 먼저 선언해야 한다 void funcA(){ cout << "여기는 funcA()" << endl; } void funcB( void ) { // 파라미터가 없는 경우에는 void 라는 키워드를 사용할 수도 있고 그냥 공백으로 두어도 된다 cout << "여기는 funcB()" << endl; } int main() { cout << "C++ 함수 사용하기" << endl; funcA(); // 함수의 호출 funcB(); return 0; }
함수의 선언보다 해당 함수의 호출부가 앞서는 경우 ( 오류 )
선언된 함수에서 다른 함수를 호출하는 경우에도 호출부보다 선언부가 먼저 와야 한다. 그렇지 않으면 다음과 같은 오류가 발생한다
#include <iostream> using namespace std; //함수는 해당 함수의 호출부보다 먼저 선언해야 한다 void funcA(){ cout << "여기는 funcA()" << endl; funcB(); // 오류 발생, error: 'funcB' was not declared in this scope } void funcB() { cout << "여기는 funcB()" << endl; } int main() { cout << "C++ 함수 사용하기" << endl; funcA(); // 함수의 호출 return 0; }
아래의 예와 같이, 함수끼리 상호 호출하는 경우에는 어떤 함수를 먼저 선언해야 할지 결정할 수 없는 경우도 있다.
#include <iostream> using namespace std; //함수는 해당 함수의 호출부보다 먼저 선언해야 한다 void funcA(){ cout << "여기는 funcA()" << endl; funcB(); // 오류 발생, error: 'funcB' was not declared in this scope } void funcB() { cout << "여기는 funcB()" << endl; funcA(); // 함수의 호출 } int main() { cout << "C++ 함수 사용하기" << endl; funcA(); // 함수의 호출 return 0; }
함수 선언 위치에 따른 오류 해결 : 함수의 원형(Prototype)만 main 함수 위에 선언하고 함수의 정의부는 main 함수 아래에 둔다
#include <iostream> #include <cstdlib> // exit(0)를 사용하기 위함 using namespace std; //함수의 원형(Prototype) 선언 (컴파일러가 함수호출 형식을 검사할 때 참조한다) void funcA(); void funcB(); int cnt = 0; int main() { cout << "C++ 함수 사용하기" << endl; funcA(); // 함수호출. 컴파일러는 함수의 원형을 참조하여 호출형식을 검사한다 return 0; } void funcA(){ cout << "여기는 funcA()" << endl; funcB(); // 함수호출. 컴파일러는 함수의 원형을 참조하여 호출형식을 검사한다 } void funcB() { cout << "여기는 funcB()" << endl; if(++cnt == 5) exit(0); funcA(); // 함수호출. 컴파일러는 함수의 원형을 참조하여 호출형식을 검사한다 }
함수의 오버로드(Overload) : 함수의 이름이 서로 동일한 함수를 다수개 선언하는 것
오버로드할 때는 함수의 이름은 동일해야 하고 파라미터는 갯수나 순서, 자료형을 달리 정의하면 된다. 리턴타입은 오버로드 문법에 아무런 영향을 받지 않으므로 임의로 지정하면 된다
#include <iostream> using namespace std; // 함수 오버로드(Overload)의 예 int add(int, int); // 함수의 원형을 선언할 때 파라미터 변수명은 생략가능 double add(double, double); int main() { cout << "C++ 함수 오버로드(Overload)" << endl; int res = add(3,8); cout << "정수의 합:" << res << endl; double res2 = add(1.2, 3.4); cout << "실수의 합:" << res2 << endl; return 0; } int add(int a, int b) { return a+b; } double add(double a, double b) { return a+b; }
함수의 파라미터에 디폴트 값을 설정하는 경우
#include <iostream> using namespace std; // 디폴트 아규먼트 값을 갖는 함수의 원형(반드시 원형을 선언할 때 디폴트 값도 선언해줘야 한다) // 다수개의 파라미터를 가진 함수의 경우에는 마지막 파라미터만이 디폴트 값을 가질 수 있다 void printGugu(int dan=2); int main() { cout << "C++ 디폴트 파라미터 값을 가진 함수의 사용" << endl; printGugu(); // 2단 출력 printGugu(5); // 5단 출력 return 0; } void printGugu(int dan){ for(int i=1;i<=9;i++) { cout << dan << " x " << i << " = " << dan*i << endl; } cout << "-----------" << endl; }
inline 함수의 사용
함수의 원형이나 정의부에 inline 키워드를 사용하면 인라인 함수가 된다. 인라인 함수는 컴파일될 때 그 함수의 호출부가 그 함수의 바디 내용으로 대치되어 컴파일되므로 함수의 전환으로 인한 지연현상이 발생하지 않는다. 그러므로 성능면에서는 더 유리하게 되는 반면 함수를 호출하는 부분마다 그 함수의 바디 내용이 복사되므로 메모리를 더 사용하게 되는 단점도 있다. 그래서 주로 함수의 내용이 매우 적고 자주 호출되는 함수를 인라인 함수로 선언하는 경향이 있다.
#include <iostream> #include <cstdlib> // srand() #include <ctime> // time() using namespace std; // inline 함수의 선언(함수의 원형이나 정의부에 inline 키워드를 사용한다) inline int getRandNum() { srand(time(NULL)); return rand() % 10 + 1; } int main() { cout << "C++ inline 함수의 사용" << endl; int n = getRandNum(); cout << "인라인 함수 호출결과:" << n << endl; return 0; }
함수의 재귀호출 ( Self-Invocation )의 예
재귀호출은 함수내에서 해당 함수를 반복적으로 호출하여 함수 내용을 반복적으로 수행하는 것이다. 반복적으로 동일한 로직을 실행하기 위한 방법에는 일차적으로 while, for 등의 반복문을 사용하는 것이 좋다. 그러나 일반 반복문으로는 구현하기 어려운 알고리듬 문제도 있으며 이런 경우에는 재귀호출만이 해결책이 되는 경우도 있을 수 있다. 다음은 재귀호출을 이용하여 팩토리얼(Factorial)을 구하는 예이다.
#include <iostream> #include <cstdlib> // srand() #include <ctime> // time() using namespace std; //재귀호출에 사용할 함수 선언 int factorial(int i) { if(i==1) return i; return i*factorial(i-1); } int main() { cout << "C++ 함수의 재귀호출 사용" << endl; int res = factorial(5); cout << "factorial(5):" << res << endl; return 0; }
Call by value / Call by reference
함수를 호출할 때 함수의 아규먼트로 어떤 변수를 사용하면 그 변수 자체가 해당 함수 안으로 전달되는 것이 아니라 아규먼트로 사용된 변수의 값만 복사해서 함수의 파라미터 변수에 전달된다. 이런 방식을 값에 의한 호출(Call by value) 라고 하고, 함수를 호출할 때 해당 함수의 파라미터에 본 함수에 있는 변수 자체를 전달하는 방식을 참조에 의한 호출(Call by reference)이라고 한다. 참조에 의한 호출을 사용하면 본 함수의 변수 자체를 특정 함수의 안으로 전달하는 개념이므로 호출된 함수 안에서도 본 함수의 변수의 값을 조작할 수 있게 된다. Call by value 방식에 의한 함수의 호출은 본 함수에 있는 변수의 값을 변경할 수가 없다.
Call by reference 방식으로 호출될 함수는 파라미터를 선언할 때 참조변수를 사용해야 하며, 해당 함수를 호출할 때는 Call by value 방식의 함수를 호출하는 방식과 동일한 방법으로 호출하면 된다. 파라미터에 참조변수를 선언하는 예는 다음과 같다
void change ( int& num ) { ..... }
#include <iostream> using namespace std; // Call by value void changeByValue(int num){ num *= 2; cout << "changeByValue(): num=" << num << endl; // 10 } // Call by reference void changeByReference(int& num){ // 참조변수(int& num)를 파라미터로 선언한다 num *= 2; cout << "changeByReference(): num=" << num << endl; // 10 } int main() { cout << "Call by value, Call by reference" << endl; int num = 5; changeByValue(num); cout << "After changeByValue(): num=" << num << endl; // 5 changeByReference(num); cout << "After changeByReference(): num=" << num << endl; // 10 return 0; }