본문 바로가기

C++ 일기

16. Call by Reference, Call by Value, Call by Reference

#include<iostream>

void Address(int* n);

void Reference(int& n);


int main()
{
   int num1 = 30;
   Adress(&num1);
   std::cout << "num1 값 : " << num1 << std::endl;
   std::cout << "num1 주소값 : " << &num1 << std::endl;
   
   int num2 = 30;
   Reference(num2);
   std::cout << "num2 값 : " << num2 << std::endl;
   std::cout << "num2 주소값 : " << &num2 << std::endl;
   
   /* 출력값
   n의 값 : 10
   n의 주소값 : (임의의 주소값 a)
   num1 값 : 10
   num1 주소값 : (임의의 주소값 b)
   
   n의 값 : 10
   n의 주소값 : (임의의 주소값 c)
   num2의 값 : 10
   num2의 주소값 : (임의의 주소값 c)
   */
   
}

void Address(int* n)
{
	*n = 10;
	std::cout << "n의 값 : " << *n << std::endl;
	std::cout << "*n의 주소값 : " << &n << std::endl;
}

void Reference(int& n)
{
	n = 10;
	std::cout << "n의 값 : " << n << std::endl;
	std::cout << "*n의 주소값 : " << &n << std::endl;
}

굉장히 긴 코드가 나타났다.

위는 기존의 C에서는 없지만, C++에서 등장하게 된 개념인 Call by Reference에 대한 내용을 위한 예시이다.

앞서 Call by Address를 설명할 때에 설명을 안 한 것에 대해 설명하자면,

포인터는 앞서 말했듯 주소를 담는 변수라고 얘기했다.

포인터도 역시 변수이기 때문에 메모리 공간을 할당받고, 주소가 있다.

포인터도 사람이고, 포인터도 집은 있다는 것이다.

물론 포인터의 집은 가리키는 a의 집 주소와는 다르다.

 

이 주소를 담는 변수는 역참조를 통해 가리키고 있는 주소에 해당하는 값을 가질 수 있다고 했다.

int a의 주소를 저장한 포인터 int* p를 예로 들어보자.

*p의 값과 a의 값은 같아서 둘이 완전히 같은 존재인 것 같지만 그렇지 않다.

각각 다른 주소에 살고있는 다른 사람이지만, 서로의 값이 바뀌면 같이 바뀌는 분신인 것이다.

앞서 분신의 예는 조금 부적절한 것 같다고 얘기했지만, 이 개념을 다룰 때 만큼은 굉장히 유용한 예인 것 같다.

서로 각자 다른 곳에 저장되어 있지만, 같은 값을 동일하게 갖고있는 분신.

그러나 분신에 해당되는 것은 *p 즉, 역참조를 통해 a의 값을 가진 녀석 뿐,

그냥 포인터 p는 분신까지는 아니고 그저 a의 주소를 가리키고 있는 녀석일 뿐이다.

단, *를 통해 a를 역참조 하게된다면,

재밌는 예를 들자면, *가면을 쓰면 p는 a와 같은 사람이 된다는 것이다.

평소에는 그저 평범한 경비아저씨인 p는 *가면을 쓰면 히어로a의 분신이 되는 재밌는 일이다.

 

 

그렇기 때문에, Call by Address를 통해 값을 갖게된 n의 주소와 본체인 num1의 주소가 다른 것이다.

값을 공유하는, 같은 모습이 되는 분신일 뿐, 서로 다른 사람이고, 각자 다른 곳에서 살고있기 때문이다.

 

 

그런데, 여기서 Reference라는 함수를 통해 나온 n은 값도 같고, 주소도 같다.

포인터도 아니고, &를 달고있는 이상한 변수가 하나 있다. &는 변수 앞에서 주소값을 전달할 때 사용하는 연산자인데,

이렇게 연산자를 변수를 선언할 때도 사용하다니 이상한 일이다.

 

C++에서 새롭게 만들어지게 된 Call by Reference는, 분신을 만들어내는 것과는 조금 다르다.

여기서 매개변수가 된 int& n은 num2의 분신이 아니라, 그냥 num2를 다르게 부르는 별명이다.

앞서 Call by Address에서는 동일한 사람이 아니라 다른 사람인데, 가면을 쓰면 똑같은 사람이 된다고 했었는데,

이번엔 다른 사람이 아니라 정말 같은 사람이다.

다른 사람을 하나 더 만드는게 아니라, 함수 내에서 num2를 부르는 이름 외에 별명을 하나 더 붙여서

그 별명으로 직접 main함수에 있는 num2를 부르는 것이다.

이를 Call by Reference(참조에 의한 호출)라고 부른다.

Reference인 &n은 num2와 데이터를 공유한다. 

num2와 같은 데이터를 공유하고, 같은 주소를 공유하는 변수를 다른 함수에서 사용함으로써,

포인터를 선언하는데 필요한 메모리를 할당할 필요 없이, 다른 함수에서 num2의 값을 초기화하고, 변경할 수 있다.

 

 

또 한가지 재밌는 예가 생각이났는데, 마치 평행세계, 멀티버스인 것 같다.

Reference세계와 main세계는 다른 시간대, 혹은 다른 공간에 있는 같은세계이다.

따라서 둘은 서로가 누구인지, 이름이 무엇인지도 알 수 없고, 이는 그 세계사람들도 마찬가지이다.

스파이더 맨에서 나오는 서로 다른 영화의 스파이더맨들은 서로 각자 다른 특징을 가지고 있지만,

여기서는 전부 동일하다. 하는 행동 마저 똑같다. 사는 곳 마저 똑같다.

그래서 Reference 세계에서 &n을 부르면 main세계에서도 누군가 num2를 부른다.

그럼 &n과 num2는 둘 다 동시에 돌아보게 된다.

하지만 Reference세계에서 num2를 부르면 알아듣지 못한다. 이 세계에선 num2는 없는 사람이기 때문이다.

마찬가지로 main세계에서도 &n을 부르면 알아듣지 못한다.

이를 반영이라도 하는 듯, main함수에서 &n이라는 변수를 사용하려고 하면 붉은줄이 가고,

Reference함수에서 num2를 사용하려고 하면 붉은줄이 간다.

 

 

 

워낙에 어려운 개념이고, 이해하기도, 설명하기도 어려워 말이 두서없지는 않았나 싶다.

나 스스로도 포인터의 개념에 대해, Call by Value, Call by Address에 대한 개념에 대해 확실하지 않았는데,

누군가에게 가르쳐준다는 생각으로 쉽게 말을 만들다 보니

오히려 내가 그 말에 더 쉽게 이해가 된 것 같고, 애매하게 알고있었던 개념을 더 확실하게 이해할 수 있게된 것 같다.

티스토리에 포인터에 대한 개념을 포스팅하려고 할 때, 처음에는 막막했으나,

정리하면서 떠오르는 여러 재밌는 예시나 쉬운 설명 덕에 즐거운 포스팅이 되었던 것 같다.

 

'C++ 일기' 카테고리의 다른 글

18. 객체지향(OOP)과 절차지향(PP)프로그래밍  (2) 2024.02.28
17. 네임스페이스 (namespace)  (0) 2024.02.28
15. 포인터  (0) 2024.02.27
14. 사용자 정의 자료형 struct (구조체)  (2) 2024.02.27
13. 난수 발생  (0) 2024.02.27