6. 함수
6.1 함수
📌 함수 Function
함수
란 특정한 작업을 수행하기 위해 독립적으로 설계된 코드를 말한다.
C++에서의 함수는 특정 작업을 캡슐화할 때 유용하게 사용된다.
함수는 왜 필요한가?
함수의 가장 큰 장점은 반복적인 프로그래밍을 피할 수 있다는 것이다.
특정한 작업을 여러 번 수행해야 할 경우 해당 작업을 수행하는 함수를 만들면 된다.
그리고 그 작업이 필요할 때마다 함수를 호출하여 해당 작업을 간편하게 수행할 수 있다.
인수 & 매개변수 Argument & parameter
인수
는 실인수, 실매개변수라고도 한다.
다음은 인수의 예시이다.
int main() {
...
i = get_max(2, 3);
...
}
여기서 인수는 2와 3이다.
매개변수
는 형식인수, 형식매개변수라고도 한다.
다음은 매개변수의 예시이다.
int get_max(int x, int y) {
...
...
...
}
여기서 매개변수는 x와 y이다.
디폴트 매개변수
는 인자를 전달하지 않아도 디폴트 값이 대신 전달된다.
다음은 디폴트 매개변수의 예시이다.
int get_max(int x, int y=10) {
...
...
...
}
여기서 주의할 점은 디폴트 매개변수는 뒤에서 앞으로만 정의가 가능하다.
int get_max(int x, int y=10); // OK
int get_max(int x, int y=10, int z=20); // OK
int get_max(int x=10, int y=10, int z=20); // OK
int get_max(int x=10, int y); // Error
int get_max(int x=10, int y, int z=20); // Error
반환값 Return Value
반환값
은 호출된 함수가 호출한 곳으로 결과값을 전달하는 것을 말한다.
인수는 여러 개가 가능하지만, 반환값
은 하나만 가능하다.
다음은 반환값의 예시이다.
return 0;
return x;
return x + y;
return x * y;
📌 사용자 함수
함수의 선언
함수의 선언은 컴파일러에 함수에 대해 미리 알리는 것을 말한다.
함수의 선언 방법은 다음과 같다.
반환자료형 함수명 ( 매개변수 1 , ... );
선언 방법을 예제로 살펴보자.
int sum(int a, int b);
함수의 정의
C 언어에서는 반환자료형
을 명시하지 않으면 int
형으로 가정했다.
하지만 C++에서는 반드시 반환자료형
을 명시해야 한다.
함수는 입력
을 받아서 명령어를 수행한 다음 결과
를 생성한다.
함수의 정의 방법은 다음과 같다.
반환자료형 함수명 ( 매개변수 1 , ... ) {
수행할 명령문;
}
정의 방법을 예제로 살펴보자.
int sum(int a, int b) {
int result = a + b;
return result;
}
함수의 호출
함수는 호출하여 사용한다.
마지막으로 함수의 호출 방법은 다음과 같다.
함수명 ( 매개변수 1의 값 , ... );
호출 방법을 예제로 살펴보자.
sum(10, 20);
다음은 두 수의 합을 구하는 함수를 만들고, 그 함수를 호출하는 소스 코드이다.
#include <iostream>
using namespace std;
int sum(int a, int b) {
int result = a + b;
return result;
}
int main() {
int result = sum(10, 20);
printf("두 수의 합: %d\n", result);
return 0;
}
이번에는 다음의 조건을 만족하는 정수의 제곱
을 계산하는 함수를 만들어보자.
(매개변수 값은 터미널에서 입력받는다.)
코드의 조건
- 반환값:
int
- 함수명:
square
- 매개변수:
n
(int)
일단 반환값과 함수명, 매개변수가 주어졌기 때문에 함수를 선언한다.
int square(int n);
이제 정수의 제곱을 계산하는 명령어를 만들어 함수를 정의한다.
제곱을 계산하는 수식은 다음과 같다.
int result = n * n;
이 수식을 사용하여 square
함수를 정의한다.
int square(int n) {
int result = n * n;
return result;
}
이제 main
함수에서 square
함수를 호출한다.
이때, 매개변수 값을 터미널에서 입력받아야 하기 때문에 cin
객체를 사용하여 값을 입력받는다. (cin
객체의 사용은 표준입출력에서 설명했다.)
int main() {
int num, result;
printf("정수를 입력하세요: ");
cin >> num;
result = square(num);
printf("정수의 제곱: %d\n", result);
return 0;
}
이제 소스 코드를 합쳐보자.
#include <iostream>
using namespace std;
int square(int n);
int main() {
int num, result;
printf("정수를 입력하세요: ");
cin >> num;
result = square(num);
printf("정수의 제곱: %d\n", result);
return 0;
}
int square(int n) {
int result = n * n;
return result;
}
마지막으로 다음의 조건을 만족하는 평균 점수를 계산하는 함수와, 그에 맞는 학점을 구하는 함수를 만들어보자. (매개변수 값은 터미널에서 입력받는다.)
코드의 조건
- 반환값:
int
,char
- 함수명:
avg_score
(int),choose_score
(char)- 매개변수:
n1
,n2
,n3
,result_avg
(int)- 조건문:
switch
학점 기준은 다음과 같다.
학점 | 점수 |
---|---|
A | 100~90점 |
B | 89~80점 |
C | 79~70점 |
D | 69~60점 |
F | ~59점 |
일단 반환값과 함수명, 매개변수가 주어졌기 때문에 평균 점수를 계산하는 함수와 학점을 계산하는 함수를 각각 선언한다.
int avg_score(int n1, int n2, int n3);
int choose_score(int result_avg);
이제 평균 점수를 계산하는 함수와 학점을 계산하는 함수를 각각 정의한다.
평균 점수를 계산하는 수식은 다음과 같다.
int sum_score = n1 + n2 + n3;
int avg_score = sum_score / 3;
이 수식을 사용하여 avg_score
함수를 정의한다.
int avg_score(int n1, int n2, int n3) {
int sum_score = n1 + n2 + n3;
int avg_score = sum_score / 3;
return avg_score;
}
이제 switch 문을 사용하여 choose_score
함수를 정의한다. (학점은 학점 기준표를 참고한다.)
char choose_score(int result_avg) {
char result_score;
switch (result_avg / 10) {
case 10:
case 9:
result_score = 'A';
break;
case 8:
result_score = 'B';
break;
case 7:
result_score = 'C';
break;
case 6:
result_score = 'D';
break;
default:
result_score = 'F';
break;
}
return result_score;
}
이제 main
함수에서 avg_score
, choose_score
함수를 호출한다.
이때, 매개변수 값을 터미널에서 입력받아야 하기 때문에 cin
객체를 사용하여 값을 입력받는다.
int main() {
int n1, n2, n3, result_avg;
char result_score;
printf("첫번째 점수를 입력하세요: ");
cin >> n1;
printf("두번째 점수를 입력하세요: ");
cin >> n2;
printf("세번째 점수를 입력하세요: ");
cin >> n3;
printf("내 점수: %d, %d, %d\n", n1, n2, n3);
result_avg = avg_score(n1, n2, n3);
printf("평균 점수: %d\n", result_avg);
result_score = choose_score(result_avg);
printf("\n학점은 %c 입니다.\n", result_score);
return 0;
}
이제 소스 코드를 합쳐보자.
#include <iostream>
using namespace std;
int avg_score(int n1, int n2, int n3);
char choose_score(int result_avg);
int main() {
int n1, n2, n3, result_avg;
char result_score;
printf("첫번째 점수를 입력하세요: ");
cin >> n1;
printf("두번째 점수를 입력하세요: ");
cin >> n2;
printf("세번째 점수를 입력하세요: ");
cin >> n3;
printf("내 점수: %d, %d, %d\n", n1, n2, n3);
result_avg = avg_score(n1, n2, n3);
printf("평균 점수: %d\n", result_avg);
result_score = choose_score(result_avg);
printf("\n학점은 %c 입니다.\n", result_score);
return 0;
}
int avg_score(int n1, int n2, int n3) {
int result_sum = n1 + n2 + n3;
int result_avg = result_sum / 3;
return result_avg;
}
char choose_score(int result_avg) {
char result_score;
switch (result_avg / 10) {
case 10:
case 9:
result_score = 'A';
break;
case 8:
result_score = 'B';
break;
case 7:
result_score = 'C';
break;
case 6:
result_score = 'D';
break;
default:
result_score = 'F';
break;
}
return result_score;
}
📌 오버로딩 함수 Overloading Function
오버로딩 함수
란 같은 이름의 함수를 여러 개 정의하는 것을 말한다.
오버로딩 함수
: 동일한 함수명, 다른 매개변수의 개수 & 다른 데이터형
오버로딩 함수의 장점은 함수의 이름을 재사용
가능하다는 것이다.
// 오버로딩 함수를 사용하지 않을 경우
square_int(int n);
square_double(double n);
square_short(short n);
// 오버로딩 함수를 사용하는 경우
square(int n);
square(double n);
square(short n);
다음은 오버로딩 함수를 사용할 때의 주의사항이다.
int square(int n1);
int square(int n1, int n2); // OK
int square(int n1, double n2); // OK
double square(double n1); // OK
double square(int n1); // Error - 모호한 호출
float square(int n1, int n2); // Error - 모호한 호출
다음은 오버로딩 함수의 예시이다.
#include <iostream>
using namespace std;
int square(int n);
double square(double n);
int main() {
// 인수가 정수이므로 정수를 제곱하는 함수가 호출됨
int result_int = square(2);
// 인수가 실수이므로 실수를 제곱하는 함수가 호출됨
double result_double = square(0.5);
printf("정수의 제곱: %d\n", result_int);
printf("실수의 제곱: %.4f\n", result_double);
return 0;
}
// 정수 값을 제곱하는 함수
int square(int n) {
int result = n * n;
return result;
}
// 실수 값을 제곱하는 함수
double square(double n) {
double result = n * n;
return result;
}
📌 인라인 함수 Inline Function
인라인 함수
란 함수 호출을 하지 않고 함수의 모든 코드를 호출된 자리에 바로 삽입하는 것을 말한다.
inline
: in
(내부) + line
(프로그램 코드 라인)
인라인 함수로 선언하면 함수 호출 시 정의로 이동하지 않고, 컴파일 도중에 해당 함수 자리에 함수를 삽입한다.
→ 함수를 실행하기 위해 정의로 이동할 필요가 없기 때문에 실행 속도가 빠른 장점이 있다.
인라인 함수의 특징
- 컴파일러가 inline화를 결정한다.
- 사용자가 inline 선언을 하지 않아도 컴파일러가 대신 할 수 있다.
- 함수가 비대하거나 재귀 호출일 경우, 컴파일러가 거절할 수 있다.
- 메모리적으로 일반 함수보다 불리한 점이 있다.
→ 만약 10번 호출하면 10번 삽입한다.- 데이터형에 독립적이지 못하기 때문에 템플릿화 해야한다.
인라인 함수의 정의 방법은 다음과 같다.
inline 데이터형 함수명 ( ... ) {
...
}
매크로 함수와의 차이점은 무엇인가?
- 매크로 함수는 기계적인 대치라 잘못 사용될 가능성이 있다.
- 매크로 함수에서는 타입 검사를 하지 않는다.
→ 데이터형에 의존적인 함수가 아니다.
다음은 인라인 함수의 예제이다.
#include <iostream>
using namespace std;
inline int square(int n);
int main() {
// 인수가 정수이므로 정수를 제곱하는 함수가 호출됨
int result = square(2);
printf("정수의 제곱: %d\n", result);
return 0;
}
// 정수 값을 제곱하는 함수
inline int square(int n) {
return n * n;
}
📌 매크로 함수 Macro Function
매크로 함수
란 #define
을 사용하여 인자를 함수처럼 동작할 수 있도록 하는 것을 말한다.
매크로 함수의 특징
- 매크로 함수라고 부르지만 단순히 치환하는 것이기 때문에 실제 함수는 아니다.
- 함수의 선언과 비슷하지만 매크로 함수는 인자가 데이터형에 의존적이지 않다.
- 매크로 함수 내부에서 자기 자신을 호출할 수 없다.
매크로 함수의 사용 방법은 다음과 같다.
#define ( 함수명 ) ( 함수의 기능 )
다음은 매크로 함수의 예제이다.
#include <iostream>
using namespace std;
#define square(n) ((n) * (n))
int main() {
int n = 5;
int result1 = square(n);
int result2 = square(n + 5);
printf("정수의 제곱: %d\n", result1);
printf("정수의 제곱: %d\n", result2);
return 0;
}
6.2 변수의 범위
📌 지역 변수와 전역 변수
지역 변수 Local variable
지역 변수
란 함수의 내부에서 선언되는 변수를 말한다.
지역 변수는 어디서나 선언 가능하다.
다음은 지역 변수의 예시이다.
#include <iostream>
using namespace std;
int func1();
int func2(int n);
int main() {
func1();
int result = func2(10);
printf("result: %d\n", result);
return 0;
}
int func1() {
int num1 = 10;
{
int num2 = 20;
int result_sum = num1 + num2;
printf("두 수의 합: %d\n", result_sum);
}
int result_sqaure = num1 * num1;
printf("정수의 제곱: %d\n", result_sqaure);
return 0;
}
int func2(int n) {
int num1 = 50;
int num2 = 100;
for (int i=0; i<n; i++) {
int temp = num1 + num2;
num1 = num2;
num1 = temp;
}
return num1;
}
지역 변수는 범위를 벗어나면 사용하지 못한다.
int func1() {
int num1 = 10;
{
int num2 = 20;
int result = num1 + num2;
}
return result; // Error - 지역 변수 result의 범위를 벗어남
}
전역 변수 Global variable
전역 변수
란 함수의 외부에서 선언되는 변수를 말한다.
전역 변수는 어디서나 접근 가능하다.
다음은 전역 변수의 예시이다.
#include <iostream>
using namespace std;
int num1 = 50;
int num2 = 100;
int num3 = 10;
int func1(int n);
int main() {
int result = func1(num3);
printf("result: %d\n", result);
return 0;
}
int func1(int n) {
for (int i=0; i<n; i++) {
int temp = num1 + num2;
num1 = num2;
num1 = temp;
}
return num1;
}
📌 저장 유형 지정자
저장 유형 지정자 static
지역 변수
에 static
을 붙이면 정적 변수
로 바뀐다.
다음은 static
의 예시이다.
#include <iostream>
using namespace std;
int func1();
int main() {
func1();
func1();
func1();
return 0;
}
int func1() {
int num1 = 0;
static int num2 = 0; // static을 붙이면 지역 변수가 정적 변수로 바뀜
num1++;
num2++;
printf("num1: %d, num2: %d\n", num1, num2);
return 0;
}
6.3 순환
📌 순환 Recursion
순환
이란 알고리즘이나 함수가 도중에 자기 자신을 다시 호출하여 문제를 해결하는 기법을 말한다.
대표적으로 팩토리얼
이 있다.
순환 알고리즘은 다음과 같은 부분들을 포함한다.
- 순환 호출을 하는 부분
- 순환 호출을 멈추는 부분
순환 호출을 멈추는 부분이 없다면?
시스템 오류가 발생할 때까지 무한정으로 호출하게 된다.
다음은 팩토리얼 프로그래밍의 예시이다.
#include <iostream>
using namespace std;
int factorial(int n);
int main() {
int result = factorial(3);
printf("result: %d\n", result);
return 0;
}
int factorial(int n) {
if (n <= 1)
return 1; // 순환을 멈추는 부분
else
return (n * factorial(n-1)); // 순환 호출을 하는 부분
}