본문 바로가기

Programming Language/C

함수

1. 함수

처음 학교에서 함수를 배웠을 때를 떠올려 보면 아래와 같은 그림을 자주 접했을 것입니다.

위 그림과 같이 함수는 함수 내부에서 해당 함수만의 연산과정에 따라서 입력 받은 값을 처리한 뒤에 출력하는 구조를 가집니다. 또한 함수는 특정한 기능에 대한 소스코드가 반복이 되는 것을 줄여줍니다. 애초에 다양한 프로그래밍 기법들은 프로그래밍을 얼마나 효과적으로 할 수 있는지, 얼마나 생산성을 높게 만들 수 있는지에 초점을 맞추고 있습니다. 따라서 함수 또한 특정한 소스코드가 반복되는 것을 줄이기 위한 목적으로 가장 많이 사용이 됩니다.

1.1. 형태

함수의 형태는 아래와 같습니다. 우리가 자주 사용해 왔던 main함수 또한 아래와 같은 형태를 따르고 있습니다.

 반환자료형 함수명(매개변수) {
     // 수행될 명령어
    return 반환할 값;
}

  • 반환자료형
    함수가 수행되어서 처리된 결과를 어떠한 자료형으로 반환해주는지를 적는다. 예를 들어 뒤에서도 다루겠지만 두 개의 값을 더하는 경우에는 반환자료형이 int가 될 것이다. 즉, 결과 값의 형태를 알 수 있는 것입니다.
  • 매개변수와 반환값
    함수에서 매개변수와 반환할 값은 경우에 따라서 없을 수도 있습니다. 예를 들어 어떠한 내용을 단순히 출력만 하는 함수는 매개변수를 받을 필요가 없습니다. 매개변수 및 반환할 값이 없을 때의 자료형은 void가 됩니다.

1.2. 예제1

그럼 이제 함수가 왜 소스코드가 반복되는 것을 줄일 때 효과적인지 예제를 통해서 알아보겠습니다.
먼저, 아래 코드에서 invest라는 함수는 단순히 input의 넣은 값을 문장에 붙여서 출력하기만 하는 함수입니다. 또한, 메인함수에는 매개변수로 각각 10, 100, 30을 넣은 invest함수 3개를 선언했습니다.

#include <stdio.h>

void invest(int input) {
    printf("카카오벤처스가 투자한 금액: %d억원\n", input);
}
int main(void) {
    invest(10);
    invest(100);
    invest(30);
    system("pause");
    return 0;
}

결과:

카카오벤처스가 투자한 금액: 10억원
카카오벤처스가 투자한 금액: 100억원
카카오벤처스가 투자한 금액: 30억원
계속하려면 아무 키나 누르십시오 . . .

코드를 보면, 맨처음 메인 함수에서 invest함수에 10이라는 값이 input값에 담기게 되고 단순히 문장을 출력한 뒤 함수는 종료됩니다. 이와 같은 과정을 2번 더 진행하게 됩니다. 만약 함수를 쓰지 않았다면 어떻게 코드가 작성되었을까요?

#include <stdio.h>

int main(void) {
    printf("카카오벤처스가 투자한 금액: 10억원\n");
       printf("카카오벤처스가 투자한 금액: 100억원\n");
    printf("카카오벤처스가 투자한 금액: 30억원\n");
    system("pause");
    return 0;
}

결과는 동일하지만 숫자만 다른 동일한 코드가 여러 번 반복되어 보기에 좋지 않습니다. 코드가 현재는 짧기 때문에 함수를 사용한 것과 사용하지 않은 것의 차이가 크게 나지 않으나 코드가 더 복잡해지고 길어진다면 이와 같은 방식은 지저분해 보이고 난잡해 보일 수 있습니다. 따라서 함수화하는 습관을 들이는 것이 중요합니다.

1.3. 예제2 더하기 함수

두 수를 더한 값을 출력하는 프로그램을 작성해보겠습니다.

두 개의 매개변수를 입력받고 그 두개의 값을 더한 값을 반환할 수 있도록 하는 함수를 만듭니다. 반환되는 결과가 int형이기 때문에 반환자료형으로 int를 넣어주면 됩니다.

int add(int a, int b) {
    return a + b;
}

메인함수에서 add()함수를 통해 반환된 값은 정수이기 때문에 형식 지정자로 '%d'를 사용해서 정수를 출력할 수 있도록 합니다.

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}
int main(void) {
    printf("%d\n", add(10, 20));
    printf("%d\n", add(50, -30));
    printf("%d\n", add(70, 125));
    system("pause");
}

1.4. 예제3 사칙연산 함수

다양한 사칙연산을 수행할 것이기 때문에 함수화를 통해 기능을 제공할 수 있도록 만들겠습니다. 두 개의 정수를 입력받으면 사칙연산을 수행한 결과를 출력할 수 있도록 합니다.
main함수에서는 간단히 사칙연산 함수를 실행하기만 하면 됩니다.

