Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

함수

함수는 어떤 일을 처리하는 단위로 function 혹은 subroutine라고 부른다. Pascal(:12)과 같은 언어에서는 procedure라고 부르기도 하는데, 용어만 다를 뿐 의미하는 바는 같다. 함수는 특정한 연산을 encapsulate(캡슐화) 해서 프로그램(:12)을 구조적이고 단순하게 만들 수 있도록 도와 준다. 예를 들어서 당신이 만든 프로그램이 일정한 범위의 숫자를 더하는 연산을 여러번 한다고 가정해 보자. 그러면 다음과 같은 코드를 만들 수 있을 것이다.
1 int main()
2 {
3 	int start = 0;
4 	int end = 0;	
5 	int sum = 0;
6
7 	// 10부터 120 까지 더한다. 
8 	for (start = 10; start < 121; start++)
9 	{
10 		sum = sum+start;
11 	}
12 	printf("%d\n", sum);
}

프로그램을 만드는건 간단하다. 그렇지만 150부터 170까지 더하고, 또 1000부터 12000까지 더해서 결과를 출력해야 한다면, 어떡할 것인가. 물론 무식하게? 8번부터 11번까지를 for의 조건만 바꿔가면서 필요한 수만큼 copy & paste 하는 방법이 있겠지만, 코드의 길이도 길어지고, 난잡해질 것이다. 다음과 같이 함수를 이용하면 이러한 문제를 해결할 수 있다.

이 프로그램은 완전히 실행되는 프로그램이다. 이름은 rangesum.c로 하겠다.
#include <stdio.h>
int rangesum(int start, int end);

int main()
{
	printf("%d\n",rangesum(1, 100));
	printf("%d\n", rangesum(1000, 1500));
	printf("%d\n", rangesum(310, 5000));
}

int rangesum(int start, int end)
{
	int sum = 0; 
	for (start; start < end+1; start++)
	{
		sum = sum+start;
	}
	return sum; 
}
프로그램이 훨씬 깔끔해 졌음을 알 수 있다. 이렇게 자주 사용하는 코드등을 함수화 하면 프로그램을 유지보수하기 편해 지며, 프로그램에 문제가 생겨도 해당 함수만 수정하면 되기 때문에, 디버깅 하기도 훨씬 편해지게 된다.

함수 만들기

함수는 어떤 정보를 입력받아서 처리하고 그것에 대한 값을 돌려주는 일을 한다. 여기에서 우리는 함수를 만들기 위해서는 다음 3개의 구성요소가 필요함을 알 수 있다.
  • 입력 부
  • 처리부
  • 출력 부
함수의 모습은 다음과 같이 코드로 나타낼 수 있는데, 모든 함수는 아래의 기본적인 형태를 따른다.
return type FunctioNname(arg1, arg2, ....)
{
	// 실행 코드
	...
	...

	return Value; // 결과 값을 리턴한다.
}

입력부 : 함수 인자

함수는 어떤 일을 처리하기 위한 단위 코드 조각이다. 일을 처리하기 위해서는 처리할 데이터를 받아야 할 것이다. 이러한 처리해야 하는 데이터를 인자라고 한다. 여러분이 두개의 수를 비교해서 큰 수를 찾아내는 프로그램을 만들려고 한다면, 2개의 인자를 가지는 함수를 만들어야 할 것이다.
int diff(int a, int b)
{
	if ( a < b )
		return b;
	else
		return a;
}

각각의 인자는 처리해야 하는 데이터와 일치하는 타입이 명시되어야 한다.

출력부 : 함수값 리턴

인자로 주어진 데이터를 가지고 어떤일을 처리했다면, 함수를 호출한 함수에게 처리 값을 알려줘야 할 것이다. 이렇게 자신을 호출한 함수에게 알려주는 값을 리턴값이라고 하며, return 문을 이용해서 값을 리턴할 수 있다. 리턴하는 값은 함수의 return type과 반드시 일치해야만 한다. 사람이라면 대충 처리결과가 문자열인지, 숫자인지 알아낼 수 있지만 C 컴파일러는 이를 구분해낼 수 없기 때문이다.

