C 프로그래밍에서 함수는 프로그램을 이해하기 쉽고, 테스트하기 용이하며, 호출 프로그램을 변경하지 않고도 쉽게 수정할 수 있게 하는 재사용 가능한 코드 블록입니다. 함수는 코드를 분할하고 프로그램을 모듈화하여 더 좋고 효과적인 결과를 만듭니다. 간단히 말해, 큰 프로그램을 여러 하위 프로그램으로 나누는데 이를 함수라고 부릅니다.
큰 프로그램을 여러 함수로 나누면 각 함수를 개별적으로 관리하기 쉬워집니다. 프로그램에서 오류 발생 시 결함이 있는 함수를 쉽게 조사하고 해당 오류만 수정할 수 있습니다. 함수가 필요할 때마다 쉽게 호출하여 사용할 수 있어 시간과 공간을 자동으로 절약하게 됩니다.
이 튜토리얼에서 배울 내용:
라이브러리 vs 사용자 정의 함수
함수 선언
함수 정의
함수 호출
함수 인수
변수 범위
정적 변수
재귀 함수
인라인 함수
라이브러리 vs 사용자 정의 함수
모든 C 프로그램에는 최소 하나인 main 함수가 있지만, 프로그램은 임의의 수의 함수를 가질 수 있습니다. C에서 main() 함수는 프로그램의 시작점입니다.
C 프로그래밍에서 함수는 두 종류로 나뉩니다:
라이브러리 함수
사용자 정의 함수
라이브러리와 사용자 정의 함수의 차이는 라이브러리 함수는 코드를 작성할 필요가 없다는 점입니다. 프로그램 처음에 항상 포함하는 헤더 파일 안에 이미 들어 있습니다. 함수 이름과 올바른 문법만으로 사용하면 됩니다. printf, scanf가 라이브러리 함수 예시입니다.
반면, 사용자 정의 함수는 함수 본문을 직접 작성하고 프로그램에서 해당 연산이 필요할 때 함수를 호출하는 유형입니다.
C에서 사용자 정의 함수는 항상 사용자가 작성하지만, 나중에는 C 라이브러리의 일부가 될 수 있습니다. 이는 C 프로그래밍의 주요 장점입니다.
C 프로그래밍 함수는 세 가지 활동으로 나뉩니다:
함수 선언
함수 정의
함수 호출
함수 선언(Function Declaration)
함수 선언이란 프로그램에서 사용할 함수 이름을 쓰는 것입니다. 코드에서 함수를 사용하기 위한 필수 부분입니다. 함수 선언 시 변수 선언처럼 사용할 함수 이름만 지정합니다. 프로그램에서 함수가 선언되지 않으면 사용할 수 없습니다. 함수 선언을 '함수 프로토타입(prototype)'이라고도 합니다.
함수 선언(프로토타입)은 보통 main() 함수 위에 하며 일반적인 형태:
반환_데이터_타입 함수_이름 (데이터_타입 인수);
반환_데이터_타입: 함수가 호출 문장으로 반환하는 값의 데이터 타입
함수_이름: 괄호가 따름
인수 이름과 데이터 타입 선언이 선택적으로 괄호 안에 위치
정수 변수의 세제곱 값을 계산하는 cube 함수 선언 예시:
#include <stdio.h>
/* 함수 선언 */
int add(int a,b);
/* 함수 선언 끝 */
int main() {
함수가 반드시 값을 반환할 필요는 없습니다. 이 경우 void 키워드 사용.
예를 들어, output_message 함수 선언은 함수가 값을 반환하지 않음을 나타냅니다: void output_message();
함수 정의(Function Definition)
함수 정의란 함수 본문을 작성하는 것입니다. 함수 본문은 특정 작업을 수행할 문장들로 구성됩니다. 함수 본문은 단일 문장 또는 문장 블록으로 이루어집니다. 이것도 함수의 필수 부분입니다.
int add(int a,int b) // 함수 본문
{
int c;
c=a+b;
return c;
}
함수 호출(Function Call)
함수 호출이란 프로그램에서 필요할 때 함수를 부르는 것입니다. 함수를 호출하면 그 함수가 설계된 연산을 수행합니다. 함수 호출은 프로그램에서 선택적 부분입니다.
result = add(4,5);
전체 코드는 다음과 같습니다:
#include <stdio.h>
int add(int a, int b); // 함수 선언
int main() {
int a=10,b=20;
int c=add(10,20); // 함수 호출
printf("Addition:%d\n",c);
getch();
}
int add(int a,int b) // 함수 본문
{
int c;
c=a+b;
return c;
}
출력:
Addition:30
함수 인수(Function Arguments)
함수의 인수는 함수 호출에 의해 필요한 값들을 함수로 전달받는 데 사용됩니다. 위치에 따라 매칭되며, 첫 번째 인수는 첫 번째 매개변수로, 두 번째는 두 번째 매개변수로 전달됩니다.
기본적으로 인수는 값에 의해 전달(pass by value)되며, 데이터의 사본이 호출된 함수에 주어집니다. 실제로 전달된 변수는 변경되지 않습니다.
값에 의한 전달을 보여주는 프로그램:
int add (int x, int y);
int main() {
int a, b, result;
a = 5;
b = 10;
result = add(a, b);
printf("%d + %d = %d\n", a, b, result);
return 0;
}
int add (int x, int y) {
x += y;
return(x);
}
프로그램 출력:
5 + 10 = 15
add 함수로 전달된 a, b 값은 변경되지 않았습니다. 값만 매개변수 x로 복사되었기 때문입니다.
변수 범위(Variable Scope)
변수 범위란 프로그램 코드 내에서 변수의 가시성을 의미합니다.
C에서 함수 내부에 선언된 변수는 그 코드 블록에 지역적(local)이며 함수 외부에서 참조할 수 없습니다. 그러나 모든 함수 외부에 선언된 변수는 전역(global)이며 프로그램 전체에서 접근 가능합니다. 프로그램 상단의 #define으로 선언된 상수는 프로그램 전체에서 접근 가능합니다. 다음 프로그램은 main과 사용자 정의 함수 양쪽에서 전역 변수 값을 출력합니다:
#include <stdio.h>
int global = 1348;
void test();
int main() {
printf("from the main function : global =%d \n", global);
test () ;
return 0;
}
void test (){
printf("from user defined function : global =%d \n", global);
}
결과:
from the main function : global =1348
from user defined function : global =1348
프로그램 세부 사항:
정수 전역 변수를 1348 초기값으로 선언
인수도 받지 않고 값도 반환하지 않는 test() 함수 선언 및 정의. 이 함수는 전역 변수 값을 출력하여 전역 변수가 프로그램 어디서든 접근 가능함을 보여줌
main 함수 내에서 전역 변수 출력
test 함수 호출하여 전역 변수 값 출력
C에서 인수가 함수 매개변수로 전달될 때, 매개변수는 함수를 빠져나갈 때 소멸하는 지역 변수처럼 동작합니다.
전역 변수를 사용할 때는 주의해야 합니다. 어디서든 변경될 수 있어 오류를 유발할 수 있기 때문입니다. 사용 전 초기화해야 합니다.
정적 변수(Static Variables)
정적 변수는 지역 범위를 가집니다. 그러나 함수를 빠져나갈 때 소멸하지 않습니다. 따라서 정적 변수는 값을 영구히 유지하며 함수가 재진입될 때 접근할 수 있습니다. 정적 변수는 선언 시 초기화되며 static 접두사가 필요합니다.
정적 변수를 사용하는 다음 프로그램:
#include <stdio.h>
void say_hi();
int main() {
int i;
for (i = 0; i < 5; i++) {
say_hi();
}
return 0;
}
void say_hi() {
static int calls_number = 1;
printf("Hi number %d\n", calls_number);
calls_number ++;
}
프로그램 출력:
Hi number 1
Hi number 2
Hi number 3
Hi number 4
Hi number 5
재귀 함수(Recursive Functions)
숫자의 팩토리얼은 다음과 같이 계산됩니다: 6! = 6 * 5 * 4 * 3 * 2 * 1.
이 계산은 fact * (fact - 1)을 fact가 1이 될 때까지 반복하여 수행됩니다.
재귀 함수는 자기 자신을 호출하는 함수이며, 재귀 호출을 끝내기 위한 종료 조건(exit condition)을 포함합니다. 팩토리얼 계산의 경우 종료 조건은 fact가 1이 되는 것입니다. 재귀는 종료 조건이 참이 될 때까지 호출을 '스택(stacking)'하여 작동합니다.
예시:
#include <stdio.h>
int factorial(int number);
int main() {
int x = 6;
printf("The factorial of %d is %d\n", x, factorial(x));
return 0;
}
int factorial(int number) {
if (number == 1)
return (1); /* 종료 조건 */
else
return (number * factorial(number - 1));
}
프로그램 출력:
The factorial of 6 is 720
프로그램 세부 사항:
정수 매개변수를 받아 이 매개변수의 팩토리얼을 반환하는 재귀 팩토리얼 함수 선언. 이 함수는 자기 자신을 호출하며 숫자를 감소시키다가 종료 조건(베이스 케이스)에 도달. 조건이 참이면 이전에 생성된 값들이 서로 곱해지고 최종 팩토리얼 값 반환
값 "6"으로 정수 변수 선언 및 초기화 후 우리 팩토리얼 함수를 호출하여 팩토리얼 값 출력
다음 차트를 보면 재귀 메커니즘을 더 잘 이해할 수 있습니다. 함수가 자기 자신을 호출하다가 베이스 케이스 또는 정지 조건에 도달하고, 그 후 이전 값들을 수집합니다:
인라인 함수(Inline Functions)
C 프로그래밍에서 함수는 가장 자주 사용되는 명령어를 저장하는 데 쓰입니다. 프로그램을 모듈화하는 데 사용됩니다.
함수가 호출될 때마다 명령어 포인터가 함수 정의로 점프합니다. 함수 실행 후 명령어 포인터는 함수 정의로 점프했던 문장으로 돌아옵니다.
함수를 사용할 때마다 함수 정의로 점프하고 돌아오는 추가 포인터 헤드가 필요합니다. 이런 포인터 헤드 필요성을 없애기 위해 인라인 함수를 사용합니다.
인라인 함수에서 함수 호출은 실제 프로그램 코드로 직접 대체됩니다. 모든 연산이 인라인 함수 내부에서 수행되므로 아무 블록으로도 점프하지 않습니다.
인라인 함수는 주로 작은 계산에 사용됩니다. 큰 연산이 관여될 때는 적합하지 않습니다.
인라인 함수는 일반 함수와 비슷하지만 함수 이름 앞에 inline 키워드가 위치합니다. 인라인 함수는 다음 문법으로 생성:
inline 함수_이름() {
// 함수 정의
}
인라인 함수를 구현하는 프로그램을 작성해 봅시다.
inline int add(int a, int b) // 인라인 함수 선언
{
return(a+b);
}
int main() {
int c=add(10,20);
printf("Addition:%d\n",c);
getch();
}
출력:
Addition: 30
위 프로그램은 두 숫자 덧셈을 위한 인라인 함수 사용을 보여줍니다. 보다시피 인라인 함수 내에서 두 숫자의 덧셈 결과를 바로 반환하며 추가 라인 없이 처리됩니다. 함수 호출 시 덧셈을 수행할 값만 전달했습니다.