언어/C언어

[C언어] 함수(Function) 개념과 구현방식, 예제

REAKWON 2018. 12. 30. 15:18

함수(Function)


함수를 중학생때부터 배우죠? 그렇기 때문에 저는 중학교 시절 수학을 포기했습니다. 여러분들은 저보다 뛰어나시니 그렇지는 않았겠죠?


C언어에서 함수는 아주 필수적이라고 할 수 있습니다. 함수에 대해서 간단히 말씀을 드리면 반복되는 코드를 하나로 묶어 필요할때 가져다가 쓴다는 것입니다.


다음의 코드는 어떻게 생각하시나요?

단순히 세 입력의 펙토리얼(!)을 구하여 곱하는 프로그램이지요.




#include <stdio.h>

int main() {
	int fact_a = 1, fact_b = 1, fact_c = 1;
	int a, b, c;
	int i;
	scanf("%d %d %d", &a, &b, &c);
	
	for (i = 1; i <= a; i++)
		fact_a*=i;
	
	for (i = 1; i <= b; i++)
		fact_b *= i;

	for (i = 1; i <= c; i++)
		fact_c *= i;

	printf("%d! * %d! * %d!= %d\n", a, b, c, fact_a*fact_b*fact_c);

	return 0;
}


프로그램이 잘 동작하는지 실행해보세요!

잘 돌아가지요? 문제 없습니다.


하지만 저는 조금 불만인데요. 펙토리얼을 세번 불러오는데, 세번 다 for루프를 돌려야하기 때문에 손가락이 아픈게 그 이유인데요.


만약 10개의 입력으로 들어오고 10개의 펙토리얼의 곱을 구하게 되면 위의 코드에서 for루프를 7개 더 추가해야한다는 것이 매우 불만이지요.


우리가 팩토리얼의 기능을 하는 하나의 코드를 두고 그 코드를 원할때 마다 불러온다면 어떨까요? 그런 역할을 하는 것이 바로 함수입니다.


함수 구현 방식

함수는 어떻게 생겨먹었을까요? 꽤나 간단하게 이해할 수 있을겁니다.

함수의 형태는 이렇습니다.


반환형 함수이름(매개변수1, 매개변수2, ... ){

         //몸체

         //return 반환값

}


o 반환형 : 반환되는 값의 자료형을 의미합니다. 함수에서는 반환되는 값이 없을 수 있는데요. 그 경우 void를 사용합니다. 만약 반환값이 있다면 return에서 값을 반환시켜 주면 됩니다. 그러니 반환값의 자료형과 반환형이 일치해야됩니다.


o 함수이름 : 함수를 불러올때 사용되는 이름입니다. 여러분이 지어주기 나름인데, 이 이름을 보고 사용하는 사람이 "아~ 이런 기능을 하는 함수겠구나!" 라고 알 수 있도록 잘 지어주어야 합니다.


o 매개변수 : 함수에 대한 input이라고 생각하면 됩니다. 이 매개변수를 토대로 함수의 반환값이 달라질 수 있습니다.


기본적인 매개변수의 동작은 전달받은 인자의 값을 복사하는 것입니다. (단, 포인터와 같은 매개변수는 값의 복사가 아닌 참조를 하게 됩니다.)


o 몸체 : 함수가 어떻게 기능을 할지 로직을 구현하는 부분입니다.


return : 반환값을 반환하는 명령입니다. return은 제어문으로 여러개 올 수 있습니다. 단, return은 한번만 진행하므로 만약 if 조건에서 return 문을 썼는데, if 조건에 걸리게 된다면 이 후의 코드를 실행시키지 않고 반환합니다.


혹은 반환형이 void이지만 그 함수를 어떤 조건에서 끝내고 싶다면 반환값없이 그냥 return을 사용해주면 그 즉시 함수를 끝냅니다.




우리는 위의 허접한 코드를 factorial이라는 함수를 만들어 조금 더 간편하게 바꿔볼 생각입니다.

위의 형식 그대로 사용해서 factorial을 구현한다면 이렇게 생겼겠죠?


int factorial(int n) {
	int ret = 1;
	int i;
	for (i = 1; i <= n; i++)
		ret *= i;
	return ret;
}