#include <stdio.h>

int sum(int a, int b)
{
  return a+b;
}
int main()
{
  int rtv;
  int a = 20;
  int b = 10;
  rtv = sum(a, b);
  printf("%d + %d = %d\n", a, b, rtv);
}
sum 함수는 int형 인자 두개를 받아들인다음, 이것을 더한 값을 리턴한다. 위의 경우 30이 리턴될 것이다. 위 프로그램을 실행시키기 전에 실행결과를 미리 예측해보고, 예측과 실행결과가 맞았는지를 체크해보기 바란다.

void

때때로 리턴값이 필요 없는 함수도 있을 것이다. 예를 들어 이름을 입력하면 "안녕하세요 누구누구씨"라고 답변하는 함수가 있다고 하면, 굳이 자신을 호출한 함수에게 결과값을 넘겨줄 필요 없이, 인삿말을 출력하고 종료해도 될 것이다.

void비어있는 타입이란 뜻으로 함수의 리턴값을 void로 하면, 리턴값이 없는 함수를 만들 수 있다.
#include <stdio.h>

void hello(char *name)
{
	printf("안녕하세요 %s님\n", name);
}

int main()
{
	hello("yundream");
}
프로그램의 도움말을 출력하는 함수같은 경우에도 굳이 리턴값이 필요없으므로 void 형으로 작성해도 될 것이다.
void help()
{
	printf("Usage : ./test a b\n");
}

main 함수

main 은 특수한 함수로, 실행되는 모든 C로 작성된 프로그램은 반드시 하나의 main() 함수를 가지고 있어야 한다. 모든 함수는 main() 함수에서 시작된다. 선조뻘 되는 함수라고 볼수 있겠다.

main 함수역시 다음과 같이 리턴값과 인자들을 가질 수 있으며, 이들을 통해서 처리할 데이터를 입력받고, 처리된 결과를 리턴할 수 있다.

int main(int argc, char **argv)
{
  ...
  ...
}

다른 함수들이야 main 함수에서 호출되니까. main 함수 혹은 상위 함수로 부터 인자를 받을 수 있다지만 main함수는 어디에서 인자를 받아야 하는가.

main 함수는 실행하는 명령으로 부터 인자를 받는다. 그래서 함수인자를 받는다라고 하지 않고, 실행인자를 받는다고 한다. 2번째 인자에, 프로그램을 실행시킬시 주어지는 인자가 전달이 된다. 1번째 인자는 받아들인 인자의 갯수다.

예를들어 키우고 있는 동물의 목록을 입력받아서 출력하는 프로그램이 있다고 가정해보자. 이 프로그램의 이름은 hello_pat으로 안녕 개, 안녕 고양이 식으로 입력받은 동물의 이름을 출력한다. 쉘에서 다음과 같이 실행할 것이다.
# ./hello_pat 개 고양이 악어 
안녕 개
안녕 고양이
안녕 악어 

다음은 완성된 프로그램이다.
#include <stdio.h>

int main(int argc, char **argv)
{
  int i = 0;

  printf("동물의 수 : %d\n\n", argc);
  for (i = 0; i < argc; i++)
  {
    printf("안녕 %s\n", argv[i]);
  }
  return 0;
}

이 프로그램을 실행시켜 보자.
# ./hello_pat 개 고양이
동물의 수 : 3

안녕 ./hello_pat
안녕 개
안녕 고양이
공백문자를 기준으로 해서 인자의 목록을 받아들이는 것을 알 수 있다. 그런데, 버그가 발견되었다. 프로그램이름까지 실행인자로 포함되어서 안녕 ./hello_pat까지 출력되어 버렸다. 주의할 점인데, 프로그램의 실행인자는 프로그램자신까지를 포함한다. 그러므로 배열의 0 번째는 프로그램이름이 들어간다. 아래는 버그를 수정한 프로그램이다.
  printf("동물의 수 : %d\n\n", argc - 1);
  for (i = 1; i < argc; i++)
  {
    printf("안녕 %s\n", argv[i]);
  }
