[c++] 기초강좌 #03(함수의 매개변수 전달)
함수간의 매개변수 전달
함수에 매개변수를 전달하는 방법은 값에 의한 매개변수 전달과 주소에 의한 매개변수 전달이 있다. 또는 값에 의한 호출, 주소에 의한 호출이라고 한다.
값에 의한 호출은 매개 변수 전달 시 매개변수의 값을 복사하여 사용한다. 따라서 원변수의 내용은 바뀌지 않는다. 그러나 주소에 의한 호출은 변수의 주소를 참조하므로 변수의 내용이 바뀌게 된다. 앞에서 우리는 포인트를 배웠다. 이 포인트를 이용하는 방법이 주소에 의한 함수 호출이 되겠다. C++에서 추가된 전달방법이 레퍼런스이다.
레퍼런스도 포인트와 비슷하다. 아니 포인트라고 해도 타당하다.
아래의 예제는 값, 포인트, 레퍼런스에 의한 호출을 비교한 프로그램이다.
예제 | ☞ | 값, 포인트, 레퍼런스에 의한 호출 비교 |
#include
#include
#include
void call_by_value(int x){ // 값에 의한 호출 함수
x=x * 2;
}
void call_by_point(int * x){ // 포인트에 의한 호출 함수
*x=*x * 2;
}
void call_by_reference(int & x){ // 레퍼런스에 의한 호출 함수
x=x * 2; // 자동 역참조된다. 역참조 연산자 '*'이 필요 없다.
}
void main(){
int v=10,p=20,r=30;
clrscr();
call_by_value(v); // 함수 호출 후 v의 값이 바뀌지 않음
cout <<"v= " << v << endl ;
call_by_point(&p); // 함수 호출 후 p의 값이 바뀜
cout <<"p= " << p << endl ;
call_by_reference(r); // 함수 호출 후 r의 값이 바뀜
cout <<"r= " << r << endl ;
getch();
}
위 예제에서 보듯이 레퍼런스는 자동 역참조되는 포인트이다. 포인트를 사용하는 것보다 훨씬 간결하고 역참조 연산자를 사용하지 않으므로 이해하기도 쉽다.
레퍼런스 매개변수를 부주의하게 변경하지 못하도록 하기 위해 받아들인 레퍼런스 매개변수 앞에 const를 둘 수 있다.
먼저, 받는 함수가 호출 함수의 값을 변경하지 못하게 할 때 왜 레퍼런스에 의해 매개변수를 전달하는지 궁금할 것이다. 값에 의해 매개변수를 전달하는 것이 더 수월하고 받는 함수가 보내는 함수의 값을 건드리지 못할 것 같다.
값에 의해 매개변수를 전달하는 것은 프로그램의 몇 가지 비효율성을 만든다. 값에 의해 변수를 전달할 때 특히 커다란 구조체 변수일 경우, Turbo C++는 변수의 지역 복사본을 만들기 위해 상당한 실행시간을 요구한다. (그것은 by value라는 용어를 흔히 by copy라고도 하는 이유이다).
그러나, 주소나 레퍼런스에 의해 변수를 전달할 때 데이터 그 자체보다는 데이터에 대한 포인터가 전달된다.(레퍼런스에 의한 전달 때 포인터는 자동적으로 역참조된다). 그러므로, 효율성이 중요하면, 레퍼런스에 의한 전달이 좋은데, 받는 함수가 매개변수를 변경하지 못하도록 하려면 const를 받는 값 앞에 두는 것이다.
아래의 max( x , y)는 x, y 중 큰 수를 반환하다. 정확히 말하자면 큰 수의 레퍼런스를 반환한다. 즉 값이 아닌 변수를 반환하는 것이다.
int & max(int & x, int & y){
return (x > y)? x: y;
}
따라서 아래의 식이 성립된다.
max(a, b)=0; // a, b중 큰 수의 값이 0이 된다.
물론 z= max(a, b)의 형태도 된다.
아래의 예제는 레퍼런스 반환을 보여주고 있다.
예제 | ☞ | 레퍼런스 반환 |
#include
#include
#include
int & max(int & x, int & y){
return (x > y)? x: y;
}
void main(){
int a=10,b=20,z;
clrscr();
z=max(a, b); // z=20이 된다.
cout << "z= " << z << endl;
max(a, b)=0; // b=0이 된다. a의 값(10)은 변화 없다.
cout << "a= " << a << endl;
cout << "b= " << b << endl;
getch();
}
두 수의 합을 반환화는 sum(x, y)함수를 만들어 보자.
int sum (int x, int y){
return (x+ y);
}
세 수의 합을 반환하는 함수 sum2(x, y, z)를 만들어 보자.
int sum (int x, int y, int z){
return (x+ y+ z);
}
4, 5, 6,...가지 수의 합을 구하는 함수가 필요하면 별 수 없이 모두 만들어야 했다. 하지만 C++는 디폴트 인수를 사용하여 여러분의 타이핑 수고를 많이 들어준다.
int sum (int a, int b, int c = 0, int d = 0, int e = 0){
return (a+ b+ c+ d+ e);
}
매개변수에 값이 주어진 것들이 있다. c, d, e가 디폴트 매개변수(인수)이다.
디폴트 인수는 생략할 수 있다. 생략하면 디폴트 값(c = 0, d = 0, e = 0)이 들어간다.
x=sum(1, 2); // x = 3 : a = 1, b = 2, c = 0, d = 0, e = 0
x=sum(1, 2, 3); // x = 6 : a = 1, b = 2, c = 3, d = 0, e = 0
x=sum(1, 2, 3, 4); // x = 10 : a = 1, b = 2, c = 3, d = 4, e = 0
x=sum(1, 2, 3, 4, 5); // x = 15 : a = 1, b = 2, c = 3, d = 4, e = 5
마술 같은 일이 아닐 수 없다.(C++ 제작팀에게 감사하는 마음을 가지자....^^)
하나의 함수로 위와 같이 여러 가지 경우를 처리할 수가 있다.
매개변수에 디폴트 값을 주는 것을 '매개변수 초기화' 라고 한다.
초기화된 매개변수는 생략이 가능하고 반대로 초기화되지 않은 매개변수는 생략이 불가하다. x=sum(1)은 불가능하다.
또 초기화된 매개변수는 매개변수 리스트에서 맨 뒤로 보내야한다. 아래의 함수는 에러다.
int func(int x, int y = 0, int z){...}
int func(int x, int z, int y = 0){...} // 에러 없음
조금만 생각해보면 당연하다는 것을 알 수 있을 것이다.
cin.getline(city,25), cin.getline(city,25,'#')에서 마지막 매개변수가 디폴트 인수이다. 마지막 인수를 생략하면 cin.getline(city, 25, 'n')이 된다. |
혼자 해보기 | |
평균을 구하는 avg()함수를 만들어 보자. 매개변수는 4개이고 디폴트 매개변수는 2개가 되어야 한다. |
a = 5 * 3 ; // (1)
b = * p; // (2)
연산자 '*'는 (1)에서는 곱셈을 의미하고, (2)에서는 포인트임을 의미한다. 같은 연산자지만 형태에 따라 Turbo C++는 정확히 구별하고 있다. 즉 연산자 '*'가 다중 정의되어 있음을 의미한다. 연산자 다중정의를 '연산자 오버로드'라고 한다.
( 비트연산자 '<<' 와 cout의 '<<'는 오버로드 연산자이다.)
그렇다면 오버로드 함수는 무엇인가?
다중 정의된 함수를 오버로드 함수라 하며, 오버로드 함수를 작성하는 행위를 함수 오버로딩이라고 한다.
int func(int i){ return i; } // (1)
char func(char c){ return c; } // (2)
float func(float f){ return f; } // (3)
함수 func()는 단순히 매개 변수의 값을 반환하고 있다. func()는 다중정의 되어 있음을 볼 수 있다.
a=func(10);
위의 문장에서 (1),(2),(3) 중 어느 것이 호출될까?
생각할 것도 없이 (1)이 호출됨을 알 수 있다.
매개변수의 자료형에 따라 그에 맞는 함수가 호출된다.
"오버로드 함수는 함수의 이름은 같고 매개변수의 개수나 매개변수의 자료형이 달라야한다."
void good(int a){...}
int good(int a, int c){...}
매개변수의 개수가 다르므로 good()는 타당한 오버로드 함수이다.
char bad(int a){...} // (1)
int bad(int a, int b = 1){...} // (2)
위의 경우는 어떤가?
bad(1)을 호출하면 어느 것이 호출될까? (2)에서 매개 변수 b는 생략이 가능하다.
그러므로 어느 함수를 호출할 지 애매하다.
Turbo C++ 는 어느 함수를 호출해야 할지 애매하다는 에러 메시지를 보내고 컴파일을 중단한다. 따라서 위의 문장은 잘못된 것이므로 다음과 같이 수정해야한다.
char bad(int a){...}
int bad(int a, int b){...}
혼자 해보기 | |
같은 이름의 두 함수를 가진 프로그램을 작성해 보자. 첫 번째 함수는 그것에 전달되는 정수형 배열의 평균을 반환하고, 두 번째 함수는 부동-소수형 배열의 평균을 반환한다. |
연산자 '+'를 오버로딩해 보자.
int operator + (const struct buza & x, const struct buza & y){
return x.money + y.money;
}
a = x.money + y.money; // 이렇게 하기보다는 아래의 문장이 코드 량을 줄여준다.
a= x + y; // 또는 a = operator + ( x , y );
예제 | ☞ | '+' 오버로딩 |
#include
#include
#include
struct buza{
int money;
};
int operator + (const struct buza & x, const struct buza & y){
return x.money + y.money;
}
void main(){
buza a, b;
a.money = 10;
b.money = 20;
clrscr();
cout << a + b;
getch();
}
-
[html] 캐쉬된 웹페이지 사용하지 않도록 하는 방법
-
[js] 이미지 미리 로딩하기
-
[js] 핫키(단축키) 구현방법
-
[js] 키보드 아스키 코드 확인하는 간단한 소스
-
[jsp] 유효성체크(Client, Server 에서)
-
[c] C로 구현한 CGI - 달력과 날짜계산기
-
[c] C로 구현한 CGI - 계산기 (링크리스트, 스택, 이진트리)
-
[c] C로 구현한 CGI - 방명록
-
[c++] 기초강좌 #04(클래스)
-
[c++] 기초강좌 #03(함수의 매개변수 전달)
-
[c++] 기초강좌 #02(레퍼런스,메모리할당)
-
[c++] 기초강좌 #01(입출력,영역지정)