C 언어 | 함수 | 함수 만들기

반복되는 처리는 함수로서 부품화할 수 있다.

특정 처리를 함수에 정리한다

지금까지 프로그램은 main() 함수에 작성해 왔다. 처음에 설명했듯이 main() 함수는 프로그램이 실행될 때에 최초에 호출되는 애플리케이션 진입점을 나타내는 특수한 함수이다. 우리는 필요한 경우 main() 이외의 함수를 만들 수 있다. 지금까지는 표준 함수로 정해져 있는 printf()와 scanf() 등의 함수를 사용하여 왔는데, 이러한 일부 기능을 정리한 함수를 직접 만들 수 있는 것이다.

프로그램에서 여러번 사용되는 처리 함수로 정리하여, 동일한 코드를 여러번 작성하는 번거로움에서 해방되어 프로그램 전체에 정합성을 갖게 할 수 있다. 이는 응용 프로그램의 설계에 있어서 매우 중요한 것이다. 처음에 약간 설명했지만, 함수를 정의하려면 다음과 같이 설명한다.

함수의 정의

반환자료형 함수명 (매개 변수 목록) {
  문장
  ...
}

기본적인 작성법은 main() 함수와 동일하다. 함수명은 변수와 동일하게 C 언어의 명명 규칙을 따르고 있으면 자유롭게 지정할 수 있다. 함수의 기능을 나타내어 알기 쉽게 의미있는 이름으로하는 것이 바람직하다. 반환 값과 매개 변수 목록은 “파라미터와 반환 값"에서 자세히 설명하겠지만, 이것들을 이용하면 함수간에 데이터를 교환할 수 있다. 이 장에서는 우선 함수는 다음과 같이 정의한다.

void 함수명() { ... }

이 함수는 값을 받지 않고 값을 돌려주지 않는 것을 나타낸다. 반환 값의 자료형으로 지정하는 void는 함수가 값을 반환하지 않는 것을 의미한다. 값을 반환하지 않는 함수는 return 키워드 값을 반환할 수 없다. 함수를 호출할 때는 printf()와 같은 표준 함수처럼 호출할 수 있다.

함수명();

한번 만든 함수는 여러번 호출할 수 있기 때문에, 프로그램의 재사용이 가능하다. 다음의 프로그램은 새로운 함수 Function()를 정의하고 main() 함수에서 이것을 호출하여 사용하고 있다.

코드1

#include <stdio.h>

void Function() {
 printf("Kitty on your lap\n");
}

int main() {
  Function();
 Function();
 return 0;
}

Function() 함수는 printf() 함수를 호출하여 화면에 문자를 표시하는 간단한 처리를 수행한다. main() 함수에서 Function() 함수를 두번 호출한다. 그 결과, 화면에는 “Kitty on your lap"이라는 문자가 두 줄에 걸쳐 표시된다. 그러나 함수는 사용되는 함수보다 먼저 기술해야 합니다. 코드1을 보고 알 수 있듯이, Function() 함수는 이를 호출하는 main() 함수보다 이전에 정의되어 있다.

왜 함수가 사용되기 이전의 위치로 지정해야 하냐면, 함수를 발견하기 전에 함수를 호출하면 컴파일러는 기본적으로 int 형의 반환 값을 가진 인수를 받지 않는 함수로 인식한다. 따라서 기본 형식이 아닌 함수를 정의보다 이전에 호출하고, 형의 불일치로 컴파일 에러가 되어 발생한다.

예를 들어, 다음 프로그램은 Function1()는 문제없이 호출할 수 있지만 Function2()는 오류이다.

int main() {
 Function1();
  Function2(); /*형식 불일치 오류*/
}
int Function1() {
  return 0;
}
void Function2() {}

Function1()는 반환 값이 int 형으로 매개 변수가 없는 함수이므로 정의가 호출보다 뒤에 있어도 호출과 같은 형태이므로 문제없이 호출할 수 있지만, Function2()는 반환 값이 void 형이므로 그 이전 호출과 형태가 다르다. 컴파일러는 함수를 다시 선언으로 간주하고, 형태가 다르기 때문에 오류를 발생시키는 것이다.

코드1의 흐름을 따르면, 먼저 main() 함수가 실행되고 Function() 함수가 호출된다. 함수가 호출되면 프로그램은 그 함수에 제어를 이동시킨다. 코드1의 경우 Function() 함수의 본체에 제어가 이동하게 된다. Function() 함수의 처리가 완료되면, 프로그램은 함수를 호출한 원래 위치에 제어를 리턴한다. 이 경우는 main() 함수로 돌아 오게 된다.

그림1 - 함수의 호출과 복귀

함수의 호출과 복귀

함수를 호출하면 함수의 처리가 완료되면 제어가 함수를 호출한 원래의 위치로 돌아오기 때문에 프로그램의 흐름은 결국 main() 함수로 돌아간다.

void 형의 반환 값을 갖는 함수는 값을 돌려 줄 필요가 없기 때문에 return 문을 생략할 수 있다. main() 함수는 int 형의 종료 코드를 시스템에 반환해야 하므로 return 문을 사용하여 값을 반환하지만 Function() 함수는 return 문을 사용하지 않는다. 그러나 다음과 같이 명시적으로 return 문을 사용하여 함수를 종료 할 수 있다. 그러나 값은 반환시키지 않기 때문에 식을 지정할 수는 없다.

void Function() {
  printf("Kitty on your lap\n");
 return;
}

특정 위치에서 함수를 끝내고 싶다면, return 문을 사용하여 제어를 반환할 수 있다.

함수는 몇겹씩 호출할 수 있습니다.main()함수가 Function1()함수를 호출하는, Function1()함수가 Function2()함수를 호출하는……라는 듯 함수에서 다른 함수를 몇겹으로 불러냈다고 해도 함수는 자신을 불러낸 장소에 제어를 되돌리는 성질이 있으므로 반드시 최고 수준의 제어, 즉 main()함수로 돌아갑니다.

함수는 몇번이나 겹쳐서 호출할 수 있다. main() 함수가 Function1() 함수를 호출하고, Function1() 함수가 Function2() 함수를 호출 …… 등등 함수에서 다른 함수를 여러번 호출해도 함수는 자신을 호출한 곳으로 제어를 되돌리는 성질이 있으므로 반드시 최상위 제어, 즉 main() 함수로 복귀한다.

코드2

#include <stdio.h>

void Function2() {
 printf("Function2() : return\n");
}

void Function1() {
 printf("Function1() : Call Function2()\n");
  Function2();
  printf("Function1() : return\n");
}

int main() {
 printf("main() : Call Function1()\n");
 Function1();
  printf("main() : return\n");

 return 0;
}

코드2는 Function1() 함수에서 Function2() 함수를 호출한다. 이 프로그램은 제어의 흐름을 시각적으로 확인할 수 있도록 하기 위해, 각 함수는 자신의 함수명과 처리를 화면에 표시한다.

프로그램은 먼저 main() 함수에서 Function1() 함수를 호출하여 Function1() 함수는 Function2() 함수를 호출한다. Function2() 함수는 즉시 제어를 반환하기 때문에 Function1() 함수에 제어가 돌아간다. 그리고 Function1() 함수를 종료하고, 마지막으로 main() 함수에 복귀하고 있는 것을 확인할 수 있다. 표시된 결과를 보면 프로그램이 어떤 순서로 실행되고 있는지 이해할 수 있다.