함수의 정의와 호출
프로그램의 규모가 커지고 복잡해질수록 main
함수 하나에 모든 코드를 작성하는 것은 비효율적이고 비생산적입니다.
코드가 너무 길어져 이해하기 어렵고, 특정 기능을 여러 번 사용해야 할 때마다 코드를 복사/붙여넣기 해야 하는 문제가 발생합니다.
이러한 문제를 해결하고 코드를 구조화하며 재사용성을 높이는 데 필수적인 개념이 바로 함수(Function) 입니다.
이 장에서는 함수가 무엇인지, 어떻게 정의하고 호출하는지, 그리고 왜 함수를 사용해야 하는지에 대해 상세히 알아보겠습니다.
함수란 무엇인가?
함수는 특정 작업을 수행하는 코드 블록에 이름을 붙여놓은 것입니다.
함수는 필요할 때마다 이 이름을 호출하여 해당 작업을 반복적으로 수행할 수 있게 합니다.
함수의 주요 목적
- 코드 재사용(Code Reusability): 한 번 정의한 함수는 프로그램의 여러 곳에서 필요할 때마다 호출하여 사용할 수 있습니다. 중복 코드를 줄이고 개발 시간을 단축합니다.
- 모듈화(Modularity): 프로그램을 작은 기능 단위로 나누어 관리하기 쉽게 만듭니다. 각 함수는 독립적인 하나의 작업을 수행하므로, 프로그램 전체를 이해하기 전에 각 부분의 동작을 먼저 파악할 수 있습니다.
- 가독성 향상(Improved Readability): 복잡한 작업을 함수로 분리하면
main
함수나 다른 함수의 코드가 훨씬 간결하고 읽기 쉬워집니다. 함수 이름만 보고도 어떤 작업을 하는지 유추할 수 있습니다. - 유지보수 용이성(Easier Maintenance): 특정 기능에 문제가 발생하면 해당 함수만 수정하면 되므로, 전체 코드를 뒤질 필요가 없습니다. 기능 개선 시에도 마찬가지입니다.
함수의 구성 요소
모든 함수는 다음과 같은 기본적인 구성 요소를 가집니다.
반환타입 함수이름(매개변수타입1 매개변수이름1, 매개변수타입2 매개변수이름2, ...) {
// 함수 몸체 (Function Body)
// 함수가 수행할 작업이 정의되는 코드 블록
// 필요 시 return 문으로 값을 반환
}
- 반환 타입(Return Type): 함수가 작업을 수행한 후 호출자에게 돌려줄 값의 데이터 타입입니다. 만약 함수가 아무런 값도 반환하지 않는다면
void
키워드를 사용합니다. - 함수 이름(Function Name): 함수를 식별하는 고유한 이름입니다. (변수 명명 규칙과 유사)
- 매개변수 목록(Parameter List): 함수가 작업을 수행하는 데 필요한 외부로부터의 값들(인자)을 받아들이는 변수들의 목록입니다. 각 매개변수는 타입과 이름을 가집니다. 매개변수가 없으면 괄호
()
안에 아무것도 작성하지 않습니다. - 함수 몸체(Function Body): 중괄호
{}
안에 실제 함수가 수행할 작업의 코드가 작성됩니다. return
문:return
키워드를 사용하여 함수가 반환 타입에 해당하는 값을 호출자에게 돌려줍니다.void
함수는return
문을 생략하거나return;
으로만 사용할 수 있습니다.
함수의 정의
함수를 정의한다는 것은 함수가 어떤 작업을 수행할지 코드로 작성하는 것을 의미합니다.
#include <iostream>
// addNumbers 함수 정의
int addNumbers(int num1, int num2) { // 반환 타입: int, 함수 이름: addNumbers, 매개변수: int num1, int num2
int sum = num1 + num2; // 함수 몸체
return sum; // 계산 결과 반환
}
int main() {
// main 함수는 프로그램의 시작점이며, 다른 함수를 호출할 수 있습니다.
std::cout << "Hello, World!" << std::endl;
return 0;
}
위 코드에서 addNumbers
함수는 두 개의 int
형 매개변수(num1
, num2
)를 받아들여 이들을 더한 int
형 결과를 반환하도록 정의되었습니다.
함수의 호출
함수를 호출한다는 것은 정의된 함수를 실행시키도록 지시하는 것을 의미합니다.
함수를 호출할 때는 함수 이름 뒤에 괄호 ()
를 붙이고, 함수가 요구하는 인자(Arguments) 를 괄호 안에 순서대로 전달합니다.
#include <iostream>
// addNumbers 함수 정의 (위에 정의한 것과 동일)
int addNumbers(int num1, int num2) {
int sum = num1 + num2;
return sum;
}
// 메시지를 출력하고 아무것도 반환하지 않는 함수 정의
void printMessage(std::string msg) { // 반환 타입: void, 매개변수: std::string msg
std::cout << "메시지: " << msg << std::endl;
// void 함수이므로 return 문 생략 가능 또는 return; 만 사용 가능
}
int main() {
int a = 10;
int b = 20;
// addNumbers 함수 호출
int result = addNumbers(a, b); // a와 b의 값을 num1과 num2로 전달
std::cout << "두 수의 합: " << result << std::endl; // 출력: 두 수의 합: 30
// 직접 상수 값을 전달하여 함수 호출
std::cout << "5와 7의 합: " << addNumbers(5, 7) << std::endl; // 출력: 5와 7의 합: 12
// printMessage 함수 호출
printMessage("함수 호출은 재밌다!"); // 문자열 리터럴을 인자로 전달
printMessage("Learning C++ is great!");
return 0;
}
addNumbers(a, b)
:main
함수 내에서addNumbers
함수를 호출합니다.a
와b
의 값(10과 20)이addNumbers
함수의num1
과num2
매개변수로 각각 복사되어 전달됩니다. 함수가 반환하는 값(30)은result
변수에 저장됩니다.printMessage("함수 호출은 재밌다!")
:printMessage
함수를 호출합니다."함수 호출은 재밌다!"
라는 문자열 리터럴이msg
매개변수로 전달됩니다. 이 함수는void
타입이므로 반환하는 값이 없습니다.
함수 원형
C++ 컴파일러는 코드를 위에서 아래로 순차적으로 처리합니다.
따라서 함수를 호출하는 시점에서 해당 함수의 정의를 아직 보지 못했다면, 컴파일러는 그 함수가 어떤 모습으로 생겼는지 알 수 없어 오류를 발생시킵니다.
이 문제를 해결하기 위해, 함수를 호출하기 전에 함수의 원형(Function Prototype) 또는 함수 선언(Function Declaration) 을 미리 작성해 놓을 수 있습니다.
함수 원형은 컴파일러에게 "이러한 이름, 반환 타입, 매개변수를 가진 함수가 나중에 정의될 것이다"라고 미리 알려주는 역할을 합니다.
반환타입 함수이름(매개변수타입1, 매개변수타입2, ...); // 매개변수 이름은 생략 가능
- 함수 원형은 함수 정의의 첫 줄과 같지만, 함수 몸체
{}
가 없고 세미콜론(;
)으로 끝납니다. - 매개변수의 이름은 생략할 수 있지만, 매개변수의 타입은 반드시 명시해야 합니다.
#include <iostream>
// 함수 원형 (함수 정의보다 먼저 선언)
int multiplyNumbers(int x, int y); // 매개변수 이름은 있어도 되고 없어도 됨 (int, int)도 가능
// main 함수 (multiplyNumbers를 호출하기 전에 원형이 선언되어 있으므로 문제 없음)
int main() {
int result = multiplyNumbers(10, 3); // multiplyNumbers 함수 호출
std::cout << "두 수의 곱: " << result << std::endl; // 출력: 두 수의 곱: 30
return 0;
}
// multiplyNumbers 함수 정의 (main 함수 뒤에 있어도 원형 덕분에 호출 가능)
int multiplyNumbers(int x, int y) {
return x * y;
}
일반적으로 main
함수는 프로그램의 시작점이며, 다른 함수들을 조직하고 호출하는 역할을 하므로, 많은 경우 main
함수 뒤에 다른 함수들을 정의합니다.
이때 main
함수 이전에 모든 함수들의 원형을 선언하는 방식이 흔하게 사용됩니다.
이는 대규모 프로젝트에서 헤더 파일(.h
또는 .hpp
)을 사용하여 함수 원형을 분리하는 기초가 됩니다.
함수를 사용해야 하는 이유 다시 한번 강조
함수는 프로그램을 설계하고 개발하는 데 있어 핵심적인 도구입니다.
- 복잡성 관리: 거대한 문제를 작은 단위의 문제로 분해하여 해결할 수 있게 합니다.
- 협업 용이: 여러 개발자가 각자의 함수를 독립적으로 개발할 수 있습니다.
- 테스트 용이: 각 함수를 독립적으로 테스트하여 전체 프로그램의 안정성을 높일 수 있습니다.
따라서 C++ 프로그래밍에서 함수를 효과적으로 사용하는 것은 좋은 코드 품질과 효율적인 개발의 기초가 됩니다.