Function(함수)
함수란?
- 특정 작업을 수행하는 코드 블록으로, 코드를 모듈화하고 재사용성을 높이는 핵심 요소다
- 기존의 monolithic programming(모놀리식 프로그래밍)에서는 하나의 긴 코드로 프로그램을 작성했지만, 함수로 코드를 분리하면서 유지보수성과 가독성이 향상되었다
함수의 메모리 구조
- C++에서 함수가 메모리에 배치되는 방식은 다음과 같다
- Code : 함수 코드가 저장되는 영역
- Stack : 함수 호출 시 생성되는 지역 변수, 매개변수 저장
- Heap : 동적으로 할당된 메모리 저장
함수 실행 과정
- Code 영역에 함수 코드가 저장
- 함수 호출 시 Stack에 매개변수, 지역변수가 생성
- Heap 영역은 함수 내에서 동적 메모리 할당(new) 시 사용
- 함수 종료 시 Stack 메모리 자동 해제, Heap 메모리는 수동 해제 필요(delete)
함수 오버로딩(Function Overloading)
- 같은 이름을 가진 여러 개의 함수를 정의하는 기법
- 매개변수의 개수 또는 타입이 달라야 한다
#include <iostream>
using namespace std;
void print(int x) { cout << "정수: " << x << endl; }
void print(double x) { cout << "실수: " << x << endl; }
void print(string x) { cout << "문자열: " << x << endl; }
int main() {
print(10); // 정수
print(3.14); // 실수
print("Hello"); // 문자열
}
함수 템플릿(제네릭 타입)
- 데이터 타입에 관계없이 동작하는 함수를 만들 때 사용
- 무분별하게 사용하면 불필요한 코드 중복이 발생할 수 있다
- 자주 재사용되는 일반적인 기능에만 사용
#include <iostream>
using namespace std;
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
cout << add(3, 5) << endl; // 정수 덧셈
cout << add(2.5, 3.7) << endl; // 실수 덧셈
}
단점
- 컴파일 시간 증가
- 코드 크기 증가(각 타입별로 별도 생성)
- 디버깅이 어려울 수 있다
기본값 매개변수(Optional Parameter)
- 기본값을 설정하면, 함수를 호출할 때 일부 매개변수를 생략 가능
#include <iostream>
using namespace std;
void greet(string name = "Guest") {
cout << "Hello, " << name << "!" << endl;
}
int main() {
greet(); // 기본값 사용 → "Hello, Guest!"
greet("Alice"); // "Hello, Alice!"
}
매개변수 전달 방법
1. 값에 의한 전달
- 매개변수의 복사본을 생성하여 전달
- 원본 값 변경 불가
void change(int x) { x = 10; }
2. 주소에 의한 전달
- 포인터(*)를 사용하여 주소 전달
- 원본 값 변경 가능
void change(int* x) { *x = 10; }
3. 참조에 의한 전달
- 참조(&)를 사용하여 원본을 직접 수정
- 원본 값 변경 가능
void change(int& x) { x = 10; }
지역 변수와 전역 변수
지역 변수
- 생성 위치 : Stack 영역
- 생명 주기 : 함수가 실행되는 동안
전역 변수
- 생성 위치 : Data 영역
- 생명 주기 : 프로그램 종료까지
int globalVar = 100; // 전역 변수
void func() {
int localVar = 50; // 지역 변수
}
정적 변수(Static Variable)
- 메모리에서 사라지지 않고 유지
- 전역 변수와 달리 특정 함수 내에서만 접근 가능
void func() {
static int count = 0; // 정적 변수
count++;
cout << count << endl;
}
int main() {
func(); // 1
func(); // 2
}
재귀 함수(Recursive Function)
int factorial(int n) {
if (n == 1) return 1;
return n * factorial(n - 1);
}
사용 시 주의점
- 기본 종료 조건(Base Case)을 반드시 설정
- 과도한 재귀 호출은 Stack Overflow 발생 가능
포인터 함수(Pointer Function)
- 함수의 반환값이 포인터인 경우, 즉, 일반 값이 아니라 메모리 주소
int* getPointer() {
static int x = 10;
return &x;
}
사용 시 주의점
- 함수 내부의 지역 변수를 포인터로 반환하면 안된다(정적 변수나 동적 메모리를 활용)
- C++에서 함수 내부의 지역 변수는 Stack 영역에 저장된다. 함수가 실행될 때, Stack에 지역 변수가 생성되지만, 함수가 종료되면 해당 변수가 저장된 메모리는 자동으로 해제된다. 따라서, 지역 변수의 주소를 반환하면 이미 해제된 메모리를 가리키게 되어 예측할 수 없는 동작이 발생할 수 있다