값에 의한 호출 (Call by Value)
함수가 호출되면 매개변수가 스택에 생성된다. 호출하는 코드에서 값을 넘겨주고, 넘어온 값이 매개변수에 복사된다.
값에 의한 호출로 객체를 전달하면 객체 이름만 사용한다. 함수의 매개변수 객체를 생성하는데, 이때 매개변수 객체의 생성자는 호출되지 않는다. 단 함수가 종료될 때 매개변수 객체의 소멸자는 호출된다.
값에 의한 호출은 해당 값이 복사되어 사용되기 때문에 매개변수로 들어가는 원본 데이터는 함수 내에서의 일에 변화를 받지 않는다.
예를 들어 다음 코드를 보자.
#include <iostream>
using namespace std;
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 1, b = 2;
swap(a, b);
cout << a << ' ' << b << endl;
return 0;
}
a 와 b 가 swap 이라는 함수에 전달되었고, swap 함수는 a 와 b 를 바꾸었다. 그리고 cout 을 통해 출력되었는데, 실제로 확인해보면 출력은 1 2 가 나온다. 즉, a 와 b 는 바뀌지 않았다. a 와 b 가 함수에 전달될 때 원본이 전달된 것이 아니라 복사된 값이 전달되었기 때문에 함수가 실행되면서 새로운 a 와 b 가 만들어진 것이고, 이 새로운 a 와 b 의 값이 변경된 후에 함수가 종료되니 결과적으로 main 함수 내에서는 아무것도 바뀐 것이 없는 것이다.
주소에 의한 호출 (Call by Address)
함수의 매개변수가 포인터 타입으로, 함수가 호출되면 포인터 타입의 매개변수가 스택에 생성된다. 호출하는 코드에서 명시적으로 주소를 넘겨주고, 넘어온 주소 값이 매개변수에 저장된다.
주소에 의한 호출로 객체를 전달하면 당연히 객체의 주소만 전달된다. 따라서 객체의 생성자, 소멸자는 호출되지 않는다.
주소에 의한 호출은 주소를 전달받았기 때문에 이를 통해 값을 변경하면 당연히 원본 값도 변경된다. 단 기본적으로 주소와 자료형만 전달받기 때문에 예상치 못한 메모리 주소의 데이터를 건들 위험이 있다.
배열을 매개변수로 전달하면, 배열은 기본적으로 이름이 주소이므로 주소에 의한 호출로서 전달된다.
값에 의한 호출과 비교하기 위해 다음 코드를 보자.
#include <iostream>
using namespace std;
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 1, b = 2;
swap(&a, &b);
cout << a << ' ' << b << endl;
return 0;
}
이제 a 와 b 가 전달되는 것이 아니라 a 와 b 의 주소, 즉 포인터 변수가 전달되었다. 따라서 함수에서는 a 와 b 의 주소에 접근하여 값을 변경하였고, 따라서 원본 값이 변경되었다. 실제로 cout 을 통해 출력된 값은 2 1 이다.
참조에 의한 호출 (Call by Reference)
- 참조 변수 선언
참조자 &를 사용하여 참조 변수를 선언할 수 있다. 참조 변수는 변수 이름만 생기는 것이며 새로운 공간을 할당하지 않는다. 초기화로 지정된 기존 변수를 공유한다. 선언하는 기본적인 문법은 다음과 같다.
datatype &reference_variable = existing_variable;
참조 변수는 반드시 초기화되어야 하고, 자료형이 다른 변수를 참조할 수 없다.
- 참조에 의한 호출
함수의 매개변수가 참조 타입으로 주소에 의한 호출과 마찬가지로 따로 메모리 공간을 할당받지 않는다. 참조 매개변수는 원본 변수의 공간을 공유하고, 참조 매개변수를 변경하면 원본 변수 역시 변경된다. 주소에 의한 호출과 달리 원본 변수를 그대로 매개변수로 넘겨주면 되고, NULL 을 가리킬 수 있는 위험이 없어서 상대적으로 안전하다.
주소에 의한 호출과 코드를 비교하기 위해 다음 코드를 보자.
#include <iostream>
using namespace std;
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 1, b = 2;
swap(a, b);
cout << a << ' ' << b << endl;
return 0;
}
주소에 의한 호출과 같은 결과가 출력되는데, 함수에 매개변수를 전달할 때 주소에 의한 호출에서는 주소를 전달하기 위해 & 을 사용한 것과 달리 참조에 의한 호출은 그대로 전달하였다. 또한 함수 내부에서도 그대로 a, b 로 사용하였다.
- 참조 반환
C 에서 함수는 반드시 값을 반환해야 했지만, C++ 에서는 참조를 반환할 수 있다. 즉 변수 등과 같이 존재하는 공간에 대한 참조를 반환할 수 있다.
예를 들어 다음과 같은 코드는 오류가 날 것이다.
int c = 1;
int get() {
return c; // 변수 c의 값을 리턴
}
int a = get(); // a = 1 이 됨
get() = 2; // 컴파일 오류
그러나 참조를 반환하는 다음 코드는 작동한다.
int c = 1;
int& find() {
return c; // 변수 c에 대한 참조 리턴
}
int a = find(); // a = 1 이 됨
int &ref = find();
ref = 2; // c = 2 가 됨
find() = 3; // c = 3 이 됨
'Language > C & C++' 카테고리의 다른 글
[C/C++] 헤더파일 분할 작성과 헤더파일 중복 선언 방지 (0) | 2024.10.13 |
---|---|
[C++] 얕은 복사(shallow copy)와 깊은 복사(deep copy) 그리고 복사 생성자 (0) | 2024.10.13 |
[C++] 동적 메모리 할당 및 반환 (0) | 2024.10.13 |
[C++] 클래스(class)와 객체(object) 선언 (0) | 2024.09.18 |
[C] 입출력 형식과 서식 지정자 (0) | 2024.08.30 |