5편에서 조건문과 반복문으로 흐름을 바꾸는 방법을 익혔다면, 이제 그 흐름을 기능 단위로 묶을 차례입니다. 함수는 "입력 -> 처리 -> 출력" 패턴을 이름으로 감싸 재사용할 수 있게 해 줍니다. 이번 글에서는 함수 선언과 정의를 나누고, 매개변수와 반환값을 어떻게 설계하는지 살펴봅니다.
이번 글에서 새로 나오는 용어
- 함수 선언 (Function Declaration): 함수가 어떤 이름, 매개변수, 반환형을 갖는지 먼저 알려 주는 문장
- 함수 정의 (Function Definition): 실제 동작을 구현한 코드 블록
- 매개변수 (Parameter): 함수가 호출될 때 받는 입력 값 자리
- 반환값 (Return Value): 함수가 계산을 마치고 바깥으로 돌려주는 값
- 함수 원형 (Function Prototype):
main위쪽에 적어 컴파일러가 함수 정보를 미리 알게 하는 선언
핵심 개념
학습 메모
- 소요 시간: 50~60분
- 준비물: 조건문·반복문 예제를 편하게 수정할 수 있는 상태
- 학습 목표: 함수 선언·정의·호출 구조를 이해하고, 매개변수와 반환값을 설계하기
함수는 반복되는 코드를 이름 붙여 묶는 도구입니다. 복잡한 흐름을 작은 이름표로 나누면 한 번만 쓰더라도 "무엇을 한다"는 설명을 이름에 담을 수 있어 읽기가 쉬워집니다. 함수는 아래 다섯 가지 요소를 가진다고 보면 됩니다.
- 반환형: 계산 결과의 자료형 (
int,void,double등) - 함수 이름:
print_line,calculate_total같은 동사 기반 이름이 좋습니다. - 매개변수 목록: 함수가 필요로 하는 입력 값들
- 본문: 중괄호로 감싸 실제 동작을 적는 부분
- return 문: 반환형이
void가 아니라면 반드시 값을 돌려줘야 합니다.
코드로 따라하기
가장 작은 함수 만들기
#include <stdio.h>
void print_separator(void) {
printf("----------\n");
}
int main(void) {
print_separator();
printf("C 함수 학습 시작\n");
print_separator();
return 0;
}
print_separator는 반환값이 없으므로 void를 사용합니다. main 안에서 이름만 불러도 해당 코드가 실행됩니다. 이렇게 반복되는 출력 코드를 함수 하나로 묶으면 실수를 줄이고 의도를 분명하게 표현할 수 있습니다.
매개변수로 입력 받기
#include <stdio.h>
int add_two_numbers(int a, int b) {
return a + b;
}
int main(void) {
int result = add_two_numbers(12, 30);
printf("결과: %d\n", result);
return 0;
}
a, b는 함수 안에서만 의미가 있습니다. 호출할 때 add_two_numbers(12, 30)처럼 값을 넘기면, 그 순간에만 a, b가 채워지고 함수가 끝나면 사라집니다. 이 덕분에 같은 함수를 다양한 입력으로 반복해서 사용할 수 있습니다.
반환값으로 결과 돌려주기
#include <stdio.h>
double average_score(int sum, int count) {
if (count == 0) {
return 0.0;
}
return (double)sum / count;
}
int main(void) {
double avg = average_score(273, 3);
printf("평균: %.2f\n", avg);
return 0;
}
반환형이 double이므로 return 문에서도 double 값을 돌려줘야 합니다. 매개변수와 반환형이 다르면 컴파일 단계에서 경고나 오류가 발생할 수 있으니, 타입을 맞추는 습관이 필요합니다.
함수 원형으로 선언과 정의 분리하기
#include <stdio.h>
double celsius_to_fahrenheit(double c); // 함수 원형
int main(void) {
double result = celsius_to_fahrenheit(24.0);
printf("섭씨 24도 = 화씨 %.1f도\n", result);
return 0;
}
double celsius_to_fahrenheit(double c) {
return (c * 9.0 / 5.0) + 32.0;
}
함수 정의를 main 아래에 작성하고 싶다면, 위쪽에 함수 원형을 적어 컴파일러에게 미리 알려야 합니다. 이렇게 해야 컴파일러가 호출 시점에 매개변수 개수와 반환형을 점검할 수 있습니다. 나중에 여러 파일로 나눌 때는 이 원형이 헤더 파일로 옮겨갑니다.
실전 예시: 성적 요약 함수 묶기
#include <stdio.h>
int is_pass(int score) {
return score >= 60;
}
void print_report(int score) {
if (is_pass(score)) {
printf("점수 %d: 합격\n", score);
} else {
printf("점수 %d: 보충 학습\n", score);
}
}
int main(void) {
print_report(85);
print_report(42);
print_report(73);
return 0;
}
is_pass는 값을 판단해서 1 또는 0을 반환합니다. print_report는 그 결과를 활용해 메시지를 출력합니다. 이렇게 작은 함수들을 조합하면 조건문과 반복문이 더 읽기 쉬워집니다.
왜 중요한가
- 함수는 반복되는 조건·반복 구조를 이름으로 묶어 버그를 줄입니다.
- 매개변수와 반환값을 명확히 하면 테스트가 쉬워지고, 잘못된 입력을 방어하기 좋습니다.
- 선언과 정의를 분리하는 습관은 이후 여러
.c파일과 헤더 파일을 다룰 때 필수입니다. - 함수 단위로 사고하면 디버깅 시 "어느 기능에서 문제가 생겼는가"를 더 쉽게 좁힐 수 있습니다.
CodeSandbox로 이어서 실습하기
아래 샌드박스는 CodeSandbox의 Universal starter입니다. C는 터미널에서 직접 컴파일하고 다시 실행하는 흐름이 중요하니, 이번 글의 코드를 파일로 만들고 빌드-실행 사이클을 다시 따라가 보세요.
💬 댓글
이 글에 대한 의견을 남겨주세요