요약
이 수업은 데이터가 어떤 길로 함수에 들어오고 그 결과가 어떤 길로 나가는지를 공부한다. 기본적으로 C++는 인자를 값으로 전달한다. 매개변수는 복사본이며, 함수가 그것에 무엇을 하든 원본에는 닿지 않는다. 반면에 참조는 이미 있는 메모리 칸의 두 번째 이름이며, 함수가 호출하는 쪽의 변수를 수정할 수 있게 한다. 상수 참조는 큰 데이터를 복사하지 않고, 또 바꿀 수 없는 상태로 읽게 해 준다. 여러 결과를 반환하기 위해 라이브러리는 std::pair 타입과 std::tuple 타입을 제공하며, 이것들은 구조적 바인딩으로 받는다. 이어서 이름의 유효 범위(어디에서 보이는가)와 변수의 수명(언제 존재하는가)을 이름 가리기 및 static 지역 변수와 함께 다루고, 자기 변수보다 오래 살아남는 참조로 마무리한다. 이 모든 것을 하나로 묶는 개념은 프로그램의 부분들 사이를 흐르는 데이터다. 곧 복사와 공유의 대비, 그리고 격리의 장치로서 이름의 가시성이다.
학습 목표
- 인자를 값으로 전달하는지 참조로 전달하는지에 따라 함수가 인자에 미치는 효과를 예측한다.
- 각 매개변수의 전달 방식(작은 자료형은 값, 큰 데이터는 상수 참조, 수정하는 것은 참조)을 고르고 그 선택을 정당화한다.
std::pair타입이나std::tuple타입으로 여러 결과를 반환하고 구조적 바인딩으로 받는다.- 프로그램의 각 변수의 유효 범위와 수명을,
static지역 변수와 다른 변수에 가려진 변수까지 포함하여 결정한다. - 이미 존재하지 않는 지역 변수나 임시 값에 대한 참조를 그것을 알려 주는 컴파일러 경고와 함께 알아보고 고친다.
값 전달
매개변수는 복사본이다
함수 수업에서는 호출할 때마다 함수의 매개변수와 지역 변수를 담은 프레임이 스택에 만들어지고, 반환할 때 그 프레임이 사라진다는 것을 보았다. 남은 일은 매개변수가 그것을 초기화하는 인자와 어떤 관계에 있는지 밝히는 것이다. C++의 기본 답은 가장 단순한 것이다. 매개변수는 새 지역 변수이고, 인자 값의 복사본으로 초기화된다. 그렇다면 자기 매개변수를 수정하는 함수는 그 복사본을 수정하는 것이고, 호출하는 쪽의 변수는 바뀌지 않는다. 아래 프로그램은 두 정수를 교환하려는 함수로 이를 시험한다.
#include <print>// a와 b를 교환하려 한다. 교환되는 것은 그 복사본뿐이다.void swapValues(int a, int b){ const int saved{a}; a = b; b = saved; std::println("함수 안: a = {}, b = {}", a, b);}int main(){ int x{1}; int y{2}; swapValues(x, y); std::println("호출 후: x = {}, y = {}", x, y);}함수 안: a = 2, b = 1호출 후: x = 1, y = 2함수 안에서는 교환이 제대로 이루어지지만, 호출 뒤에 x 변수와 y 변수는 원래 값을 유지한다. 컴파일러는 경고하지 않는다. 경고할 것이 없기 때문이다. 이 코드는 끝나면 사라지는 두 지역 변수를 교환할 뿐이다. 그림은 호출 동안의 스택 프레임을 따라간다.
main 함수의 프레임에는 x 변수와 y 변수가 있다. 호출은 그 위에 자기 칸을 가진 swapValues 함수의 프레임을 만든다.
증명
호출 전에
main함수의 프레임에는 칸이 두 개 있다. 1이 든x변수와 2가 든y변수다.호출은
swapValues함수의 프레임을 만든다. 그 매개변수a변수와b변수는 새 칸이며, 거기에 인자의 값 1과 2가 복사된다. 이 순간부터a변수와x변수 사이에는 같은 값에서 출발했다는 것 말고는 아무 연결도 남지 않는다.지역 변수인
saved변수는 같은 프레임 안에 만들어지고a변수의 복사본을 보관한다.두 대입이
a변수와b변수를 교환한다. 바뀌는 칸은 모두 위쪽 프레임에 있으며,main함수의 칸은 건드려지지 않는다.반환할 때
swapValues함수의 프레임은 그 안에서 이루어진 교환과 함께 통째로 사라진다.x변수와y변수는 여전히 1과 2다.
복사본이 지키는 것
값 전달은 결함이 아니라, 함수를 다루기 쉽게 만드는 규칙이다. 첫째, 인자는 변수뿐 아니라 어떤 식이든 될 수 있다. swapValues(x + 1, 7) 호출은 올바르다. 매개변수는 그 값이 어디에서 왔든 하나의 값으로 초기화되기 때문이다. 둘째, 호출하는 쪽은 본문을 읽지 않고도 선언만 보고 자기 변수가 바뀌지 않으리라는 것을 안다. 셋째, 함수 안에서 매개변수는 지역 변수의 하나일 뿐이어서, 바깥에 아무 영향 없이 작업용 변수로 쓸 수 있다.
#include <print>// n의 십진 자릿수.// 사전 조건: n >= 0.// 사후 조건: n의 자릿수를 반환한다(0이면 1).int digitCount(int n){ int count{1}; while (n >= 10) { n = n / 10; ++count; } return count;}int main(){ const int population{48'210}; std::println("{}의 자릿수: {}", population, digitCount(population));}48210의 자릿수: 5이 함수는 매개변수를 10으로 계속 나누어 한 자리만 남게 함으로써 매개변수를 망가뜨린다. 그러나 그것은 복사본이므로 population 변수는 멀쩡하며, const 키워드를 붙여도 된다. 여기서 강좌 전체에 통하는 읽기 규칙이 따라 나온다. 매개변수에 뒤의 절에서 공부할 & 기호가 없으면, 함수가 그것으로 무엇을 하든 호출하는 쪽의 변수를 수정할 수 없다.
콘텐츠를 불러오는 중…