함수와 재귀
2025. 3. 27. 23:43ㆍProgramming Languages/C++
함수와 재귀
함수는 C++ 프로그래밍의 핵심 요소로, 코드의 모듈화, 재사용성, 가독성을 향상시킵니다. 이 챕터에서는 함수의 기본 개념부터 고급 기능까지 심층적으로 살펴보겠습니다.
4.1 함수의 기본
4.1.1 함수의 구조
C++에서 함수는 다음과 같은 구조로 이루어져 있습니다:
반환_타입 함수_이름(매개변수_목록) {
// 함수 본문
return 반환_값; // 있는 경우
}
예제: 간단한 함수
#include <iostream>
// 두 정수의 합을 반환하는 함수
int add(int a, int b) {
int sum = a + b;
return sum;
}
int main() {
int result = add(5, 3);
std::cout << "5 + 3 = " << result << std::endl;
return 0;
}
함수의 주요 구성 요소:
- 반환 타입(Return Type): 함수가 작업을 완료한 후 반환하는 값의 데이터 타입
- 함수 이름(Function Name): 함수를 식별하는 고유한 이름
- 매개변수 목록(Parameter List): 함수가 받는 입력 값들
- 함수 본문(Function Body): 함수가 수행하는 작업을 정의하는 코드 블록
- 반환 문(Return Statement): 함수의 결과를 호출자에게 돌려주는 구문
4.1.2 함수 선언과 정의
C++에서는 함수 선언(Declaration)과 정의(Definition)를 분리할 수 있습니다:
- 함수 선언(프로토타입): 함수의 인터페이스(반환 타입, 이름, 매개변수 타입)만 컴파일러에 알려줍니다.
- 함수 정의: 함수의 실제 구현 코드를 포함합니다.
예제: 함수 선언과 정의 분리
#include <iostream>
// 함수 선언 (프로토타입)
int multiply(int x, int y);
int main() {
int result = multiply(4, 5);
std::cout << "4 × 5 = " << result << std::endl;
return 0;
}
// 함수 정의
int multiply(int x, int y) {
return x * y;
}
함수 선언의 장점:
- 컴파일러가 함수 호출을 검증할 수 있음
- 함수 정의 순서에 상관없이 함수 사용 가능
- 코드 조직화 및 헤더 파일 활용에 유용
4.1.3 반환 타입(Return Type)
함수는 다양한 타입의 값을 반환할 수 있습니다:
기본 타입 반환
int getAge() {
return 25;
}
double getPI() {
return 3.14159265359;
}
char getGrade() {
return 'A';
}
bool isValid() {
return true;
}
void 반환 타입
반환 값이 없는 함수는 void 타입을 사용합니다:
void printMessage(std::string message) {
std::cout << message << std::endl;
// return 문 필요 없음
}
배열 반환
C++에서 함수는 직접 배열을 반환할 수 없지만, 포인터나 참조를 사용하거나 구조체/클래스를 통해 간접적으로 반환할 수 있습니다:
#include <iostream>
#include <array>
// 정적 배열을 사용할 경우
int* getArray() {
static int arr[5] = {1, 2, 3, 4, 5}; // static 키워드 필요
return arr;
}
// std::array 사용 (권장)
std::array<int, 5> getModernArray() {
return {1, 2, 3, 4, 5};
}
int main() {
int* arr = getArray();
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
auto modernArr = getModernArray();
for (int num : modernArr) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
문자열 반환
#include <iostream>
#include <string>
std::string getGreeting(const std::string& name) {
return "Hello, " + name + "!";
}
int main() {
std::string greeting = getGreeting("Alice");
std::cout << greeting << std::endl; // Hello, Alice!
return 0;
}
4.1.4 매개변수(Parameters)
함수에 데이터를 전달하는 방법입니다:
값에 의한 전달(Pass by Value)
void incrementByValue(int x) {
x++; // 매개변수 x의 복사본 수정
std::cout << "함수 내부 x: " << x << std::endl;
}
int main() {
int num = 5;
incrementByValue(num);
std::cout << "함수 호출 후 num: " << num << std::endl; // 여전히 5
return 0;
}
참조에 의한 전달(Pass by Reference)
void incrementByReference(int& x) {
x++; // 원본 변수 직접 수정
std::cout << "함수 내부 x: " << x << std::endl;
}
int main() {
int num = 5;
incrementByReference(num);
std::cout << "함수 호출 후 num: " << num << std::endl; // 6으로 변경됨
return 0;
}
상수 참조(Const Reference)
참조로 전달하되 수정은 방지하는 방법입니다:
void printInfo(const std::string& str) {
// str = "변경"; // 컴파일 오류: 상수 참조는 수정 불가
std::cout << "문자열 길이: " << str.length() << std::endl;
}
int main() {
std::string message = "Hello, World!";
printInfo(message); // 복사 없이 효율적으로 전달
return 0;
}
포인터에 의한 전달(Pass by Pointer)
void incrementByPointer(int* ptr) {
if (ptr != nullptr) { // null 체크 중요
(*ptr)++; // 역참조하여 값 수정
}
}
int main() {
int num = 5;
incrementByPointer(&num); // 주소 전달
std::cout << "함수 호출 후 num: " << num << std::endl; // 6
return 0;
}
다양한 타입의 매개변수
#include <iostream>
#include <string>
#include <vector>
void displayInfo(std::string name, int age, double height, bool isStudent) {
std::cout << "이름: " << name << std::endl;
std::cout << "나이: " << age << "세" << std::endl;
std::cout << "키: " << height << "cm" << std::endl;
std::cout << "학생 여부: " << (isStudent ? "예" : "아니오") << std::endl;
}
// 벡터 매개변수
double getAverage(const std::vector<int>& numbers) {
if (numbers.empty()) {
return 0.0;
}
double sum = 0.0;
for (int num : numbers) {
sum += num;
}
return sum / numbers.size();
}
int main() {
displayInfo("홍길동", 25, 175.5, true);
std::vector<int> scores = {85, 92, 78, 90, 88};
std::cout << "평균 점수: " << getAverage(scores) << std::endl;
return 0;
}
4.1.5 기본 매개변수(Default Parameters)
매개변수에 기본값을 지정하여 선택적 인자로 만들 수 있습니다:
#include <iostream>
#include <string>
// 모든 기본 매개변수는 오른쪽에서부터 지정해야 함
void printMessage(std::string message, int count = 1, bool addNewLine = true) {
for (int i = 0; i < count; i++) {
std::cout << message;
if (addNewLine) {
std::cout << std::endl;
}
}
}
int main() {
printMessage("Hello"); // 기본값 사용: count=1, addNewLine=true
printMessage("Hi", 3); // 기본값 사용: addNewLine=true
printMessage("No newline", 1, false); // 모든 매개변수 지정
return 0;
}
주의사항:
- 기본 매개변수는 함수 선언에만 지정해야 함 (정의에는 반복하지 않음)
- 모든 기본 매개변수는 오른쪽에서부터 배치해야 함
4.1.6 함수 오버로딩(Function Overloading)
같은 이름의 함수를 매개변수 타입이나 개수를 다르게 하여 여러 개 정의할 수 있습니다:
#include <iostream>
#include <string>
// 정수 두 개 더하기
int add(int a, int b) {
std::cout << "정수 버전 호출" << std::endl;
return a + b;
}
// 부동 소수점 두 개 더하기
double add(double a, double b) {
std::cout << "실수 버전 호출" << std::endl;
return a + b;
}
// 세 개의 정수 더하기
int add(int a, int b, int c) {
std::cout << "세 정수 버전 호출" << std::endl;
return a + b + c;
}
// 문자열 연결
std::string add(const std::string& a, const std::string& b) {
std::cout << "문자열 버전 호출" << std::endl;
return a + b;
}
int main() {
std::cout << add(5, 3) << std::endl; // 정수 버전
std::cout << add(3.5, 2.5) << std::endl; // 실수 버전
std::cout << add(1, 2, 3) << std::endl; // 세 정수 버전
std::cout << add("Hello, ", "World!") << std::endl; // 문자열 버전
return 0;
}
함수 오버로딩의 규칙:
- 반환 타입만 다른 경우는 오버로딩 불가
- 기본 매개변수만 다른 경우도 오버로딩 불가 (모호성 발생)
- 함수 이름과 매개변수의 타입/개수로 함수를 구분
4.1.7 인라인 함수(Inline Functions)
작은 함수를 호출 오버헤드 없이 실행하기 위한 최적화 기법입니다:
#include <iostream>
// 인라인 함수 선언
inline int max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
int x = 5, y = 7;
// 컴파일러는 함수 호출 대신 코드를 직접 삽입할 수 있음
// 아래 코드는 다음과 같이 처리될 수 있음: int result = (x > y) ? x : y;
int result = max(x, y);
std::cout << "최대값: " << result << std::endl;
return 0;
}
인라인 함수의 특징:
- inline 키워드는 컴파일러에 대한 힌트일 뿐, 반드시 인라인화되는 것은 아님
- 작고 자주 호출되는 함수에 적합
- 헤더 파일에 완전한 정의가 필요
- 재귀 함수나 큰 함수는 인라인화에 적합하지 않음
'Programming Languages > C++' 카테고리의 다른 글
재귀(Recursion) (0) | 2025.03.27 |
---|---|
함수 포인터와 함수 객체 (0) | 2025.03.27 |
챕터3. 실습문제 (0) | 2025.03.27 |
조건부 컴파일 (0) | 2025.03.27 |
제어 흐름 변경 (0) | 2025.03.27 |