#include <stdio.h>

void calculator(int a, int b) {
    printf("%d + %d = %d\n", a, b, a + b);
    printf("%d - %d = %d\n", a, b, a - b);
    printf("%d * %d = %d\n", a, b, a * b);
    printf("%d / %d = %d\n", a, b, a / b);
    printf("\n");
}

int main(void) {
    calculator(10, 3);
    calculator(15, 2);
    calculator(18, 4);
    system("pause");
}

이와 같이 함수를 이용하면 main함수가 매우 간결해지고 전반적인 프로그램의 형태가 매우 예쁘게 된다는 점을 확인할 수 있습니다. 또한 이러한 함수들을 묶어서 만든 것을 라이브러리라고 합니다. 위의 코드 상단에서 불러온 stdio.h 또한 라이브러리로 내부적으로는 다양한 함수와 기능들을 묶어서 하나의 패키지 형태로 만들어 놓은 것이라고 이해하면 되겠습니다.


2. 재귀함수

재귀함수란 자기 자신을 포함하는 함수입니다. 기본적으로 자기 자신을 계속해서 불러내기 때문에 반드시 재귀 종료 조건이 필요하고 종료 조건이 없을 시에는 무한루프를 반복하게 됩니다. 재귀함수를 이용해서 팩토리얼(factorial)을 계산하는 프로그램을 작성해보면서 재귀함수에 대해서 조금 더 자세히 알아보겠습니다.

2.1. 재귀함수를 이용한 팩토리얼

팩토리얼은 1부터 n개의 양의 정수를 모두 곱한 것으로 n!와 같이 나타냅니다. n!은 1 * 2 * 3 * ... * (n-1) * n 과 같이 계산됩니다. n! = n * (n-1)!이라는 성질에서 n이 1일 때 1 = 1 * 0! 이 되므로 0! = 1 로 약속합니다.

위에서 정의한 팩토리얼을 볼 때 팩토리얼은 n! = n * (n-1)!이라는 성질을 가지고 있는데, 이것을 재귀함수로 구한다면 간단한 코드로 쉽게 구할 수가 있습니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int factorial(int a) {
    if (a == 1) return 1;
    else return a * factorial(a - 1);
}

int main(void) {
    int n;
    printf("n팩토리얼을 계산합니다. ");
    scanf("%d", &n);
    printf("%d\n", factorial(n));
    system("pause");
}

결과:

n팩토리얼을 계산합니다. 5
120
계속하려면 아무 키나 누르십시오 . . .

위 코드에서 factorial 함수에 대한 설명을 하자면 main함수에서 n값으로 5를 받았을 때, factorial(5) = 5 * factorial(4)가 된다. 따라서 factorial 함수는 factorial(4)에 대한 계산을 하러 다시 돌아갑니다. factorial(4) = 4 * factorial(3)이 되고 마찬가지로 동일한 계산이 반복됩니다. 계속 반복된다면 무한루프가 발생하기 때문에 우리는 종료 조건을 걸어두었습니다.

if (a == 1) return 1;

a가 1이 되는 순간 1을 반환하고 factorial 함수는 종료가 됩니다. 따라서 factorial(2) = 2 * factorial(1)이 되는 순간 factorial(1)은 1이 되고 factorial 함수는 종료되며 결과값 120을 출력합니다. 결과적으로 factorial(5) = 5 * 4 * 3 * 2 * 1 과 같은 계산이 이루어진 것입니다.

재귀함수를 이용하고 종료조건만 잘 명시한다면 기본적인 반복문의 형태를 유사하게 구현할 수 있습니다. 마찬가지로 반복문을 이용해서도 팩토리얼을 계산할 수 있습니다. 아래의 코드는 for문을 활용한 팩토리얼 계산이며 결과는 동일합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int factorial(int a) {
    int i;
    int x = 1;
    for (i = 1; i <= a; i++) {
        x = x * i;
    }
    return x;
}

int main(void) {
    int n;
    printf("n팩토리얼을 계산합니다. ");
    scanf("%d", &n);
    printf("%d\n", factorial(n));
    system("pause");
}

참고로 재귀함수인지 판별하는 방법은 자신의 함수명이 함수의 몸통 부분에 들어가 있는지 확인하면 된다. factorial 함수를 예로 들면 factorial함수가 다른 factorial함수를 불러온다는 점에서 재귀함수라고 말할 수 있다.


3. 정리

  1. C언어는 함수로 시작해서 함수로 끝나는 언어입니다. 프로그램 실행과 동시에 main함수가 실행이 되고, main함수는 또 다른 함수를 불러오기 때문에 C언어 자체가 함수의 연속이라고 볼 수 있습니다.
  2. 재귀함수는 코드를 간결하게 만들어 줄 수 있습니다. 그러나 무한루프가 발생할 수 있기 때문에 특정한 지점에 재귀함수가 종료할 수 있는 조건을 꼭 달아놓아야 합니다.