by

C++ 포인터/참조 연산자는 쪼개서 이해해야 쉽다.

나는 처음 학교에서 C++ 배울때 선언(declare)에서의 *와 &는 표현(expression)에서의 *와 &와 아무 상관없다는 걸 아무도 안가르쳐줬었다..

C++ 참조와 포인터를 어려워 하는 이유는, 연산자들의 모습만 똑같이 생겼지, 상황에 따라 전혀 다르게 동작한다는 것을 명확하게 이해하지 못해서다.

기호만 같지 서로 연관이 없는 연산자 사이에 연관관계를 연상하고, 하나의 개념으로이해하려니까 이해될 수가 없다. 이거 아예 다른 연산자들로 쪼개 이해해야 쉽다.

(1) 참조 &

이퀄 연산자 왼쪽에 있는 &, 즉 변수 타입을 선언하는데 사용된 &는, 왼쪽의 변수가 오른쪽의 값을 부르는 또 다른 이름이라는 뜻이 된다.

즉, "같은 사람을 부르는 여러 별명 중 하나"로 동작한다.

int a = 0;
int& b = a; // b는 a를 부르는 또 다른 이름. b를 수정하면 a가 수정됨.
b = 5; // a도 5가 된다. 실체는 하나만 존재하고 a와 b는 같은 실체를 부르는 서로 다른 별명.

(2) 주소 &

이퀄 연산자 오른쪽에 있는 &, 또는 이미 선언된 변수나 값에 대한 연산자로 쓰는 &는, 값이 위치한 주소를 까준다.

int a = 10;
print(&a); // a의 메모리 주소값이 나옴.
int* adressOfa = &a; // 주소를 까서, 주소 저장용 타입에 주소를 저장

(3) 함수 입력에 선언된 참조 &

함수 입력에 있는 &는 입력값의 복제본이 아니라 원본을 받겠다는 뜻이다. C#의 ref나 out 키워드에 대응된다. 즉, 함수 내부에서 입력된 값에 적용한 변경사항이 함수 외부에서도 그대로 적용된다.

처음부터 타입으로서의 &로 이해하면 이해 잘안된다. 물론, 사실 이 동작은 (1)의 연장선이다. 함수에 입력된 값의 복제본이 아닌, 입력한 값을 가리키는 또다른 별명을 만들어 함수 내부에서 쓰겠다는 이야기다.

Change(int& a) {
a = 10; // 함수를 호출한 측에서도 입력값이 10으로 변경됨
}

a = 5;
Change(a);
print(a); // a가 10이 되어있음.

(4) 포인터 *
이퀄 연산자 왼쪽에 있는 *는, 즉 변수 타입을 선언하는데 사용된 *는 그냥 주소 저장하는 타입이다.

포인터는 친구의 집 주소다. 참조는 친구를 부르는 별명 중 하나다.

참조의 값을 수정하면, 실체가 수정된다. 포인터 타입의 변수는, 그 값을 수정 한다고 실체가 수정되지 않는다. 포인터 타입의 변수가 가르키는 곳에 도착해서 값을 수정해야 실체가 수정된다.

주소를 메모한 종이에 뭘 한다고, 실제로 무슨 일이 일어나는게 아니다. 주소를 메모한 종이를 통해 실제 그 주소로 이동해야 실체를 만나고, 무언가를 수정할 수 있다.

int a = 0;
int* addressOfa = &a; // a의 주소를 까서 addressOfa에 저장함.
addressOfa = 10; // 이런다고 해도 a의 값은 변함이 없음.

int* addressOfa = &a; // a의 주소를 까서 addressOfa에 저장함.
(*addressOfa) = 10; // 주소로 이동하기 연산자를 통해 addressOfa가 가리키는 곳에 이동해서 10을 넣으면, 이제 a의 값이 10이됨

(5) 역참조 *

선언용* 가 주소를 받는 변수 타입(포인터 타입)을 선언하는데 사용되는 반면에, 이미 선언된 변수에 사용되는* 연산자는, 해당 주소로 이동하는 연산자다. 쇼트 컷 버전으로 ->를 쓰기도함.

int a = 10;
int* addressOfa = &a; // a의 주소를 까서 addressOfa에 저장함.
print(addressOfa); // a의 주소가나옴
print(*addressOfa); // a의 주소로 이동하여 찾은 값 10이 나옴