[02-4] 참조자(Reference)와 함수 (2024)

이번에는 참조자의 활용과 관련해 다루려 한다.

Call-by-value & Call-by-reference

우리가 C언어를 공부하면서 배운 함수의 두 가지 호출방식은 다음과 같다.

  • Call-by-value
    값을 인자로 전달하는 함수의 호출방식
  • Call-by-reference
    주소 값을 인자로 전달하는 함수의 호출방식

Call-by-value 기반의 함수는 다음과 같다.

int Adder(int num1, int num2) {return num1 + num2;}

위 함수는 두 개의 정수를 인자로 요구하고 있다.
Call-by-value 형태로 정의된 함수의 내부에서는,
함수외부에 선언된 변수에 접근이 불가능하다.
따라서 두 변수에 저장된 값을 서로 바꿔서 저장할 목적으로 다음과 같이 함수를 정의하면 원하는 결과를 얻을 수 없다.

void SwapByValue(int num1, int num2) {int temp = num1; num1 = num2; num2 = temp;}

위의 함수를 대상으로 다음의 main 함수를 실행하면,

int main(void) {int val1 = 10; int val2 = 20; SwapByValue(val1, val2); //val1과 val2에 저장된 값이 바뀌기를 기대함 cout<<"val1: "<<val1<<endl; // 10 출력 cout<<"val2: "<<val2<<endl; // 20 출력 return 0;}

출력결과는 다음과 같다.

val1 = 10val2 = 20

이는 val1과 val2에 저장된 값이 서로 바뀌지 않았음을 의미한다. 그래서 필요한 것이 Call-by-reference 기반의 함수이다.

void SwapByRef(int * ptr1, int * ptr2) {int temp = *ptr1; *ptr1 = *ptr2; *ptr2 = temp;}

위의 함수에서는 두 개의 주소 값을 받아서 주소 값이 참조하는 영역에 저장된 값을 직접 변경하고 있다.

int main(void) {int val1 = 10; int val2 = 20; SwapByRef(&val1, &val2); //val1과 val2에 저장된 값이 바뀌기를 기대함 cout<<"val1: "<<val1<<endl; // 20 출력 cout<<"val2: "<<val2<<endl; // 10 출력 return 0;}

다음의 출력결과를 확인할 수 있다.

val1 = 20val2 = 10

Call-by-address? Call-by-reference!

본래 C언어에서 말하는 Call-by-reference는 다음의 의미를 지닌다.

"주소 값을 전달받아서, 함수 외부에 선언된 변수에 접근하는 형태의 함수호출"

C++에서는 함수 외부에 선언된 변수의 접근 방법으로 두 가지가 존재한다. 하나는 '주소 값'을 이용하는 방식, 다른 하나는 ' 참조자'를 이용하는 방식이다.

  • 주소값을 이용한 Call-by-reference
  • 참조자를 이용한 Call-by-reference

이렇듯 C++에서는 두 가지 방식으로 Call-by-reference의 함수정의가 가능하다.

참조자를 이용한 Call-by-reference

C++에서는 참조자를 기반으로도 Call-by-reference의 함수호출을 진행할 수 있다.

void SwapByRef2(int &ref1, int &ref2) {int temp = ref1; ref1 = ref2; ref2 = temp;}

엥? 참조자는 선언과 동시에 변수로 초기화되어야 한다면서? 맞다! 하지만 매개변수는 함수가 호출되어야 초기화가 진행되는 변수들이다. 즉, 위의 매개변수 선언은 초기화가 이뤄지지 않은 것이 아니라, 함수호출 시 전달되는 인자로 초기화를 하겠다는 의미의 선언이다.
그럼 위의 함수를 대상으로 다음과 같이 호출하면?

int main(void) {int val1 = 10; int val2 = 20; SwapByRef2(val1, val2); cout<<"val1: " <<val1<<endl; cout<<"val2: " <<val2<<endl; return 0;

[02-4] 참조자(Reference)와 함수 (1)

ref과 ref2가 val1, val2를 각각 참조하게 된다.

그리고, SwapByRef2(val1, val2) 호출 함수를 빠져나가게 되면 ref1, ref2라는 이름은 다 사라지게 된다.

C언어에서는 함수의 호출 형태만 보고 그 함수에서 일어나는 일을 알 수가 있었다.

  • 값을 전달한다 -> 값이 복사됨
  • 주소값을 전달한다 -> 안에 변수에 저장된 값을 바꿀 수 있다.

이렇게 예측이 가능했으나,
C++에서는 함수의 호출문만 봐서는 알 수가 없다.
값이 전달되는지, 참조자가 전달되는지 모른다.
이는 함수의 매개변수가 무엇으로 선언되었느냐에 따라 달라진다.

아래 예제를 통해 함수의 호출결과를 실제로 확인해보자.

RefSwap.cpp

#include <iostream>using namespace std;void SwapByRef2(int &ref1, int &ref2) { int temp =ref1; ref1=ref2; ref2=temp;}int main(void) { int val1 = 10; int val2 = 20; SwapByRef2(val1, val2); cout<<"val1: "<<val1<<endl; cout<<"val2: "<<val2<<endl; return 0;}
val1: 20val2: 10

참조자를 이용한 Call-by-reference의 황당함과 const 참조자

참조자 기반의 함수정의에 좋은 점만 있는 것은 아니다.

int num = 24;HappyFunc(num);cout<<num<<endl;

C언어의 관점에서는 24가 출력된다. 그러나 C++에서는 얼마가 출력될 지 알 수 없다. 함수가 다음과 같이 정의되어 있다면 24가 출력되겠지만,

void HappyFunc(int prm) { . . . . }

다음과 같이 정의되어 있다면, 참조자를 이용해 num에 저장된 값을 변경할 수도 있는 일이니 말이다.

void HappyFunc(int &ref) { . . . . }

이는 분명히 참조자의 단점이 된다.
예를 들어, 코드를 분석하는 과정에 있다면, 함수의 호출문장만 보고도 함수의 특성을 어느 정도 판단할 수 있어야 한다. 그러나 참조자를 사용하는 경우, 함수의 원형을 확인해야 하고, 확인결과 참조자가 매개변수의 선언에 와있다면, 함수의 몸체까지 문장 단위로 확인을 해서 참조자를 통한 값의 변경이 일어나는지를 확인해야한다.

이러한 문제점의 해결방안이 무엇일까? 완변한 해결방안이 되질 못하겠지만, 어느정도 극복할 수 있는 방안이 있다. 이는 const 키워드의 사용이다.

void HappyFunc(const int &ref) { . . . . }

참조자 ref에 const 선언이 추가되었다. 이는 다음의 의미를 지닌다.

"함수 HappyFunc 내에서 참조자 ref를 이용한 값의 변경은 하지 않겠다!"

여기 const 선언으로 인해, 참조자 ref에 값을 저장하는 경우 컴파일 에러가 발생한다. 따라서 함수 내에서 값의 변경이 이뤄지지 않음을 확신할 수 있다.
앞으로 다음의 원칙을 지켜주자.

"함수 내에서, 참조자를 통한 값의 변경을 진행하지 않는 경우, 참조자를 const로 선언해서, 함수의 원형만 봐도 변경이 이뤄지지 않음을 알 수 있게 한다."

반환형이 참조형(Reference Type)인 경우

예제를 통해 이해해보자!

RefReturnOne.cpp

#include <iostream>using namespace std;int& RefRetFunOne(int &ref) { ref++; return ref;}int main(void) { int num1 = 1; int &num2 = RefRetFunOne(num1); num1++; num2++; cout <<"num1: "<<num1<<endl; cout <<"num2: "<<num2<<endl; return 0;}
num1: 4num2: 4

위의 코드를 살펴보면,

int &num2 = RefRetFunOne(num1);

RefRetFunOne 함수가 참조자를 반환했고 이를 다시 참조자에 저장하고 있다.

그림을 통해 이해해보자.
[02-4] 참조자(Reference)와 함수 (2)
ref에 저장된 값 4가 아닌, ref라는 참조의 데이터가 반환된다. 따라서 num2는 num1에 대한 참조의 데이터를 전달받는 것이다.

그럼 이번에는 예제 RefRetFunOne.cpp의

int &num2 = RefRetFunOne(num1);

를 다음과 같이 변경해보자.

int num2 = RefRetFunOne(num1);

참조자를 변수로 대신하였다. 예제를 통해 확인해보자.

RefReturnTwo.cpp

#include <iostream>using namespace std;int& RefRetFunOne(int &ref) { ref++; return ref;}int main(void) { int num1 = 1; int num2 = RefRetFunOne(num1); num1+=1; num2+=100; cout <<"num1: "<<num1<<endl; cout <<"num2: "<<num2<<endl; return 0;}
num1: 3num2: 102

다음과 같이 변경함으로써,

int num2 = RefRetFunOne(num1);

num1과 num2는 완전히 별개의 변수가 되는 것이다.
[02-4] 참조자(Reference)와 함수 (3)
왜? 참조형(&num2)가 아니라 변수(num2)이어서 참조데이터가 아닌 '값'을 전달 받기 때문이다.

이제 마지막으로 참조자를 반환하되, 반환형은 기본자료형인 경우를 알아보자.

RefReturnThree.cpp

#include <iostream>using namespace std;int RefRetFunTwo(int &ref) { //반환형이 기본자료형 int이다! ref++; return ref;}int main(void) { int num1 = 1; int num2 = RefRetFunTwo(num1); num1+=1; num2+=100; cout <<"num1: "<<num1<<endl; cout <<"num2: "<<num2<<endl; return 0;}
num1: 3num2: 102

이 행을 살펴보면,

return ref;

참조자를 반환하지만, 반환형이 기본자료형 int이기 때문에 참조자가 참조하는 변수의 값이 반환된다. 다시 한번 말하지만, 변수에 저장된 값이 반환된다.

RefReturnTwo.cpp와 실행결과에 차이가 없으나 반환형이 기본자료형으로 선언된 RefReturnTwo 함수의 반환 값은 반드시 변수에 저장해야한다. 반환 값은 상수나 다름없기 때문이다.

  • int num2 = RefRetFunTwo(num1); (o)
  • int &num2 = RefRetFunTwo(num1); (x)

잘못된 참조의 반환

위에서 말한 함수의 반환형에 대해 잘 이해했다면, 다음 함수에 어떠한 문제가 있는지 예상할 수 있다.

int & RetuRefFunc(int n) {int num = 20; num +=n; return num;}

위 함수에서는 지역변수 num에 저장된 값을 반환하지 않고, num을 참조의 형태로 반환하고 있다. 따라서 다음의 형태로 함수를 호출하고 나면,

int &ref = RetuRefFunc(10);

지역변수 num에 ref라는 또 하나의 이름이 붇게 된다. 하지만 이게 끝이 아니다. 함수가 반환이 되면, 정작 지역변수 num은 소멸 된다. 따라서 위 처럼 지역변수를 참조형으로 반환하는 일은 없어야 한다.

const 참조자의 또 다른 특징

다음 코드를 보고 논리적 문제점을 찾아보자.

const int num = 20;int &ref = num;ref+=10;cout<<num<<endl;

여기서 에러의 원인은 다음 코드이다.

int &ref = num;

이를 허용한다는 것은 ref를 통한 값의 변경을 허용한다는 뜻이 되고, 이는 num을 const로 선언하는 이유를 잃게 만든다.

따라서 다음과 같이 참조자 선언을 해야 한다.

const int num = 20;const int &ref = num;

이렇게 선언이 되면 ref를 통한 값의 변경이 불가능하기 때문에 상수화에 대한 논리적인 문제점은 발생하지 않는다. 그리고 const 참조자는 다음과 같이 상수도 참조가 가능하다.

const int &ref = 50;

엥?? 지금까지 참조자는 변수만 참조가 가능하다고 했는데.. 이어서 알아보자..

어떻게 참조자가 상수를 참조하냐고요!

int num = 20+30;

여기서 20, 30은 '리터럴 상수(literal constant)'라 하고 다음의 특징을 지닌다.

"임시적으로 존재하는 값이다. 다음 행으로 넘어가면 존재하지 않는 상수다."

그런데, 이러한 상수를 참조한다는 것이 이치에 맞는가? 다음과 같이 말이다.

const int &ref = 30;

이는 숫자 30이 메모리 공간에 계속 남아있을 때에나 성립이 가능하다. 그래서 C++에서는 위의 문장이 성립할 수 있도록, const 참조자를 이용해 상수를 참조할 때 '임시변수'라는 것을 만든다. 그리고 이 장소에 상수 30을 저장하고선 참조자가 이를 참조하게끔 한다.
[02-4] 참조자(Reference)와 함수 (4)
사진과 같이 정의된 함수에 인자의 전달을 목적으로 변수를 선언한다는 것은 매우 번거로운 일이다. 그러나 임시변수의 생성을 통한 const 상수참조를 허용함으로써, 위의 함수는 다음과 같이 매우 간단히 호출이 가능해졌다.

cout<<Adder(3, 4)<<endl;
[02-4] 참조자(Reference)와 함수 (2024)

References

Top Articles
Newsreader Jo Hall calls footy star Jack Ginnivan 'a disgrace'
Taryn Faliszewski on LinkedIn: #creatoreconomy #creators
This website is unavailable in your location. – WSB-TV Channel 2 - Atlanta
Xre-02022
Nybe Business Id
Pixel Speedrun Unblocked 76
Ghosted Imdb Parents Guide
Mopaga Game
Toyota Campers For Sale Craigslist
Big Spring Skip The Games
Gore Videos Uncensored
Achivr Visb Verizon
New Day Usa Blonde Spokeswoman 2022
Ssefth1203
Keniakoop
Craigslist Pets Longview Tx
House Party 2023 Showtimes Near Marcus North Shore Cinema
Maplestar Kemono
Lake Nockamixon Fishing Report
Char-Em Isd
Noaa Ilx
Barber Gym Quantico Hours
PCM.daily - Discussion Forum: Classique du Grand Duché
Obituaries Milwaukee Journal Sentinel
European Wax Center Toms River Reviews
Bleacher Report Philadelphia Flyers
Cfv Mychart
Trinket Of Advanced Weaponry
Tamil Movies - Ogomovies
Downloahub
Lininii
How often should you visit your Barber?
Math Minor Umn
Citibank Branch Locations In Orlando Florida
Moonrise Time Tonight Near Me
Family Fare Ad Allendale Mi
Pillowtalk Podcast Interview Turns Into 3Some
Instafeet Login
Let's co-sleep on it: How I became the mom I swore I'd never be
Improving curriculum alignment and achieving learning goals by making the curriculum visible | Semantic Scholar
Actor and beloved baritone James Earl Jones dies at 93
Toomics - Die unendliche Welt der Comics online
About Us
Senior Houses For Sale Near Me
How To Get To Ultra Space Pixelmon
Ts In Baton Rouge
How the Color Pink Influences Mood and Emotions: A Psychological Perspective
Advance Auto.parts Near Me
Rovert Wrestling
Craigslist Anc Ak
Zadruga Elita 7 Live - Zadruga Elita 8 Uživo HD Emitirani Sat Putem Interneta
Subdomain Finer
Latest Posts
Article information

Author: Madonna Wisozk

Last Updated:

Views: 6001

Rating: 4.8 / 5 (68 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Madonna Wisozk

Birthday: 2001-02-23

Address: 656 Gerhold Summit, Sidneyberg, FL 78179-2512

Phone: +6742282696652

Job: Customer Banking Liaison

Hobby: Flower arranging, Yo-yoing, Tai chi, Rowing, Macrame, Urban exploration, Knife making

Introduction: My name is Madonna Wisozk, I am a attractive, healthy, thoughtful, faithful, open, vivacious, zany person who loves writing and wants to share my knowledge and understanding with you.