참조자
기존 변수의 별명인 참조자의 초기화와 원본 변경 방식을 익히고 값·참조 전달 및 포인터와의 차이를 구분합니다.
포인터로 다른 객체에 접근하려면 주소를 얻고, 쓸 때마다 *로 간접 참조하고, 비어 있지 않은지도 확인해야 했습니다. 대상이 반드시 있고 다른 객체로 바꿀 일도 없다면, 이 과정은 번거롭기만 합니다. 참조자(reference)는 이미 있는 객체에 다른 이름을 붙여, 그 이름을 원래 변수처럼 쓰게 해 줍니다.
이 글은 참조자를 선언하고 쓰는 방법, 포인터와 다른 점, 함수 매개변수와 반환값에 참조를 쓰는 방법, 읽기 전용인 const 참조를 다룹니다. 참조자를 이해하는 핵심은 참조자가 새 객체가 아니라 기존 객체의 별명(alias)이라는 점입니다.
참조자 선언
참조자는 타입 뒤에 &를 붙여 선언하고, 선언할 때 반드시 참조할 객체로 초기화합니다. 이 &는 포인터 기초에서 본 주소 연산자와 모양만 같고, 선언에서는 참조자라는 표시입니다.
#include <iostream>
int main() {
int num = 10;
int& ref = num;
std::cout << num << ' ' << ref << ' '
<< std::boolalpha << (&ref == &num) << '\n';
ref = 20;
std::cout << num << ' ' << ref << '\n';
num = 30;
std::cout << num << ' ' << ref << '\n';
}10 10 true
20 20
30 30ref는 num의 다른 이름이므로 &ref는 num의 주소와 같습니다. ref = 20은 num에 20을 대입하고, num = 30은 ref로 읽어도 30이 나옵니다. 포인터와 달리 * 없이 원래 변수처럼 쓰며, 참조자가 내부적으로 저장 공간을 쓰는지는 구현이 정할 일입니다.
초기화와 재지정
참조자는 선언할 때 대상이 정해지고, 그 뒤로는 다른 객체를 참조하도록 바꿀 수 없습니다. 그래서 int& r;처럼 초기화 없이 선언하면 컴파일 오류입니다.
#include <iostream>
int main() {
int val1 = 5;
int val2 = 15;
int& ref = val1;
ref = val2;
std::cout << val1 << ' ' << val2 << ' ' << ref << '\n';
}15 15 15ref = val2는 ref가 val2를 참조하게 바꾸는 것이 아니라, ref가 참조하는 val1에 val2의 값 15를 대입합니다. 그래서 val1이 15가 되고, ref는 여전히 val1의 별명입니다. 참조자에 대한 대입은 언제나 대상 객체에 대한 대입입니다.
사본, 별명, 포인터 구분하기
값이 우연히 같아도 같은 객체인지는 따로 확인해야 합니다. 다음 프로그램은 값 복사, 참조, 포인터를 한꺼번에 비교합니다.
#include <iostream>
int main() {
int first = 5;
int second = 15;
int& ref = first;
int saved = ref;
int* p = &first;
ref = second;
p = &second;
*p = 25;
std::cout << first << ' ' << second << ' ' << ref << ' ' << saved
<< ' ' << std::boolalpha << (&ref == &first) << '\n';
}15 25 15 5 true아래 그림처럼 처음에는 ref와 p가 모두 first와 관계를 맺고, saved는 ref의 값 5를 복사한 별도의 int입니다. ref = second는 first에 15를 대입할 뿐 ref의 대상을 바꾸지 않고, p = &second는 포인터가 가리키는 대상을 바꾸므로 *p = 25는 second를 25로 만듭니다. saved는 그 뒤의 변화와 상관없이 처음의 5를 유지합니다.
C++ 참조 바인딩과 포인터 계약
본문 추가 예제에서 first 5의 ref, 독립 saved 5, 재지정 가능한 p를 준비한 뒤 ref=second는 first에 15를 쓰고 p는 second 25를 가리킨다. 순서별 동일 객체와 사본을 구분하면 최종 관계 이해에 도움이 된다.
OBJECT IDENTITY · FIXED BINDING · OPTIONAL ADDRESS
참조자는 객체의 별명이고 포인터는 주소 값이다
T& ref = object;는 선언 순간 기존 객체에 바인딩되고, 이후 ref를 읽거나 쓰는 일은 그 객체를 읽거나 쓰는 일입니다. 포인터는 주소 값을 저장하므로 비어 있거나 다른 주소로 바뀔 수 있으며, 사용 전에 대상과 수명을 확인해야 합니다.
int& ref = value;
참조는 독립된 값을 가진 새 객체가 아니라 value를 가리키는 언어 수준의 별명입니다. 한 번 바인딩된 뒤 다른 객체의 별명으로 재지정되지 않습니다.
int* ptr = &value;
포인터는 주소 값을 보유합니다. nullptr로 대상 부재를 표현하거나 &other를 대입해 다른 객체를 가리킬 수 있습니다.
참조자 대입은 원본 객체의 값을 바꾼다
int first = 5;
int second = 15;
int& ref = first;
ref = second;
// first가 15가 됨
// ref는 여전히 first의 별명
&ref == &first 관계는 유지됩니다. 대입문의 오른쪽 값이 참조 대상에 쓰일 뿐, 바인딩이 second로 이동하지 않습니다.
| 형태 | 대상 계약 | 사용과 변경 | 수명 점검 |
|---|---|---|---|
| T& | 호출 시 실제 대상이 필요하며 널 상태를 표현하지 않습니다. | 역참조 문법 없이 읽고 쓰며, 다른 대상으로 재바인딩되지 않습니다. | 대상 객체가 먼저 소멸하면 참조는 댕글링 상태가 됩니다. |
| const T& | 실제 대상을 읽기 전용으로 빌립니다. | 복사 없이 관찰하지만 이 참조를 통해서는 수정할 수 없습니다. | 소유권을 갖지 않으므로 원본 또는 바인딩된 임시 객체의 수명 규칙을 따라야 합니다. |
| T* | nullptr로 대상 부재를 표현할 수 있습니다. | 다른 주소로 재지정할 수 있고, 접근할 때 * 또는 ->가 필요합니다. | 널 여부만으로 충분하지 않으며 객체 존재·수명·범위·타입을 확인해야 합니다. |
필수이며 수정 가능한 고정 대상
직접 읽고 쓰지만 재바인딩되지 않습니다. 대상이 먼저 소멸하면 댕글링 참조가 됩니다.
필수이며 읽기 전용인 빌림
이 경로로 수정하지 않고 관찰합니다. 소유권은 없으며 원본이나 바인딩된 임시 객체의 수명 규칙을 따라야 합니다.
선택 가능하고 재지정 가능한 주소
nullptr와 다른 주소를 표현할 수 있으며, 역참조 전에 대상과 수명을 검증합니다.
참조에 포인터 같은 well-defined 널 상태는 없지만, 이것이 런타임 유효성을 자동으로 증명한다는 뜻은 아닙니다. 참조를 만드는 코드와 사용하는 코드는 대상 객체가 살아 있다는 수명 계약을 함께 지켜야 합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 본문 추가 예제의 최종 상태는 5개 노드와 2개 간선이다. ref는 first 15의 고정 별명이며 p는 재지정한 second 25의 주소를 가진 포인터 객체다. saved는 처음 ref에서 복사한 5를 보관하는 별도 int 객체이며 현재 대상 관계 간선이 없다. 참조 대입 ref=second는 first에 15를 썼고 ref를 second로 옮기지 않았다. 모바일도 두 관계와 독립 사본을 유지한다. 물리적인 참조 저장 공간의 그림은 아니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 사본과 별명 first의 값은 5이며 ref가 이를 참조한다. 독립 사본 saved의 값도 5이고 p도 first를 가리킨다. |
|
|
| ref에 값 대입 ref=second는 참조의 대상을 바꾸지 않는다. |
|
|
| 포인터 재지정·변경 p는 second를 가리키며 그 대상 second의 값이 25로 바뀐다. |
|
|
C++ 참조 바인딩과 포인터 계약
본문 추가 예제에서 first 5의 ref, 독립 saved 5, 재지정 가능한 p를 준비한 뒤 ref=second는 first에 15를 쓰고 p는 second 25를 가리킨다. 순서별 동일 객체와 사본을 구분하면 최종 관계 이해에 도움이 된다.
OBJECT IDENTITY · FIXED BINDING · OPTIONAL ADDRESS
참조자는 객체의 별명이고 포인터는 주소 값이다
T& ref = object;는 선언 순간 기존 객체에 바인딩되고, 이후 ref를 읽거나 쓰는 일은 그 객체를 읽거나 쓰는 일입니다. 포인터는 주소 값을 저장하므로 비어 있거나 다른 주소로 바뀔 수 있으며, 사용 전에 대상과 수명을 확인해야 합니다.
int& ref = value;
참조는 독립된 값을 가진 새 객체가 아니라 value를 가리키는 언어 수준의 별명입니다. 한 번 바인딩된 뒤 다른 객체의 별명으로 재지정되지 않습니다.
int* ptr = &value;
포인터는 주소 값을 보유합니다. nullptr로 대상 부재를 표현하거나 &other를 대입해 다른 객체를 가리킬 수 있습니다.
참조자 대입은 원본 객체의 값을 바꾼다
int first = 5;
int second = 15;
int& ref = first;
ref = second;
// first가 15가 됨
// ref는 여전히 first의 별명
&ref == &first 관계는 유지됩니다. 대입문의 오른쪽 값이 참조 대상에 쓰일 뿐, 바인딩이 second로 이동하지 않습니다.
| 형태 | 대상 계약 | 사용과 변경 | 수명 점검 |
|---|---|---|---|
| T& | 호출 시 실제 대상이 필요하며 널 상태를 표현하지 않습니다. | 역참조 문법 없이 읽고 쓰며, 다른 대상으로 재바인딩되지 않습니다. | 대상 객체가 먼저 소멸하면 참조는 댕글링 상태가 됩니다. |
| const T& | 실제 대상을 읽기 전용으로 빌립니다. | 복사 없이 관찰하지만 이 참조를 통해서는 수정할 수 없습니다. | 소유권을 갖지 않으므로 원본 또는 바인딩된 임시 객체의 수명 규칙을 따라야 합니다. |
| T* | nullptr로 대상 부재를 표현할 수 있습니다. | 다른 주소로 재지정할 수 있고, 접근할 때 * 또는 ->가 필요합니다. | 널 여부만으로 충분하지 않으며 객체 존재·수명·범위·타입을 확인해야 합니다. |
필수이며 수정 가능한 고정 대상
직접 읽고 쓰지만 재바인딩되지 않습니다. 대상이 먼저 소멸하면 댕글링 참조가 됩니다.
필수이며 읽기 전용인 빌림
이 경로로 수정하지 않고 관찰합니다. 소유권은 없으며 원본이나 바인딩된 임시 객체의 수명 규칙을 따라야 합니다.
선택 가능하고 재지정 가능한 주소
nullptr와 다른 주소를 표현할 수 있으며, 역참조 전에 대상과 수명을 검증합니다.
참조에 포인터 같은 well-defined 널 상태는 없지만, 이것이 런타임 유효성을 자동으로 증명한다는 뜻은 아닙니다. 참조를 만드는 코드와 사용하는 코드는 대상 객체가 살아 있다는 수명 계약을 함께 지켜야 합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 본문 추가 예제의 최종 상태는 5개 노드와 2개 간선이다. ref는 first 15의 고정 별명이며 p는 재지정한 second 25의 주소를 가진 포인터 객체다. saved는 처음 ref에서 복사한 5를 보관하는 별도 int 객체이며 현재 대상 관계 간선이 없다. 참조 대입 ref=second는 first에 15를 썼고 ref를 second로 옮기지 않았다. 모바일도 두 관계와 독립 사본을 유지한다. 물리적인 참조 저장 공간의 그림은 아니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 사본과 별명 first의 값은 5이며 ref가 이를 참조한다. 독립 사본 saved의 값도 5이고 p도 first를 가리킨다. |
|
|
| ref에 값 대입 ref=second는 참조의 대상을 바꾸지 않는다. |
|
|
| 포인터 재지정·변경 p는 second를 가리키며 그 대상 second의 값이 25로 바뀐다. |
|
|
| 특징 | 포인터 | 참조자 |
|---|---|---|
| 정체 | 주소를 값으로 담는 객체 | 기존 객체의 다른 이름 |
| 초기화 | 나중에 대입할 수 있고, nullptr도 가능 | 선언할 때 반드시 대상 객체로 초기화 |
| 대상 바꾸기 | 다른 주소를 대입해 바꿀 수 있음 | 바꿀 수 없음 |
| 빈 상태 | nullptr | 올바르게 만든 참조에는 없음 |
| 접근 | *p로 간접 참조 | 이름 그대로 사용 |
참조자에는 빈 상태가 없지만, 대상 객체가 먼저 사라지면 허상 참조(dangling reference)가 됩니다. 허상 포인터처럼, 수명이 끝난 객체를 참조자로 쓰는 것도 정의되지 않은 동작입니다.
함수 매개변수로 쓰기
함수의 정의와 호출에서 본 값 매개변수는 인자의 사본이어서, 함수 안에서 바꿔도 호출한 쪽의 변수는 그대로였습니다. 매개변수를 참조로 선언하면 매개변수가 인자로 넘긴 변수 자체의 별명이 되므로, 함수가 호출한 쪽의 변수를 바꿀 수 있습니다.
#include <iostream>
void increment(int& value) {
++value;
}
void swapValues(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int count = 10;
increment(count);
int x = 1;
int y = 2;
swapValues(x, y);
std::cout << count << ' ' << x << ' ' << y << '\n';
}11 2 1increment(count)의 value는 count의 별명이어서 ++value가 count를 11로 만들고, swapValues(x, y)는 x와 y의 값을 맞바꿉니다. 같은 일을 포인터로 하려면 swapPointers(&x, &y)처럼 호출할 때 주소를 넘기고 함수 안에서 *a로 간접 참조해야 하지만, 참조를 쓰면 호출이 간결하고 널 검사도 필요 없습니다. 대신 호출문 swapValues(x, y)만 보고는 인자가 바뀐다는 것을 알 수 없으므로, 함수 이름이나 선언으로 바뀐다는 것이 드러나게 합니다.
const 참조
참조를 const로 선언하면 그 참조로는 대상을 읽기만 하고 바꿀 수 없습니다. 복사하지 않으면서 원본이 바뀌지 않는다는 것도 보장하므로, 문자열이나 컨테이너처럼 복사 비용이 큰 인자를 읽기만 할 때 씁니다.
#include <iostream>
#include <string>
void printName(const std::string& name) {
// name += "!"; // 컴파일 오류: const 참조로는 바꿀 수 없음
std::cout << "name: " << name << '\n';
}
int main() {
std::string user = "Ada";
printName(user);
printName("Grace");
}name: Ada
name: Graceconst 참조는 수정할 수 있는 참조와 달리 임시 객체에도 묶일 수 있습니다. printName("Grace")에서는 리터럴로 임시 std::string이 만들어져 name에 묶이고, 함수 호출이 끝나면 사라집니다. 반면 int& r = 20;처럼 수정할 수 있는 참조를 임시 값에 묶는 것은 컴파일 오류입니다.
더 깊이: 임시 객체의 수명이 늘어나는 경우와 늘어나지 않는 경우
const std::string& name = std::string("Grace");처럼 지역 참조를 임시 객체에 묶으면, 그 임시 객체는 문장 끝에서 사라지지 않고 참조가 범위를 벗어날 때까지 살아 있습니다. 이 규칙은 임시 객체의 멤버나 배열 원소에 참조를 묶을 때도 적용되어, 임시 객체 전체의 수명이 늘어납니다.
하지만 수명이 늘어나는 것은 임시 객체를 직접 묶은 참조 하나에 대해서입니다. 함수의 참조 매개변수에 묶인 임시 객체는 그 호출이 들어 있는 식이 끝날 때 사라지므로, 받은 참조를 그대로 돌려주는 pick 같은 함수의 결과를 const std::string& r = pick(std::string("a"));로 받으면 r은 다음 문장에서 허상 참조가 됩니다. 이미 묶인 참조로 다른 참조를 초기화해도 수명은 다시 늘어나지 않으며, return 문에서 반환 참조에 묶인 임시 객체도 반환식이 끝나자마자 사라집니다. C++26부터는 이렇게 반환 참조를 임시 객체에 묶는 return 문을 컴파일 오류로 정했습니다.
그래서 수명이 늘어난다는 규칙에 기대기보다, 참조로 받은 값이 어디에서 왔는지를 따라가 보는 편이 안전합니다. 자세한 예외 목록은 cppreference의 참조 초기화 문서에 있습니다.
매개변수 형태 고르기
매개변수의 형태는 성능뿐 아니라, 함수가 인자를 어떻게 다룰지를 호출하는 사람에게 알려 줍니다. 아래 그림은 네 가지 형태를 고르는 기준입니다.
VALUE · BORROW · MUTATION · OPTIONAL TARGET
매개변수 형태는 함수가 원본을 어떻게 다룰지 선언한다
값, const T&, T&, T*의 차이는 성능만이 아니라 소유·변경·대상 존재성에 대한 API 계약입니다. 호출자가 시그니처만 보고도 사본을 만드는지, 원본을 바꾸는지, 대상이 없을 수 있는지를 알 수 있어야 합니다.
| 형태 | 선택 조건 | 호출자에게 보이는 계약 | 주의할 수명·비용 |
|---|---|---|---|
| T value | 작은 값이거나 함수가 독립 사본·소유 값을 필요로 합니다. | 함수 내부 변경은 호출자의 원본을 직접 바꾸지 않습니다. | 호출 과정에서 복사 또는 이동이 일어날 수 있습니다. |
| const T& | 대상이 반드시 있고 함수가 읽기만 하며 복사를 피하려 합니다. | 원본을 빌려 관찰하고 이 참조를 통해 수정하지 않습니다. | 큰 문자열·컨테이너에 유용하지만 소유권은 없으며, 호출 뒤 보관하려면 별도 수명 계약이 필요합니다. |
| T& | 함수가 반드시 존재하는 호출자 객체를 수정해야 합니다. | 호출 뒤 원본 값이 달라질 수 있음을 시그니처가 드러냅니다. | 대상 수명은 호출 전체를 포함해야 하며 재바인딩은 일어나지 않습니다. |
| T* | 대상이 없을 수 있거나 주소를 다루는 API 경계입니다. | nullptr 처리 정책과 수정 가능성을 문서화해야 합니다. | 역참조 전에 대상 존재·수명·범위·타입을 검증합니다. |
독립 사본 또는 소유 값
작은 값이나 함수 내부 보관이 필요할 때 선택합니다. 원본을 직접 수정하지 않으며 복사·이동 비용을 고려합니다.
필수 대상, 무복사 읽기
큰 객체를 빌려 읽되 수정하지 않습니다. 호출 뒤 보관하려면 대상이 더 오래 산다는 별도 계약이 필요합니다.
필수 대상, 원본 수정
함수가 호출자의 객체를 바꾼다는 사실을 시그니처로 드러냅니다.
선택적 대상
대상이 없을 수 있을 때 사용하고 nullptr 정책과 역참조 조건을 명시합니다.
호출 뒤에도 살아 있는 객체만 참조로 반환한다
호출자가 소유한 객체, 수명이 충분한 멤버, 정적 저장 기간 객체처럼 반환 참조의 사용보다 오래 살아 있는 대상이어야 합니다. 반환 타입은 복사가 아니라 그 객체에 대한 접근을 제공합니다.
지역 변수의 참조를 반환하지 않는다
함수가 끝나면 지역 객체의 수명이 종료됩니다. 그 참조를 반환하면 즉시 댕글링 상태가 되고, 호출자가 읽거나 쓰는 순간 정의되지 않은 동작입니다.
const T&는 임시 값을 받을 수 있지만 보관 권한은 주지 않는다
참조 매개변수에 바인딩된 임시 객체는 그 함수 호출을 포함하는 전체 표현식이 끝날 때까지 유효합니다. 함수가 그 참조나 포인터를 이후 사용하려고 저장하면 수명 계약을 위반합니다.
“복사가 싫다”만으로 참조를 선택하지 않습니다. 함수가 대상을 필수로 요구하는지, 원본 변경을 약속하는지, 호출 뒤 값을 보관해야 하는지를 먼저 정하면 형태와 수명 책임이 함께 선명해집니다.
| 형태 | 고를 때 | 호출하는 쪽이 알 수 있는 것 |
|---|---|---|
T value | 작은 값이거나, 함수가 자기만의 사본을 가져야 할 때 | 원본은 바뀌지 않음 |
const T& value | 반드시 있는 큰 객체를 읽기만 할 때 | 복사 없이 빌려 보고, 바꾸지 않음 |
T& value | 반드시 있는 호출자의 객체를 바꿔야 할 때 | 호출 뒤 원본이 바뀔 수 있음 |
T* value | 대상이 없을 수도 있을 때 | nullptr을 넘길 수 있음 |
int나 double 같은 작은 값은 값으로 받는 편이 단순하고, 참조로 받아도 빨라지지 않습니다. const T&로 받은 객체는 빌린 것이므로, 함수가 그 참조를 저장해 두었다가 호출이 끝난 뒤에 쓰면 원본이 이미 사라졌을 수 있습니다.
참조를 반환하기
함수가 참조를 반환하면, 호출식이 그 대상 객체 자체를 나타내므로 호출 결과에 대입할 수도 있습니다. 아래 getCounter는 반환 타입이 int가 아니라 int&이며, main은 그 호출식을 대입의 왼쪽에 둡니다. 반환 타입이 int였다면 호출 결과는 이름 없는 임시 값이어서, 같은 대입이 컴파일되지 않습니다.
#include <iostream>
int counter = 100;
int& getCounter() {
return counter;
}
int main() {
getCounter() = 200;
std::cout << counter << '\n';
}200getCounter()는 전역 변수 counter의 참조를 반환하므로, getCounter() = 200은 counter에 200을 대입합니다. 이렇게 참조를 반환하는 방식은 뒤의 장에서 볼 클래스의 멤버 접근 함수나 연산자 오버로딩에서 자주 씁니다.
반환하는 참조의 대상은 호출한 쪽이 쓰는 동안 살아 있어야 합니다. 함수의 지역 변수는 함수가 끝나면 사라지므로, 지역 변수의 참조를 반환하면 호출한 쪽은 허상 참조를 받게 됩니다. GCC는 기본으로 켜진 -Wreturn-local-addr로 지역 변수의 주소나 참조를 반환하는 코드를 경고합니다.
정리
- 참조자는 기존 객체의 다른 이름이며,
T& r = 객체;처럼 선언할 때 반드시 대상으로 초기화합니다. - 참조자의 대상은 바꿀 수 없으며, 참조자에 대입하면 대상 객체에 값이 대입됩니다.
- 포인터와 달리
*없이 쓰고 빈 상태가 없지만, 대상이 사라지면 허상 참조가 됩니다. - 참조 매개변수는 호출한 쪽의 변수를 바꿀 수 있고,
const참조 매개변수는 복사 없이 읽기만 합니다. const참조는 임시 객체에 묶일 수 있지만, 수정할 수 있는 참조는 임시 값에 묶일 수 없습니다.- 참조를 반환할 때는 대상이 호출한 쪽에서 쓰는 동안 살아 있어야 하며, 지역 변수의 참조는 반환하지 않습니다.
연습 문제
1. 대상이 바뀌지 않는 참조
copy_alias_pointer.cpp에서 *p = 25;만 second = 30;으로 바꾸면 출력은 무엇인가요? 그 뒤에 ref = 7;을 추가하면 second도 7이 되나요?
해설 보기
first는 ref = second로 15가 되고 second는 30이 되므로 출력은 15 30 15 5 true입니다. ref = 7;을 추가하면 ref의 대상인 first만 7이 되고, second는 30, saved는 5 그대로입니다. ref는 처음부터 끝까지 first의 별명입니다.
2. 값으로 받은 교환 함수
swapValues의 매개변수를 int a, int b로 바꾸면 swap_values.cpp의 출력은 어떻게 되나요?
해설 보기
a와 b가 x와 y의 사본이 되어, 함수 안에서는 사본끼리만 맞바뀌고 x와 y는 그대로입니다. 출력은 11 1 2입니다. 호출한 쪽의 변수를 바꾸려면 매개변수를 참조나 포인터로 받아야 합니다.
3. 매개변수 형태 고르기
다음 세 함수에 알맞은 매개변수 형태는 무엇인가요? (가) 원소가 많은 std::vector<int>의 합을 구하는 함수 (나) 계산 결과를 호출자의 int 변수에 반드시 써 주는 함수 (다) 결과를 받을 변수를 넘기지 않아도 되는 함수
해설 보기
(가)는 반드시 있는 큰 객체를 읽기만 하므로 const std::vector<int>&, (나)는 반드시 있는 호출자의 변수를 바꾸므로 int&가 알맞습니다. (다)는 대상이 없을 수도 있으므로 int*로 받고, 함수 안에서 nullptr이 아닐 때만 결과를 씁니다. 계산 결과는 반환값으로 돌려주는 방법이 가장 단순한 경우도 많습니다.
다음 글 포인터와 배열에서는 배열 이름이 첫 원소의 포인터로 바뀌는 규칙과, 포인터에 정수를 더해 다음 원소로 이동하는 포인터 산술을 다룹니다.