학습 개요
- 함수는 C++ 프로그램을 구성하는 기본 단위임
- 함수의 기본적인 형식과 함께, 함수에 인수를 전달하는 방법, 개념적으로 동일한 처리를 대상에 맞게 정의할 수 있게 하는 함수 다중 정의, 효율적인 프로그램 작성이 매우 중요한 경우 활용할 수 있는 inline 함수에 대하여 학습함
- 함수 자체는 C 언어에서와 동일하나, 참조 호출, 함수 다중 정의와 같은 기능은 C++에서 확장된 부분으로 많이 사용되는 내용임
학습 목표
- 함수의 머리부와 몸체부를 문법에 맞게 작성할 수 있음
- 값 호출과 참조 호출을 할 경우 형식 매개변수와 실 매개변수 사이에 인수가 어떻게 전달되는지 설명할 수 있음
- 동일한 이름의 함수가 인수에 따라 서로 다른 동작을 하도록 함수를 정의할 수 있음
- 필요한 경우 함수의 호출 과정이 효율적으로 이루어질 수 있도록 지정할 수 있음
주요 용어
- 순환 호출(recursive call)
- 함수에서 직접 또는 간접적으로 그 함수를 호출하는 것
- 인수(argument)
- 함수 호출 문장에서 함수에 전달하기 위해 나열된 식
- 실 매개변수(actual parameter)
- 함수 호출 문장에서 함수의 형식 매개변수에 전달할 인수
- 형식 매개변수(formal parameter)
- 인수를 전달 받기 위해 함수에 선언된 매개 변수
- 값 호출(call-by-value)
- 실 매개 변수의 값을 형식 매개변수에 복사하는 매개변수 전달 방식
- 참조 호출(call-by-reference)
- 실 매개 변수의 참조를 형식 매개변수에 전달하는 매개변수 전달 방식
- 함수 다중 정의
- 동일한 이름을 갖는 함수를 여러 개 정의하는 것
강의록
함수의 정의와 호출
함수의 개념
다음 프로그램이 하는 작업은?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
int main() { double a[50], b[100]; // 생략: a와 b에 각각 50개와 100개의 데이터 준비 double max = a[0]; for (int i = 1; i < 50; i++) if (max < a[i]) max = a[i]; cout << max << endl; max = b[0]; for (int i = 1; i < 100; i++) if (max < b[i]) max = b[i]; cout << max << endl; }
함수를 이용하여 작성된 프로그램
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
double maximum(const double arr[], int n) { double max = arr[0]; for (int i = 1; i < n; i++) if (max < arr[i]) max = arr[i]; return max; } int main() { double a[50], b[100]; // 생략: a와 b에 각각 50개와 100개의 데이터 준비 cout << maximum(a, 50) << endl; cout << maximum(b, 100) << endl; }
함수란?
- 특정 작업을 수행하는 프로그램 문장들을 하나의 단위로 모아 놓고 이름을 부여한 것
- 함수에 정의된 처리가 필요한 부분에서 호출하여 사용함
- 매개 변수를 통해 함수에서 처리할 데이터(인수)를 전달함
- 호출된 함수로 이동하여 함수 몸체 블록을 실행함
- 정해진 처리를 한 후 결과 값을 반환할 수 있음
- 함수의 실행을 완료하면 호출한 곳으로 복귀함
- C++ 프로그램은 함수를 기본 단위로 하여 구성됨
함수 사용 형식
함수 정의 형식
1 2 3 4 5 6 7 8 9 10
ReturnType functionName(fParameterList) // 머리부 { // 몸체 블록 Type1 localVar1; // 지역변수 선언 Type2 localVar2; statement1; // 처리 작업을 수행하는 문장 statement2; return returnExpression; // 결과 값을 반환함 }
fParameterList
- 인수를 받기 위한 형식 매개 변수 선언
ReturnType
- 함수의 결과로 반환하는 값의 자료형
returnExpression
- 함수의 결과로 반환하는 값
return
명령- 함수를 마치고 함수를 호출한 곳으로 복귀하는 명령
- 함수 안의 어느 곳이든 복귀를 위해 사용할 수 있음
returnExpression
은 함수 머리 부에 선언한ReturnType
과 일치하는 자료형의 수식 또는 묵시적 형 변환이 가능한 자료형의 수식을 사용함- 반환하는 값이 없는 함수는
ReturnType
은void
로 선언함 main
함수에서는return
명령을 만나지 않은 상태로 함수의 끝에 도달하면return 0;
을 실행한 것 같음
- 함수 호출 형식
functionName(aParameterList);
varName = functionName(aParameterList);
aParameterList
- 인수로 전달할 실 매개 변수 나열
- 형식 1
- 반환 값 유무에 관계없이 사용 가능함
- 형식 2
ReturnType
이void
가 아닌 함수에 사용할 수 있음
함수 사용 예 - ConvFtoC.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;
// 화씨온도를 섭씨온도로 변환하는 함수
// 매개변수 float fahr: 화씨온도
// 반환값 섭씨온도(float)
float FahrToC(float fahr)
{
return (fahr - 32) * 5 / 9;
}
int main()
{
float fTemp, cTemp;
cout << "화씨온도 : ";
cin >> fTemp;
cTemp = FahrToC(fTemp); // 함수 호출
cout << "---> 섭씨온도 : " << cTemp << endl;
return 0;
}
함수의 원형
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
float FahrToC(float fahr); // 함수의 원형
int main()
{
float fTemp, cTemp;
cout << "화씨온도 : ";
cin >> fTemp;
cTemp = FahrToC(fTemp); // 함수 호출
cout << "---> 섭씨온도 : " << cTemp << endl;
return 0;
}
float FahrToC(float fahr)
{
return (fahr - 32) * 5 / 9;
}
함수의 순환 호출
- 순환 호출 (recursive call)
- 함수를 정의하는 몸체 블록 안에서 직접 또는 간접적으로 그 함수를 호출하도록 코드를 작성하는 것
- 순환 호출의 예
- 팩토리얼 계산 함수의 순환 호출 구현
1 2 3 4 5 6 7
int factorial(int n) { if (n <= 1) **return 1; else return n * factorial(n - 1); }
함수의 장점과 단점
- 장점
- 크고 복잡한 프로그램을 작은 크기의 의미 있는 작업 단위로 분할하여 구성
- 간결하고 이해하기 쉬운 프로그램을 만들 수 있음
- 반복 사용되는 코드의 중복 방지
- 잘 설계된 함수는 다른 응용에서 재 사용할 수 있음
- 크고 복잡한 프로그램을 작은 크기의 의미 있는 작업 단위로 분할하여 구성
- 단점
- 함수 호출과 복귀 과정에서 처리 시간이 추가됨
- 매우 효율적으로 동작해야 하는 함수라면
inline
함수로 선언함
- 매우 효율적으로 동작해야 하는 함수라면
- 함수 호출과 복귀 과정에서 처리 시간이 추가됨
인수의 전달
인수(argument)와 매개 변수(parameter)
- 인수(argument)
- 함수 호출 문장에서 함수에 전달하는 식(expression)
- 여러 개의 인수가 있을 경우 콤마(,)로 구분하여 나열함
- 매개 변수를 통해 인수를 전달함
- 실 매개 변수(actual parameter)
- 함수 호출 문장에서 함수의 형식 매개 변수에 전달할 인수
1 2 3 4 5 6
int main() { // ... cTemp = FahrToC(fTemp); // fTemp : 실 매개 변수 // ... }
- 형식 매개 변수(formal parameter)
- 인수를 전달 받기 위해 함수에 선언된 매개 변수
- 함수 헤더에 매개 변수의 자료형과 이름을 선언함
1 2 3 4
float FahrToC(float fahr) // fahr : 형식 매개 변수 { return (fahr - 32) * 5 / 9; }
인수 전달 방식 - 값 호출
- 값 호출(call-by-value)
실 매개 변수의 값을 형식 매개 변수에 복사하는 방식
1 2 3 4 5 6 7 8 9 10 11 12 13 14
float FahrToC(float fahr) // fahr에 값 복사 { return (fahr - 32) * 5 / 9; } int main() { float fTemp, cTemp; cout << "화씨온도 : "; cin >> fTemp; cTemp = FahrToC(fTemp); // 함수 호출 cout << "---> 섭씨온도 : " << cTemp << endl; return 0; }
형식 매개 변수의 값을 변경해도 실 매개 변수는 변함 없음
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
float FahrToC(float fahr) // 형식 매개변수 수정 (복사본) { fahr -= 32; return fahr * 5 / 9; } int main() { float fTemp, cTemp; cout << "화씨온도 : "; cin >> fTemp; cTemp = FahrToC(fTemp); // 원본은 변함 없음 cout << "---> 섭씨온도 : " << cTemp << endl; return 0; }
- 값 호출 방식의 장점과 단점
- 장점
- 실 매개 변수와 형식 매개 변수는 별개의 데이터이므로 불필요한 부작용이 발생하지 않음
- 단점
- 구조체와 같이 많은 양의 데이터로 구성된 인수를 전달할 경우 데이터의 복사 량이 많아짐
- 장점
인수 전달 방식 - 참조 호출
- 참조 호출(call-by-reference)
- 실 매개 변수의 참조를 형식 매개 변수에 전달하는 방식
- 참조 호출의 용도
- 함수에서 처리한 결과를 매개 변수를 통해 받아 오려는 경우
- 함수에서 형식 매개 변수의 값을 변경하는 것은 실 매개 변수의 값을 변경하는 것과 같음
- 많은 양의 데이터로 구성되는 구조체와 같은 인수를 함수에 효율적으로 전달하는 경우
- 형식 매개 변수에 복사 되는 데이터의 양은 실 매개 변수의 크기와 관계 없이 일정함
- 함수에서 처리한 결과를 매개 변수를 통해 받아 오려는 경우
참조 호출의 예 - SwapInt.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#include <iostream> using namespace std; void SwapValues(int& x, int& y); // 원형 int main() { int a, b; cout << "두 수를 입력하시오 : "; cin >> a >> b; if (a < b) SwapValues(a, b); // 순서를 바꿔 a에 큰 값을 넣음 cout << "큰 수 = " << a << " 작은 수 = " << b << endl; return 0; } void SwapValues(int& x, int& y) { int temp = x; x = y; y = temp; }
- 참조 호출을 통한 효율적인 인수 전달
- 많은 양의 데이터로 구성되는 객체나 객체를 인수로 전달하는 경우 값 호출을 사용하는 것에 비해 참조 호출을 사용하는 것이 효율적임
- 함수 호출의 효율성을 위해 참조 호출을 하지만 실 매개 변수의 값이 변경되는 것을 원하지 않는 경우에는 형식 매개 변수에
const
한정어를 지정하여 실 매개 변수를 보호
const
매개변수
const
한정어를 이용한 실 매개 변수 보호 - PrintRec.cpp1 2 3 4 5 6 7 8 9 10 11 12 13 14
struct SalesRec { char pID[10]; int dYear, dMonth, dDate; char deliverAddr[40]; }; // 62바이트(64바이트) void PrSalesRec(SalesRec srec) // 64바이트 복사 { cout << "품목코드 : " << srec.pID << endl; cout << "배달일자 : " << srec.dYear << "년 "; cout << srec.dMonth << "월 "; cout << srec.dDate << "일" << endl; cout << "배달주소 : " << srec.deliverAddr << endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14
struct SalesRec { char pID[10]; int dYear, dMonth, dDate; char deliverAddr[40]; }; // 62바이트(64바이트) void PrSalesRec(SalesRec &srec) // 4바이트 복사 { cout << "품목코드 : " << srec.pID << endl; cout << "배달일자 : " << srec.dYear << "년 "; cout << srec.dMonth << "월 "; cout << srec.dDate << "일" << endl; cout << "배달주소 : " << srec.deliverAddr << endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14
struct SalesRec { char pID[10]; int dYear, dMonth, dDate; char deliverAddr[40]; }; // 62바이트(64바이트) void PrSalesRec(const SalesRec &srec) // srec 보호 { cout << "품목코드 : " << srec.pID << endl; cout << "배달일자 : " << srec.dYear << "년 "; cout << srec.dMonth << "월 "; cout << srec.dDate << "일" << endl; cout << "배달주소 : " << srec.deliverAddr << endl; }
디폴트 인수
- 인수의 디폴트 값을 지정하는 방법
- 일반적으로 사용되는 디폴트 값이 있는 인수의 경우 함수를 정의할 때 그 값을 미리 지정할 수 있음
ex)
1
istream& getline(char* str, int count, char delimiter='\n');
1 2 3
char str1[10], str2[10]; cin.getline(str1, 10, ','); // ','가 나올 때까지 최대 9자 입력 cin.getline(str2, 10); // cin.getline(str2, 10, '\\n')과 동일
반올림 함수 Round - Round.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
double Round(double x, int d = 0); int main() { double a; cout << "값 = "; cin >> a; cout << "반올림 --> " << Round(a) << endl; cout << " " << Round(a, 1) << endl; cout << " " << Round(a, 2) << endl; cout << " " << Round(a, 3) << endl; return 0; } double Round(double x, int d) { double a = x >= 0 ? 0.5 : -0.5; double pow10 = pow(10, d); return trunc(x * pow10 + a) / pow10; } // 값 = 12.3456 // 반올림 --> 12 // 12.3 // 12.35 // 12.346
- 주의 사항
- 디폴트 인수는 인수 중 끝에만 위치할 수 있음
ex)
1 2
void f(int x, int y=10, int z=20); // OK void g(int x, int y=10, int z); // 오류
1
f(5); // x=5, y=10, z=20 전달
1
f(5, 100); // x=5, y=100, z=20 전달
1
f(5, 100, 200); // x=5, y=100, z=200 전달
1
f(5, ,300); // 오류: f(5, 10, 300);으로 작성
함수의 다중 정의
함수 다중 정의의 개념
- 다중 정의(overloading)
- 동일한 이름에 대하여 여러 가지 의미를 부여하는 것
- 함수 다중 정의
- 동일한 이름을 갖는 함수를 여러 개 정의하는 것
- 동일한 개념의 처리를 여러 가지 데이터나 객체에 대해 각각의 대상에 맞는 처리를 해야 할 경우 사용함
- 다중 정의 된 함수의 구분 기준
- 인수의 개수 및 자료형
- 함수의 반환 자료형으로 함수를 구분할 수 없음
함수 다중 정의의 예 - TimeCalc.cpp
시간 구조체
TimeRec
1 2 3 4
struct TimeRec { int hours; int minutes; };
AddTime
함수의 다중 정의1
void AddTime(TimeRec& t1, const TimeRec& t2);
1
void AddTime(TimeRec& t, int minutes);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct TimeRec {
int hours;
int minutes;
};
// 시간을 더하는 함수
// 인수 TimeRec &t1 : 누계할 시간
// const TimeRec &t2 : 더할 시간
// 반환값 없음
void AddTime(TimeRec &t1, const TimeRec &t2)
{
t1.minutes += t2.minutes;
t1.hours += t2.hours + t1.minutes / 60;
t1.minutes %= 60;
}
void AddTime(TimeRec &t, int minutes)
{
t.minutes += minutes;
t.hours += t.minutes / 60;
t.minutes %= 60;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main()
{
TimeRec tRec1 = { 2, 30 };
TimeRec tRec2 = { 1, 45 };
cout << tRec1.hours << "시간 " << tRec1.minutes << "분 + ";
cout << tRec2.hours << "시간 " << tRec2.minutes << "분 = ";
AddTime(tRec1, tRec2); // void AddTime(TimeRec &t1, const TimeRec &t2) 호출
cout << tRec1.hours << "시간 " << tRec1.minutes << "분" << endl;
cout << tRec1.hours << "시간 " << tRec1.minutes << "분 + ";
cout << "135분 = ";
AddTime(tRec1, 135); // void AddTime(TimeRec &t, int minutes) 호출
cout << tRec1.hours << "시간 " << tRec1.minutes << "분" << endl;
return 0;
}
주의: 모호한 함수 다중 정의
반환 자료형만 다른 경우
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
void f(int a) { cout << a * a; } void f(int a, int b) { cout << a * b; } int f(int a, int b) { // 에러! (반환 자료형만 다름) cout << a * b; } int main() { f(10); f(10, 20); }
선택 기준이 모호한 경우 (디폴트 인수)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
void g(int a) { cout << a * a; } void g(int a, int b = 100) { cout << a * b; } int main() { g(10, 20); g(10); // 에러! (선택 기준이 모호함) }
형 변환 대상이 모호한 경우
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
void h(int a) { cout << a * a; } void h(float a) { cout << a * b; } int main() { h(10); h(10.0f); h(10.0); // 에러! (형 변환 대상이 모호함) }
inline
함수
inline
함수의 개념
inline
함수란?- 함수 호출 절차를 따르지 않고 함수 호출 위치에 함수의 처리 문장이 삽입되게 번역하도록 선언된 함수
- 함수를 사용함으로써 얻을 수 있는 모듈화의 장점을 살리면서, 함수 호출에 따른 부수적인 처리 시간이 생략됨
- 함수 호출 루틴으로부터 함수로 넘어가는 부분의 코드 최적화가 가능해짐
- 매우 빈번히 호출되며 빠른 실행이 요구되는 함수를
inline
함수로 선언하면 성능을 높이는데 도움이 됨
- 매우 빈번히 호출되며 빠른 실행이 요구되는 함수를
- 주의
inline
함수로 선언하더라도 반드시inline
으로 번역되는 것은 아님inline
선언을 무시하고 일반 함수로 번역하는 경우- 함수가 너무 큰 경우
- 순환 호출(recursive call)을 하는 경우
- 프로그램 내에서 그 함수에 대한 포인터를 사용하는 경우
연습 문제
위 지문의 함수에서 ㈀에 넣을 내용은?
1 2 3 4 5 6 7
// ___㈀___ f(int a, int b) { if (a < b) cout << "작은 수 = " << a << endl; else cout << "작은 수 = " << b << endl; }
a.
void
- 값을 반환하지 않는 함수는
void
형으로 선언함
- 값을 반환하지 않는 함수는
다음 중 값 호출(call-by-value)에 대한 올바른 설명은? a. 함수에서 형식 매개 변수의 값을 변경해도 실 매개 변수의 값은 변하지 않는다.
- 실 매개 변수의 값을 형식 매개 변수에 복사하는 인수 전달 방식으로, 함수 내에서 형식 매개 변수의 값을 수정하는 것은 복사본을 수정하는 것이므로 원본인 실 매개 변수의 값에는 영향이 없음
함수
f
를 위 지문과 같이 선언하였다. int형 변수 a와 b에 각각 10, 15가 저장되어 있을 때,f(a, b)
를 호출한 후 변수 a와 b의 값은 무엇인가?1 2 3 4 5 6
int f(int x, int &y) { x *= x; y /= 2; return x * y; }
a.
a = 10, b = 7
- x는 값 호출, y는 참조 호출 방식으로 인수를 전달 받음
- 그러므로 x를 변화 시켜도 x에 값을 전달한 변수 a는 변화하지 않는 반면, y를 변화 시키는 것은 이에 대한 실 매개 변수의 값을 변화 시킨 것이 됨
위 지문에서 함수의 몸체인 ㈀에서 사용할 수 있는 명령은?
1 2 3 4
int g(const int arr[ ]) { // ___㈀___ }
a.
cout << arr[0];
arr
은 상수를 저장하는 배열이므로,arr[1]++;
이나cin >> arr[1];
와 같이arr
의 원소 값을 변경할 수 없음- 또한 int 형 함수이므로
return
명령은 int 형 수식을 반환하도록 해야 함
함수 다중 정의에 대한 올바른 설명은?
a. 동일한 이름으로 함수를 정의하되, 인수의 자료형이나 개수 등으로 구분할 수 있게 다중 정의한다.
- 함수 다중 정의는 동일한 이름의 함수를 여러 개 정의하는 것임
- 함수의 이름은 갖지만, 인수의 자료형 및 개수가 형식 매개 변수와 맞는 것을 선택하여 호출할 수 있음
정리 하기
- 함수는 특정 작업을 수행하는 프로그램 문장들을 하나의 단위로 모아 놓고 이름을 부여한 것으로 함수에 정의된 처리가 필요한 부분에서 호출하여 사용할 수 있음
- 함수를 호출할 때 함수의 처리에 사용할 인수를 매개 변수를 통해 전달함
- C++에서는 실 매개 변수의 값을 형식 매개 변수에 복사하는 값 호출 방식과 실 매개 변수의 참조를 형식 매개 변수에 전달하는 참조 호출 방식으로 인수를 함수에 전달할 수 있음
- 많은 양의 데이터로 구성되는 구조체나 객체를 인수로 전달하는 경우 값 호출을 사용하는 것에 비해 참조 호출을 사용하는 것이 효율적임
- 일반적으로 사용되는 디폴트 값이 있는 인수는 디폴트 인수로 지정할 수 있음
- 동일한 개념이지만 처리 대상에 맞게 동작하는 여러 개의 함수를 만들 때 이들을 같은 이름의 함수로 다중 정의할 수 있음
- 매우 빈번히 호출되며 빠른 실행이 요구되는 함수를
inline
함수로 선언하면 성능을 높이는 데 도움이 됨