함수를 만드는 이유
해결해야 할 규모가 크거나 형태가 복잡한 경우 구현에 필요한 기능들을 분석하고, 그 분석 결과를 바탕으로 작은 크기의 함수들로 구현한다. 한마디로 "하나씩 천천히 해나가는 것"이다.
기능들을 나눠놓으면 문제의 발생 및 프로그램의 요구사항 변경으로 인한 소스코드의 변경이 필요할 때, 변경의 범위를 축소 및 제한할 수 있다.
함수에는 전달 인자의 유무와 반환 값의 유무에 따른 네 가지의 형태가 있다.
- 1. 전달인자가 있고, 반환 값이 있다.
- 2. 전달인자가 있고, 반환 값이 없다.
- 3. 전달인자가 없고, 반환 값이 있다.
- 4. 전달인자가 없고, 반환 값이 없다.
이러한 유형들의 함수들을 정의해 보겠다.
1. 전달인자와 반환 값이 모두 있는 경우
int Add(int n1, int n2) {
int result = n1 + n2;
return result;
}
이는 덧셈 기능을 구현한 함수이다.
전달 인자로 int형 정수인 n1과 n2를 받으며, result변수에 두 정수를 더한 값을 저장하고 result를 반환하였다.
#include<stdio.h>
int Add(int n1, int n2) {
return n1 + n2;
}
int main() {
printf("%d", Add(2, 3));
return 0;
}
이와 같이 main함수에서 Add함수의 매개변수에 값을 정해주면 Add함수에서 매개변수가 값을 전달받아 2 + 3이라는 값을 반환한다.
2. 전달인자 또는 반환 값이 없는 경우
void ShowAddResult(int num) {
printf("덧셈결과 출력: %d\n", num);
}
위는 전달인자가 있고, 반환 값이 없는 코드이다. printf를 대신할 수 있는 함수로 정의해 보겠다.
printf 함수는 서식을 지정해야 하기 때문에 상대적으로 빈번히 호출하기가 번거롭다.
하지만 이러한 함수를 만들었기 때문에 서식지정을 하지 않고 함수를 호출하면 된다.
int ReadNum() {
int num = 0;
scanf("%d", &num);
return num;
}
이번에는 전달인자가 없고 반환 값이 있는 코드이다. 이는 scanf를 대신할 수 있는 함수로 정의해 보았다.
printf와 같이 scanf도 서식 지정을 계속해서 해주는 게 번거로우니 함수를 통해 호출을 하도록 하자.
3. 전달인자와 반환 값이 모두 없는 경우
void Question() {
printf("두개의 정수를 입력하시오: ");
}
이와 같은 함수는 단순히 메시지를 전달하는 함수로 전달 인자와 반환 값이 모두 필요 없다.
이러한 작은 코드들을 한 번에 모아 보면 다음과 같이 된다.
#include<stdio.h>
void Question() {
printf("두개의 정수를 입력하시오: ");
}
int Add(int num1, int num2) {
return num1 + num2;
}
void ShowAddResult(int result) {
printf("덧셈결과 : %d\n", result);
}
int ReadNum(){
int num = 0;
scanf("%d", &num);
return num;
}
int main() {
Question();
num1 = ReadNum();
num2 = ReadNum();
int result = Add(num1, num2);
ShowAddResult(result);
return 0;
}
이와 같이 함수들을 나누어 하나씩 해결하다 보면 큰 프로젝트를 보기 쉽게 나눌 수 있다.
재귀함수(Recursive Function)
재귀함수란 함수 내에서 자기 자신을 다시 호출하는 함수이다.
void Recursive() {
printf("Recursive call!\n");
Recursive();
}
이해가 잘 되지 않는다면 위의 사진과 같이 복사본이 실행되는 구조라 생각하면 된다.
"Recursive 함수를 실행하는 중간에 다시 Recursive 함수가 호출되면, Recursive함수의 복사본을 하나 더 만들어서 복사본을 실행하게 된다."
예제를 보며 확인해보자.
#include<stdio.h>
void Recursive(int num) {
if(num <= 0) return;
printf("%d\n", num);
Recursive(num-1);
}
int main() {
Recursive(3);
return 0;
}
// 3 2 1
위에서 보면 Recursive함수에서 num이 0보다 같거나 작으면 함수가 종료되도록 하는 "탈출 조건"을 만들어 주었다.
main함수에서 Recursive함수에 3을 전달인자에 주었고, 이를 통해 num이 0보다 클 시 출력이 되며 재귀함수를 통해 num-1이라는 값을 전달인자가 또 다시 받게 된다. 반복을 하다 num이 0의 값이 된다면 함수가 끝나게 되는 것이다.
재귀함수는 자료구조나 알고리즘의 어려운 문제를 단순화하는 데 사용되므로 중요한 역할을 하고 있다. 또한 재귀적인 수학적 수식을 그대로 코드로 옮길 수 있다. 예를 들어 팩토리얼(Factorial) 값을 반환하는 함수를 재귀적으로 구현해보자.
팩토리얼이란
n! = n * (n-1) * (n-2) * (n-3) * . . . . . * 2 * 1이라는 수식이다.
따라서 5! = 5 * 4 * 3 * 2 * 1인 120이 된다.
이러한 알고리즘을 디자인해보면,
수식적으로 이렇게 표현할 수 있다.
따라서 n이 1 이상일 경우 n * f(n-1)의 값을 반환하면 된다.
if(n >= 1) return n * Factorial(n-1);
또한 n이 0인 경우의 결과값은 1이므로 아래와 같이 된다.
if(n == 0) return 1;
이를 if~else를 통해 묶으면
if(n == 0) return 1;
else return n * Factorial(n-1);
이와 같이 된다. 이로써 팩토리얼의 값을 반환하는 Factorial함수를 완성한 것이다.
완전한 코드를 통해 결과값이 제대로 나오는지 알아보겠다.
#include<stdio.h>
int Factorial(int num) {
if(num == 0) return 1;
else return n * Factorial(num - 1);
}
int main() {
for(int i=1; i<=5; i++) printf("%d! = %d\n", i, Factorial(i));
return 0;
}
/*
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
*/
*재귀함수는 함수의 코드를 이해하는 것보다 그 과정을 이해하는 것이 더 중요하다.
'Programming > C' 카테고리의 다른 글
C언어 - 1차원 배열 (0) | 2021.09.23 |
---|---|
C언어 - 지역변수와 전역변수 (0) | 2021.09.22 |
C언어 - 흐름의 분기(if, else, continue, break, switch) (0) | 2021.09.17 |
C언어 - 반복문(while문, do~while문, for문) (0) | 2021.09.15 |
C언어 - 상수, 자료형 (0) | 2021.09.14 |
댓글