Home C언어 (12) 함수
Post
Cancel

C언어 (12) 함수

C언어 (12) 함수

책을 보면 함수를 간단하게 소개하고, 그 다음이 배열, 포인터 순서로 하는 것이 대부분이다.
하지만 포인터, 포인터-배열 관계, 함수, 포인터-함수 관계로 잇고 싶어서 순서를 조금 꼬았다.
흐름만 이해하면 함수는 쉽게 이해하는 것 같다.

함수

image

수학에서 함수란 어떤 x를 넣었을 때 y라는 값을 내는 f(x)같은 것이다.
컴퓨터에서의 함수(function)도 비슷하게 특정한 기능을 수행하는 코드의 묶음이다.

사실 우리는 이미 만들어 놓은 함수들을 계속 쓰고 있었다.

1
2
printf()
scanf()

와 같은 함수들은 C언어에서 제공하는 표준 라이브러리 함수이다.
각각 출력과 입력을 하는 기능이 있는 코드의 묶음이다.
printf 정의

위의 링크에서 printf 및 다양한 함수들의 원형과 정의를 볼 수 있다.
잠깐 보고 오면 알겠지만 무슨 소리인지 알아보기 어려운 코드들이 나열되어 있다.
만약 프로그램을 만드는데 출력이 필요할 때마다 저런 원형을 찾아서 사용해야한다면 아주 불편할 것이다.

이렇게 프로그램 개발의 생산성을 높이기 위해서 어떤 기능을 하는 코드가 반복된다면 함수를 사용하면 좋다.
프로그래머가 직접 작성한 함수를 사용자 정의 함수라고 하고 큰 단위의 프로그램을 만들 때 특히 도움이 된다.
큰 프로그램은 기능별로 코드를 세분화하는 것이 필요한데, 함수를 통해 나눈다면 코드의 안정성이 좋아진다.
또한 필요한 부분만 쉽게 수정할 수 있고, 에러를 수정하는 것도 쉽다.

한 번 함수를 만들어 놓으면 필요할 때마다 호출해서 사용하면 되므로 재사용성도 좋다.
이런 이야기들은 소프트웨어 공학을 공부하면 더 자세하게 들을 수 있다.
main() 함수에 모든 기능을 넣으면 코드가 얼마나 복잡해질까? 가독성을 위해서도 필요하다.

  • 함수의 장점
    • 코드 재사용성 (한 번 정의되면 여러 곳에서 반복적으로 사용해 코드의 중복을 줄인다.)
    • 유지보수 능력 향상 (특정 기능에 버그가 있을 때 해당 기능의 함수만 확인하면 된다.)
    • 가독성 향상 (코드를 읽지 않아도 함수 이름으로 기능을 예상할 수 있고 보기 좋아진다.)
    • 성능 최적화 (작은 단위로 작업을 나누기 때문에 코드의 효율성을 높일 수 있다.)
    • 재귀적 접근 (반복적인 문제를 효율적으로 풀 수 있다.)


함수의 장점은 이렇게 정리 할 수 있을 것 같다.
재귀적 접근은 후에 알아보고, 다른 장점들은 함수의 사용법을 보면 자연스럽게 느껴질 것 같다.

다양한 형태의 함수

우선 가장 기본적인 main()을 통해 함수의 기본 요소를 알아보자.

1
2
3
4
int main(void){
  ... //함수의 기능
  return 0;
}

main() 함수는 C 프로그램에서 시작점이 되는 특별한 함수이다.

1
2
3
4
5
int -> 반환 자료형
main -> 함수 이름
(void) -> 입력 자료형
{ ... } -> 함수의 기능
return 0; -> 반환 값

함수의 구조를 나눠서 보면 위와 같다. 이번에는 두 정수를 더하는 함수를 만들어보자.

1
2
3
4
5
int sum(int x, int y){
  int result;
  result = x+y;
  return result;
}
1
2
3
4
5
int -> 반환 자료형
sum -> 함수 이름
(int x, int y) -> 입력 자료형
{ ... } -> 함수의 기능
return result; -> 반환 값

입력 형태에 이번에는 두 개의 변수가 생겼다.
이를 매개 변수라고 부르고, 두 개 이상이 필요하면 ,로 구분한다.
함수의 이름은 sum이고 두 개의 정수를 더하고 있다.
그 다음에 반환하는 값이 x,y 두 개의 정수를 더한 result이고 그 자료형은 맨 윗줄의 int이다.

함수가 대충 이렇게 생겼다는 것을 알고 한 번 실행시켜보면

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int sum(int x, int y) {
    int result;
    result = x + y;
    return result;
}
int main() {
    int ans = 0;
    ans = sum(3, 4);
    printf("%d", ans);
    return 0;
}
1
7

라는 결과가 잘 나온다.
순서를 보면 main함수에서 시작해 sum()이라는 함수를 호출한다.