여러분은 아직 배열(:12)과 포인터(:12)를 배우지 않았기 때문에 main 함수의 인자인 char **argv가 의미하는 바를 명확히 알지 못할 것이다. 배열과 포인터는 다음장에서 다룰 것이다. 우선은 목록을 저장하기 위한 데이터 단위정도로만 알고 넘어가도록 하자.

int main ?

함수의 리턴값이 자기를 호출한 함수에게 결과값을 넘겨주기 위해서 필요하다는 것을 이해했을 것이다. 그렇다면 main 함수의 리턴값은 어떻게 설명해야 할까. main은 가장 상위에 있는 함수인데, int main 이라면 누구에게 값을 넘겨줄 건지가 궁금할 것이다.

main의 리턴값은 자신을 실행시킨 프로세스에게 넘겨진다. 보통은 shell(:12)에서 프로그램을 실행시킬테니, 실행 shell로 값이 리턴된다. bash:::shell(:12)을 사용하고 있다면 다음과 같이 리턴값을 확인해 볼 수 있다.

main 함수의 리턴값이 정말 전달되는지 확인해보자. 아래와 같은 간단한 코드를 만들고 main_return.c 로 저장한 후 컴파일 해보자.
int main(int argc, char **argv)
{
  return 2;
}

이제 실행시키면, main 함수의 리턴값 2가 출력되는걸 확인할 수 있을 것이다. main 함수의 리턴값은 프로그램이 종료될때 되돌려지는 값이므로, 리턴값 이란 용어대신 종료값이란 용어를 사용한다. 앞으로는 종료값이란 용어를 사용하도록 하겠다.
# ./main_return
# echo $?
2

$? 는 bash:::shell(:12)에서 최근실행 시킨 프로그램의 종료값을 저장하고 있는 특수변수다.

main 함수의 종료값은 프로그램이 어떻게 종료되었는지를 확인하기 위한 좋은 방법을 제공한다. 전통적으로 Unix(:12)에서는 프로그램이 주어진 일을 제대로 해냈다면 0을 그렇지 않다면, 0보다 큰값을 리턴하도록 하고 있다. 다음은 프로그램의 리턴값을 이용해서 프로그램을 실행하는 예이다. 쉘스크립트(:12)로 작성되었는데, 이해하는데에는 크게 어려움이 없을 것이다.
#!/bin/bash
which mutt 2>&1>&/dev/null

echo -n "Default Mail Client is "
if [ $? = 0 ]
then
    echo "mutt"
else
    echo "pine"
fi
which(1)는 실행 프로그램이 존재하는지를 확인하는 프로그램이다. 만약 프로그램이 존재한다면 0이 리턴되고, 그렇지 않다면 0보다 큰 값이 리턴된다. 위 스크립트 프로그램은 이러한 특성을 이용해서 which로 mutt프로그램이 있는지 확인하는 일을 한다. 만약 mutt 가 존재한다면 echo "mute"가 실행되고, 그렇지 않다면 echo "pine"이 실행된다.

exit 함수

exit(2)함수는 프로그램을 종료시키기 위해서 사용하는 함수다. 어떤 위치에서든지 exit 함수를 호출하면 프로그램은 그 즉시 종료된다. exit 함수는 하나의 int형 인자를 받아들이는데, 이 인자값은 프로그램의 종료값으로 사용된다.
int main(int argc, char **argv)
{
	exit(2);	
}
컴파일 후 echo $? 를 실행하면 2가 출력되는걸 확인할 수 있을 것이다.

문제

  1. 입력된 숫자의 제곱구하기
하나의 숫자를 입력하면 제곱한 결과를 리턴하는 프로그램을 작성하라. 제곱하는 코드는 함수형태로 작성되어야 한다.
  1. int형 값을 리턴하는 함수가 있다. 그런데 함수에서 아무것도 리턴하지 않으면 어떻게 될까 ?
  2. 프로그램 중간에 종료시키고 빠져나올려면 어떻게 해야 할까 ?
  3. main 함수의 return 값을 어떤용도로 사용할 수 있을지 생각해 보자.

댓글