기본 매개변수
생략한 인자를 선언의 기본값으로 채우는 규칙을 익히고 선언 위치·오른쪽 배치·오버로딩 모호성을 점검합니다.
메시지를 출력하는 함수에 반복 횟수를 받게 했는데, 대부분의 호출이 한 번만 출력한다면 매번 1을 적는 것은 번거롭습니다. 그렇다고 횟수가 있는 버전과 없는 버전을 오버로딩하면 거의 같은 함수가 두 개 생깁니다. 기본 매개변수(default argument)를 쓰면 매개변수에 기본값을 정해 두고, 호출에서 인자를 생략했을 때 그 값을 쓰게 할 수 있습니다. 영어 이름을 그대로 옮겨 기본 인자라고도 부르며, 이 글의 그림은 그 이름을 씁니다.
이 글은 기본 매개변수를 선언하는 규칙, 기본값이 언제 계산되는지, 선언과 정의 중 어디에 적는지, 오버로딩과 함께 쓸 때 생기는 모호함, 그리고 기본값을 바꿀 때의 주의점을 다룹니다.
선언과 오른쪽 배치 규칙
매개변수 뒤에 = 값을 붙이면 기본값이 됩니다. 호출에서 그 자리의 인자를 생략하면, 컴파일러가 호출 지점에서 보이는 선언의 기본값으로 인자를 채워 넣습니다. 함수 본문이 실행 중에 기본값을 고르는 것이 아니라, 호출하는 쪽의 코드에 값이 채워진다는 점이 중요합니다.
#include <iostream>
void setWindowSize(int x, int y, int width = 800, int height = 600) {
std::cout << "x=" << x << " y=" << y
<< " width=" << width << " height=" << height << '\n';
}
int main() {
setWindowSize(10, 20);
setWindowSize(50, 50, 1024);
setWindowSize(0, 0, 1920, 1080);
}x=10 y=20 width=800 height=600
x=50 y=50 width=1024 height=600
x=0 y=0 width=1920 height=1080아래 그림처럼 세 호출은 서로 독립적이며, 각 호출에서 빠진 뒤쪽 인자만 기본값으로 채워집니다. 첫 호출은 width와 height를 모두 생략했고, 둘째 호출은 height만 생략했으며, 셋째 호출은 모두 명시해 기본값을 쓰지 않습니다. 인자는 앞에서부터 차례로 매개변수에 대응하므로, width를 건너뛰고 height만 지정하는 호출은 쓸 수 없습니다.
CALL SITE · TRAILING DEFAULTS
생략한 인자는 호출 지점에 보이는 선언이 채운다
기본 인자는 함수 본문 안의 fallback이 아닙니다. 컴파일러는 호출 지점에서 도달 가능한 같은 범위의 선언을 보고, 생략된 뒤쪽 인자를 보충합니다. 직접 전달한 인자는 언제나 기본값보다 우선합니다.
세 호출은 독립 사례빠진 뒤쪽만 기본값 사용
필수 인자 뒤에 선택 인자를 연속해서 둔다
void setWindowSize(
int x,
int y,
int width = 800,
int height = 600);
setWindowSize(10, 20) → width=800, height=600
중간 인자를 건너뛰는 위치 기반 문법은 없다
setWindowSize(50, 50, 1024);
// width=1024, height=600
setWindowSize(0, 0, 1920, 1080);
// 두 기본값 모두 사용하지 않음
따라서 한 선언에서는 기본값을 둔 매개변수 뒤의 모든 매개변수가 현재 선언 또는 앞선 같은 범위의 선언에서 기본값을 가져야 합니다.
선언에서 실제 호출까지의 세 단계
도달 가능한 선언을 모은다
호출 지점에서 보이는 같은 host scope(선언이 속한 범위)의 선언들이 각 매개변수에 제공한 기본값을 확인합니다. 내부 범위에 새로 선언한 함수는 바깥 선언의 기본값을 자동으로 물려받지 않습니다.
빠진 trailing 인자를 확인한다
왼쪽부터 전달된 인자를 대응시킨 뒤, 남은 오른쪽 매개변수마다 유효한 기본값이 있어야 그 호출 형태를 사용할 수 있습니다.
기본 표현식을 호출마다 평가한다
기본 표현식의 이름은 선언 문맥에서 해석되지만, 표현식 자체는 해당 인자를 생략한 각 호출에서 평가됩니다.
| 선언 조합 | 판정 | 이유 |
|---|---|---|
| 적합 | 기본값이 오른쪽 끝에서 연속합니다. |
| 오류 | x 뒤의 y에 현재 또는 앞선 선언의 기본값이 없습니다. |
| 적합 | 같은 범위의 후속 선언이 아직 없던 x 기본값만 추가하고, y는 앞선 선언에서 가져옵니다. |
| 오류 | 정의도 선언입니다. 이미 제공한 n의 기본값을 같은 값으로 반복해도 재정의입니다. |
void place(
int x,
int y = 20,
int z = 30);
기본값이 오른쪽 끝에서 연속합니다.
void gap(
int x = 10,
int y,
int z = 30);
x 뒤의 y에 현재 또는 앞선 선언의 기본값이 없습니다.
void staged(int x,
int y = 20);
void staged(int x = 10,
int y);
같은 범위의 후속 선언이 아직 없던 x 기본값만 추가하고, y는 앞선 선언에서 가져옵니다.
void print(int n = 1);
void print(int n = 1) {
/* … */
}
정의도 선언입니다. 이미 제공한 n의 기본값을 같은 값으로 반복해도 재정의입니다.
공개 API 원칙: 호출부가 기본값을 볼 수 있도록 헤더 선언에 두고, 소스의 정의에는 반복하지 않습니다. 후속 선언의 추가 규칙은 허용되지만 선언을 흩어 놓을 이유가 되지는 않습니다.
그래서 기본값은 오른쪽 끝의 매개변수부터 연속해서 주어야 합니다. 기본값이 있는 매개변수 뒤에 기본값이 없는 매개변수가 오면, 앞의 인자를 생략할 방법이 없으므로 컴파일 오류입니다.
int funcA(int a, int b = 20, int c = 30); // 올바름
int funcB(int a, int b, int c = 30); // 올바름
int funcC(int a = 10, int b = 20, int c = 30); // 올바름
// int bad1(int a = 10, int b); // 컴파일 오류
// int bad2(int a, int b = 20, int c); // 컴파일 오류필수 인자는 왼쪽에, 대부분 같은 값을 쓰는 선택 인자는 오른쪽에 두면 이 규칙이 자연스럽게 지켜집니다.
기본값은 호출할 때마다 계산됩니다
기본값은 리터럴뿐 아니라 변수나 함수 호출 같은 식이어도 됩니다. 식 안의 이름은 선언된 위치에서 찾지만, 그 값은 인자를 생략한 호출마다 새로 계산됩니다.
#include <iostream>
int repeat = 1;
void show(int value, int count = repeat) {
for (int i = 0; i < count; ++i) {
std::cout << value;
}
std::cout << '\n';
}
int main() {
show(7);
repeat = 3;
show(7);
show(7, 2);
}7
777
77첫 호출은 그때의 repeat 값 1로 count를 초기화해 7을 출력하고, repeat를 3으로 바꾼 뒤의 둘째 호출은 3으로 초기화해 777을 출력합니다. 함수를 정의할 때의 1을 저장해 두었다가 계속 쓰는 것이 아닙니다. 셋째 호출은 count를 직접 2로 넘겼으므로 기본값의 식을 계산하지 않습니다. 이 예는 전역 변수를 권하려는 것이 아니라, 기본값이 계산되는 시점을 보여 주기 위한 것입니다.
선언과 정의 가운데 어디에 적는가
기본값은 한 매개변수에 한 번만 지정할 수 있으며, 같은 값이라도 선언과 정의에 두 번 적으면 컴파일 오류입니다. 함수를 헤더에 선언하고 소스 파일에 정의할 때는, 호출하는 쪽이 기본값을 볼 수 있도록 헤더의 선언에 기본값을 적고 정의에는 적지 않습니다.
#pragma once
void printData(int value, char separator = '-', int repeat = 1);#include "my_printer.h"
#include <iostream>
void printData(int value, char separator, int repeat) {
for (int i = 0; i < repeat; ++i) {
std::cout << value;
if (i < repeat - 1) {
std::cout << separator;
}
}
std::cout << '\n';
}#include "my_printer.h"
int main() {
printData(10);
printData(20, '*');
printData(30, '#', 5);
}인라인 함수에서처럼 두 소스를 함께 빌드합니다. main.cpp의 세 호출은 인자를 하나, 둘, 셋 넘기므로, 출력의 각 줄에서 어떤 인자가 기본값으로 채워졌는지를 확인해 보세요.
g++ -std=c++20 -Wall -Wextra main.cpp my_printer.cpp -o default_demo.exe
.\default_demo.exe10
20
30#30#30#30#30printData(20, '*')는 구분자를 바꾸었지만 repeat가 1이어서 구분자를 넣을 자리가 없으므로 20만 출력합니다. 정의 쪽에 기본값을 적으면 그 정의가 보이는 my_printer.cpp 안에서만 기본값이 쓰이고, 헤더만 보는 main.cpp에서는 인자를 생략할 수 없게 됩니다.
오버로딩과 함께 쓸 때의 모호함
함수 오버로딩에서 본 대로, 기본 매개변수가 있는 함수는 인자를 생략한 호출에서도 호출 가능한 후보가 됩니다. 하지만 기본값을 덜 썼다고 해서 더 좋은 후보가 되지는 않습니다.
#include <iostream>
#include <string>
void greet(std::string name) {
std::cout << "Hello, " << name << '\n';
}
void greet(std::string name, std::string salutation = "Hi") {
std::cout << salutation << ", " << name << '\n';
}
int main() {
greet("Kim", "Welcome");
// greet("홍길동"); // 컴파일 오류: 모호한 호출
}인자가 둘인 greet("Kim", "Welcome")은 둘째 함수만 받을 수 있어 Welcome, Kim을 출력합니다. 주석으로 막은 한 인자 호출은 아래 그림처럼 두 함수가 모두 호출 가능하고, 넘긴 첫 인자의 변환도 같아서 어느 쪽도 더 낫지 않습니다.
C++ 기본 인자와 오버로드 판정, 공개 API 영향
실제 greet(홍길동)에 A(std::string)와 B(std::string,std::string=안녕)가 모두 viable이고 제공된 인자의 변환도 같아 호출이 거부되는 구체 사례다. 후보 수 2→viable 2→유일 best 없음의 검증 장부로 구성하고 공개 connect 기본 1000/3000의 별도 재컴파일 비교는 전체 관계도와 표로 보존한다.
VIABLE · RANK · API
기본 인자는 후보를 살릴 수 있지만 순위를 올려 주지 않는다
오버로드 판정은 “호출 가능한가”와 “누가 더 좋은가”를 나눠서 봅니다. 생략한 뒤쪽 매개변수에 기본값이 있으면 후보가 viable해지지만, 기본값을 덜 사용했다는 이유만으로 우선하지는 않습니다.
한 인자 greet 호출에서 A와 기본 인자 B가 모두 viable하다. 제공한 첫 인자의 변환 순위가 같으므로 유일 best가 없어 호출은 거부된다. 별도 connect 예에서 v1/v2 헤더로 컴파일한 호출은 각각 1000/3000을 전달한다.
관계도 해설과 예제 원자료
greet(std::string)
void greet(
std::string name);
greet("홍길동")의 인자 수와 맞으므로 viable 후보입니다.
greet(std::string, std::string)
void greet(
std::string name,
std::string salutation
= "안녕");
두 번째 인자를 생략해도 기본값이 있으므로 같은 한 인자 호출에 viable합니다.
greet("홍길동")이 모호해지는 흐름
이름으로 후보 집합을 만든다
보이는 두
greet선언이 모두 후보가 됩니다. 기본 인자는 함수 타입이나 별도 오버로드를 만들지 않습니다.인자 수와 기본값으로 viability를 거른다
후보 A는 매개변수가 하나라서, 후보 B는 빠진 두 번째 매개변수에 기본값이 있어서 모두 살아남습니다.
제공된 인자의 변환 순위를 비교한다
두 후보 모두 문자열 리터럴을 첫 번째
std::string으로 바꾸는 같은 변환이 필요합니다. 유일한 최적 후보가 없으므로 호출은 ill-formed입니다.
| 호출 | 결과 | 판정 근거 |
|---|---|---|
greet("홍길동") | 모호함 | A와 B가 모두 viable이고, 제공된 인자에 대한 변환 순위도 같습니다. |
greet("A", "Hi") | 후보 B | 후보 A는 인자가 너무 많아 viable하지 않습니다. |
| 후보 A를 제거한 뒤 한 인자 호출 | 후보 B | 단 하나의 함수가 기본 인자로 공통 호출 형태를 제공합니다. |
| 역할별 이름을 분리 | 명확함 | 동작 의미가 다르면 기본 인자보다 서로 다른 함수 이름이 호출 의도를 더 잘 드러냅니다. |
greet("홍길동")
A와 B가 모두 viable이고, 제공된 인자에 대한 변환 순위도 같습니다.
greet("A", "Hi")
후보 A는 인자가 너무 많아 viable하지 않습니다.
후보 A를 제거한 한 인자 호출
단 하나의 함수가 기본 인자로 공통 호출 형태를 제공합니다.
역할별 이름을 분리
동작 의미가 다르면 서로 다른 함수 이름이 호출 의도를 더 잘 드러냅니다.
헤더의 기본값 변경은 재컴파일된 호출부의 동작을 바꾼다
// v1 header
void connect(
int timeoutMs = 1000);v1 헤더로 컴파일한 connect() 호출은 인자 1000을 전달하는 코드가 됩니다.
// v2 header
void connect(
int timeoutMs = 3000);v2 헤더로 다시 컴파일한 호출부는 3000을 전달합니다. 기존 오브젝트를 단순히 다시 링크하는 것만으로 그 호출 인자가 바뀌지는 않습니다.
설계 체크: 자주 쓰는 한 가지 의미에만 기본값을 주고, 같은 호출 형태의 오버로드를 만들지 않으며, 공개 기본값 변경을 소스 호환성 이상의 동작 변경으로 검토합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 선언 후보 greet(홍길동)에 보이는 두 함수를 놓는다. |
|
|
| 호출 가능·순위 한 인자 호출에서 두 후보가 모두 살아남는다. |
|
|
| 호출 거부 유일한 최적 후보가 없어 실행하지 않는다. |
|
|
C++ 기본 인자와 오버로드 판정, 공개 API 영향
실제 greet(홍길동)에 A(std::string)와 B(std::string,std::string=안녕)가 모두 viable이고 제공된 인자의 변환도 같아 호출이 거부되는 구체 사례다. 후보 수 2→viable 2→유일 best 없음의 검증 장부로 구성하고 공개 connect 기본 1000/3000의 별도 재컴파일 비교는 전체 관계도와 표로 보존한다.
VIABLE · RANK · API
기본 인자는 후보를 살릴 수 있지만 순위를 올려 주지 않는다
오버로드 판정은 “호출 가능한가”와 “누가 더 좋은가”를 나눠서 봅니다. 생략한 뒤쪽 매개변수에 기본값이 있으면 후보가 viable해지지만, 기본값을 덜 사용했다는 이유만으로 우선하지는 않습니다.
한 인자 greet 호출에서 A와 기본 인자 B가 모두 viable하다. 제공한 첫 인자의 변환 순위가 같으므로 유일 best가 없어 호출은 거부된다. 별도 connect 예에서 v1/v2 헤더로 컴파일한 호출은 각각 1000/3000을 전달한다.
관계도 해설과 예제 원자료
greet(std::string)
void greet(
std::string name);
greet("홍길동")의 인자 수와 맞으므로 viable 후보입니다.
greet(std::string, std::string)
void greet(
std::string name,
std::string salutation
= "안녕");
두 번째 인자를 생략해도 기본값이 있으므로 같은 한 인자 호출에 viable합니다.
greet("홍길동")이 모호해지는 흐름
이름으로 후보 집합을 만든다
보이는 두
greet선언이 모두 후보가 됩니다. 기본 인자는 함수 타입이나 별도 오버로드를 만들지 않습니다.인자 수와 기본값으로 viability를 거른다
후보 A는 매개변수가 하나라서, 후보 B는 빠진 두 번째 매개변수에 기본값이 있어서 모두 살아남습니다.
제공된 인자의 변환 순위를 비교한다
두 후보 모두 문자열 리터럴을 첫 번째
std::string으로 바꾸는 같은 변환이 필요합니다. 유일한 최적 후보가 없으므로 호출은 ill-formed입니다.
| 호출 | 결과 | 판정 근거 |
|---|---|---|
greet("홍길동") | 모호함 | A와 B가 모두 viable이고, 제공된 인자에 대한 변환 순위도 같습니다. |
greet("A", "Hi") | 후보 B | 후보 A는 인자가 너무 많아 viable하지 않습니다. |
| 후보 A를 제거한 뒤 한 인자 호출 | 후보 B | 단 하나의 함수가 기본 인자로 공통 호출 형태를 제공합니다. |
| 역할별 이름을 분리 | 명확함 | 동작 의미가 다르면 기본 인자보다 서로 다른 함수 이름이 호출 의도를 더 잘 드러냅니다. |
greet("홍길동")
A와 B가 모두 viable이고, 제공된 인자에 대한 변환 순위도 같습니다.
greet("A", "Hi")
후보 A는 인자가 너무 많아 viable하지 않습니다.
후보 A를 제거한 한 인자 호출
단 하나의 함수가 기본 인자로 공통 호출 형태를 제공합니다.
역할별 이름을 분리
동작 의미가 다르면 서로 다른 함수 이름이 호출 의도를 더 잘 드러냅니다.
헤더의 기본값 변경은 재컴파일된 호출부의 동작을 바꾼다
// v1 header
void connect(
int timeoutMs = 1000);v1 헤더로 컴파일한 connect() 호출은 인자 1000을 전달하는 코드가 됩니다.
// v2 header
void connect(
int timeoutMs = 3000);v2 헤더로 다시 컴파일한 호출부는 3000을 전달합니다. 기존 오브젝트를 단순히 다시 링크하는 것만으로 그 호출 인자가 바뀌지는 않습니다.
설계 체크: 자주 쓰는 한 가지 의미에만 기본값을 주고, 같은 호출 형태의 오버로드를 만들지 않으며, 공개 기본값 변경을 소스 호환성 이상의 동작 변경으로 검토합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 선언 후보 greet(홍길동)에 보이는 두 함수를 놓는다. |
|
|
| 호출 가능·순위 한 인자 호출에서 두 후보가 모두 살아남는다. |
|
|
| 호출 거부 유일한 최적 후보가 없어 실행하지 않는다. |
|
|
유일한 최적 후보가 없으므로 컴파일러는 이 호출을 거부하고, 프로그램은 실행되지 않습니다. 이런 충돌을 피하려면 같은 호출 모양을 받아들이는 오버로드와 기본 매개변수를 함께 두지 않습니다. 위 예에서는 첫째 함수를 지우고 둘째 함수의 기본값 하나로 두 경우를 처리하는 것이 간단합니다.
기본값을 바꿀 때의 주의점
기본값은 호출하는 쪽의 코드에 채워지므로, 함수를 쓰는 사람에게 약속한 동작의 일부입니다. 그림의 아래쪽처럼 헤더에 void connect(const std::string& host, int timeoutMs = 1000);을 두었다가 기본값을 3000으로 바꾼 경우를 생각해 봅니다.
새 헤더로 다시 컴파일한 호출부는 connect("db")에 3000을 넘기지만, 다시 컴파일하지 않은 옛 목적 파일의 호출부는 계속 1000을 넘깁니다. 함수 본문은 바뀌지 않았는데 호출부마다 다른 값이 들어가는 것입니다. 그래서 라이브러리의 기본값을 바꾸는 것은 동작을 바꾸는 일로 검토하고, 그 헤더를 쓰는 모든 코드를 다시 빌드해야 합니다.
더 깊이: 기본값은 함수 타입에 들어가지 않습니다
기본값은 함수 타입의 일부가 아닙니다. int f(int = 0);의 타입은 int(int)이므로, int (*p)(int) = &f;로 받은 함수 포인터로는 p()처럼 인자를 생략할 수 없고, int (*q)() = &f;는 타입이 맞지 않아 컴파일되지 않습니다. 기본값은 호출하는 자리에서 그 선언이 보일 때만 쓰이는 것이어서, 함수를 콜백으로 넘기면 기본값은 따라가지 않습니다. 또 템플릿이 아닌 함수는 같은 범위에서 다시 선언하면서 아직 기본값이 없는 매개변수에 기본값을 더할 수 있고, 호출 지점에서는 보이는 선언들의 기본값을 모두 합쳐 씁니다.
가상 함수에서는 이 성질이 함정이 됩니다. 기본값은 호출식의 정적 타입으로 정해지므로, Base의 virtual void f(int a = 7);을 Derived가 재정의했다면 Base&로 부른 b.f()는 Derived::f(7)을 실행합니다. 재정의한 함수는 기반 클래스의 기본값을 물려받지 않아서, Derived의 선언에 기본값이 없으면 Derived 객체로 직접 부르는 d.f()는 컴파일되지 않습니다. 그래서 가상 함수에는 기본값을 두지 않거나, 기본값을 받는 비가상 함수가 가상 함수를 부르도록 나누는 편이 안전합니다. 규칙과 예는 cppreference의 Default arguments 문서에 정리되어 있습니다.
정리
- 기본 매개변수는 호출에서 생략한 뒤쪽 인자를 선언의 기본값으로 채우며, 그 값은 호출하는 쪽의 코드에 들어갑니다.
- 기본값은 오른쪽 끝부터 연속해야 하고, 인자는 앞에서부터 대응하므로 중간 인자만 건너뛸 수 없습니다.
- 기본값의 식은 인자를 생략한 호출마다 새로 계산되며, 인자를 명시하면 계산하지 않습니다.
- 한 매개변수의 기본값은 한 번만 적으며, 헤더의 선언에 적고 정의에는 적지 않습니다.
- 기본 매개변수가 있는 함수와 같은 호출 모양을 받는 오버로드를 함께 두면 모호한 호출이 됩니다.
- 기본값을 바꾸면 다시 컴파일한 호출부만 새 값을 쓰므로, 동작 변경으로 보고 전체를 다시 빌드합니다.
연습 문제
1. 기본값이 0이면
default_expression.cpp에서 대입문만 repeat = 0;으로 바꾸면 둘째 줄과 셋째 줄은 어떻게 되나요?
해설 보기
둘째 호출은 count가 0이어서 반복 본문을 한 번도 실행하지 않고 줄바꿈만 출력하므로, 빈 줄이 됩니다. 셋째 호출 show(7, 2)는 count를 직접 넘겼으므로 repeat를 읽지 않고 여전히 77을 출력합니다.
2. 올바른 선언 찾기
다음 가운데 컴파일 오류가 나는 것은 무엇인가요? (가) void f(int a, int b = 1, int c = 2); (나) void g(int a = 0, int b, int c = 2); (다) 헤더에 void h(int x = 5);를 선언하고 소스에 void h(int x = 5) { }로 정의
해설 보기
(나)와 (다)가 오류입니다. (나)는 기본값이 있는 a 뒤에 기본값이 없는 b가 있어 오른쪽 배치 규칙을 어깁니다. (다)는 같은 매개변수의 기본값을 선언과 정의에 두 번 적어서, 값이 같아도 컴파일 오류가 납니다. 정의는 void h(int x) { }로 적어야 합니다.
3. 다시 컴파일하지 않은 호출부
connect의 기본값을 1000에서 3000으로 바꾼 헤더로 a.cpp만 다시 컴파일하고, b.cpp의 목적 파일은 옛것을 그대로 링크했습니다. 두 파일의 connect("db")는 각각 몇을 넘기나요?
해설 보기
a.cpp의 호출은 새 헤더의 기본값 3000을, b.cpp의 호출은 컴파일할 때 채워 넣은 옛 기본값 1000을 넘깁니다. 기본값은 함수 본문이 아니라 각 호출부의 코드에 들어가기 때문이며, 헤더를 바꾸면 그 헤더를 쓰는 모든 소스를 다시 컴파일해야 합니다.
다음 글 재귀 함수에서는 함수가 자기 자신을 호출해 문제를 더 작은 같은 문제로 나누어 푸는 방법을 다룹니다.