반환형태는 int형이면서 매개변수는 정수형 n입니다. 비교해보세요. 반환형과 반환값(ret)의 자료형이 일치하는 것을 알 수 있죠?


그 후 메인에서는 이 함수를 호출해서 쓰기만 하면 된답니다.


#include <stdio.h> int factorial(int n) { int ret = 1; int i; for (i = 1; i <= n; i++) ret *= i; return ret; } int main() { int fact_a = 1, fact_b = 1, fact_c = 1; int a, b, c; int i; scanf("%d %d %d", &a, &b, &c); fact_a = factorial(a); fact_b = factorial(b); fact_c = factorial(c); printf("%d! * %d! * %d!= %d\n", a, b, c, fact_a*fact_b*fact_c); return 0; }


어때요? 메인이 훨씬 간결해졌음을 알 수 있습니다.


호출 과정은 다음과 같습니다.



메인 함수를 실행하다가 factorial함수를 만났습니다. 그러면 factorial 함수를 실행시키고 함수가 끝나면 다시 메인함수로 돌아와서 그 전에 실행했던 것을 계속 진행하게 됩니다.



fact_a는 factorial 함수의 반환값이 저장됩니다. 나머지 fact_b, fact_c도 역시 마찬가지구요.


만약 10개의 입력이 주어진다하더라도 factorial만 10번 호출하면 되지요.

(아 물론 이 경우에는 배열과 반복문을 써야하겠지만)




함수 선언

근데 꼭 위에서만 함수를 정의하고 몸체를 구현해야할까요? 그럴필요는 없습니다. 

함수를 메인 아래에서 정의할 수도 있습니다.


하지만 꼭 위에서 함수 선언을 해주어야만 합니다. 왜 그러냐구요?

C언어는 절차지향언어이기 때문에 위에서 아래로 실행하기 때문이지요. 그래서 함수가 밑에 정의되어있는데 메인함수에서 그 함수를 호출한다고 하면 컴파일러는 그 함수를 본적이 없으니까 컴파일 에러를 토하게 됩니다.


위의 코드를 함수의 선언 방식으로 코딩해보도록 하면 다음과 같이 간단하게 바뀝니다.




#include <stdio.h>

int factorial(int);     //함수선언

int main() {
	int fact_a = 1, fact_b = 1, fact_c = 1;
	int a, b, c;
	int i;
	scanf("%d %d %d", &a, &b, &c);
	
	fact_a = factorial(a);
	fact_b = factorial(b);
	fact_c = factorial(c);
	
	printf("%d! * %d! * %d!= %d\n", a, b, c, fact_a*fact_b*fact_c);

	return 0;
}

int factorial(int n) {
	int ret = 1;
	int i;
	for (i = 1; i <= n; i++)
		ret *= i;
	return ret;
}


함수에 밑에 있군요. 메인함수 위의 선언이 있죠?

선언에서는 매개변수의 자료형만 적어주어도 상관없습니다.


재귀함수


함수에서 자신의 함수를 불러오는 것을 바로 재귀함수라고 합니다. factorial함수는 재귀함수로도 구현할 수 있습니다.

int factorial(int n) {
	if (n <= 1) return 1;
	return n*factorial(n - 1);
}

factorial함수에서 factorial함수를 호출하는 것을 볼 수 있지요? 매개변수 n과 다음 factorial(n-1)의 반환값을 곱하는 과정을 반복하고 있습니다. factorial의 매개변수 n은 하나씩 줄어들어 결국에는 1 이하가 될겁니다. 그때 1을 반환하지요.


결국 n * (n-1) * (n-2) * ... * 1이 되어 n!을 구현하는 함수죠.


그림으로 보면 더 이해가 쉽게 될겁니다.






3!을 구하는 과정을 보여줍니다. factorial(3)은 factorial(2)를 호출하고 factorial(2)는 factorial(1)을 호출하는 과정을 보여주고 있습니다.


이때 factorial(1)은 if조건문에 걸려 1을 반환하여 더이상 자신을 호출하지 않습니다. 

*** 이를 기저 사례라고 합니다.


재귀함수는 시스템의 스택을 사용하고 계속 사용할 경우 stack overflow가 발생할 수 있으므로 되도록이면 반복문을 사용하는 것이 좋습니다.


이상으로 함수에 대해서 기본적인 설명을 해봤습니다.


반응형