그 때 매개 변수로 3과 4를 넘겨주면 int sum(int x, int y)의 x, y에 각각 3, 4가 저장된다.
그 후 sum()에서 계산이 되어 result = 7이 된 다음 return result를 통해 다시 main으로 돌아온다.
반환값인 7이 main의 ans에 저장되고 ans를 출력하며 프로그램을 종료한다.
함수를 호출하면, 매개 변수를 가지고 그 함수를 실행한 다음, return을 통해 값을 반환하며 돌아온다.

함수의 형태

함수의 형태는 출력이 있는지 없는지, 입력이 있는지 없는지로 나눌 수 있다.
int main(void)라는 함수는 입력 값이 없는 함수라고 볼 수 있다.
void란 아무것도 없는 이라는 뜻을 가지고 있다.

함수의 입출력이 있는지 확인하는 방법은 반환 자료형과 입력 자료형이 무엇인지 보면 된다.
없으면 void 또는 비어있을 것이고, 있다면 필요에 따라 자료형을 써줘야 한다.

  • 출력이 있는 경우
    • 입력이 있는 경우 : int sum(int x, int y)
    • 입력이 없는 경우 : int input(void)
  • 출력이 없는 경우
    • 입력이 있는 경우 : void print(int x)
    • 입력이 없는 경우 : void output(void)

이렇게 4가지로 구별할 수 있다.
return이란 값을 반환하고 함수를 종료하라는 의미이기에 출력이 있다면 return 문이 꼭 필요하다.
하지만 출력 형태가 void라면 반환 값이 없기에 return 문은 없어도 상관없다.

함수의 선언 위치

C언어는 위에서 아래로 컴파일 된다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main() {
    int ans = 0;
    ans = sum(3, 4);
    printf("%d", ans);
    return 0;
}
int sum(int x, int y) {
    int result;
    result = x + y;
    return result;
}

위에서 아래로 내려오며 main에서 sum()이라는 함수가 있는데 아직 sum() 함수는 정의되지 않았다.
그렇기에 사용할 함수들은 아까 sum()함수를 사용한 예시처럼 main()위에 정의를 해줘야한다.

이 문제를 해결하는 다른 편리한 방법도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int sum(int x, int y);
int main() {
    int ans = 0;
    ans = sum(3, 4);
    printf("%d", ans);
    return 0;
}
int sum(int x, int y) {
    int result;
    result = x + y;
    return result;
}

이렇게 main()위에 sum이라는 함수의 원형을 선언을 해주는 것이다.
이러면 컴파일럿가 sum() 함수를 보고 위에 선언되어 있는 것을 봤기에 아무 이상없이 실행된다.
함수를 여러개 사용할수록 무슨 기능인지, 어떤 코드가 있는지 알아보기가 힘들어진다.
가장 윗줄에 모든 함수들의 원형을 선언해주면 함수의 개수와 직관적으로 기능을 분석할 수 있다.

1
2
3
4
5
6
#include <stdio.h>
int sum(int x, int y);
int sub(int x, int y);
int multi(int x, int y);
int div(int x, int y);
...

이렇게 사용할 함수들을 다 선언해주는 코딩을 하자. 원형을 선언할 땐 마지막에 꼭 ;를 써야한다.

1
2
3
최근에 확인을 해보니 컴파일러 편의성이 좋아져서 순서를 안지켜도 실행이 되긴 한다.
자동으로 함수 프로토타입을 선언해준다고 한다니 세상이 좋아졌다.
그럼에도 가독성 면에서 원형을 맨 위에 선언해주는 것은 좋은 방법이기에 이렇게 하자.

함수의 return

함수에 있어서 return이라는 것은 무슨 의미일까?

  1. 값 반환 : 함수에서 어떤 값을 계산하고, 그 값을 호출한 위치로 반환한다.
  2. 함수 종료: 해당 함수의 실행을 종료한다.

이를 잘 이용해 조건문에서의 break처럼 return을 쓸 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
int find_posit(int size) {
    int arr[] = { -5, -2, -3, 4, 6 };
    for (int i = 0; i < size; i++) {
        if (arr[i] > 0) {
            return arr[i];
        }
    }
    return -1;
}
int main() {
    int result = find_posit(5);
    if (result == -1)printf("양수가 없습니다.\n");
    else printf("첫번째 양수: %d\n", result);
    return 0;
}

위의 코드는 배열에서 첫번째 양수를 찾는 것으로 배열의 요소에 대해 순차적으로 반복문을 돈다.
그러다 양수가 있으면 return arr[i]를 통해 바로 함수를 종료시키고, 반복문을 다 돌고 없으면 -1을 반환한다.
이렇게 return을 통해 다양한 기능을 만들어 볼 수도 있다.


함수를 선언하는 방법을 봤다. 다음 글에서는 더 자세히 알아보자.

This post is written by PRO.