본문 바로가기

별걸다하는 IT/프로그래밍언어

[C/C++ 함수 호출방식] 값에 의한 호출, 참조에 의한 호출, 포인터에 의한 호출 (call by value, call by reference, call by pointer)

반응형

[C/C++ 프로그래밍 목차 링크 모음]

안녕하세요 양햄찌 블로거 입니다. 

오늘은 C언어 함수 파트에서 주로 설명되는 '함수 호출 방식'에 대해 설명해보려고 해요.

C언어를 처음 접하시는 분들은 이 부분에서 많이 헷갈려 하시더라고요.

 

▼함수가 무엇인가 기초 포스팅은 아래 링크를 참조해주세요.

https://jhnyang.tistory.com/85

 

함수(function)란? 함수의 종류, 함수 역할 및 기능, 메인(Main)함수

[C언어, C++언어 목차 포스팅 링크 모음] [Java언어 목차 포스팅 링크 모음] 오늘은 기초 중의 기초 함수에 대해서 다뤄볼까해요. 함수(function)란? 수학에서 많이 봤기 때문에 익숙한 그림이죠? x를 ��

jhnyang.tistory.com

▼포인터란 무엇인가!

일단~~~! 포인터에 대한 개념을 알아야 이후에 진행할 콜바이포인터를 이해할 수 있습니다.

포인터가 무엇인지 모르시는 분은 아래 포스팅에서 기본적인 개념을 잡아옵시다.

https://jhnyang.tistory.com/100

 

[C,C++]C언어의 꽃, 포인터 총정리(*, &)

[C/C++ 완전정복 링크 ] C언어의 핵심! 꽃! 포인터!! 포인터는 C언어가 고급언어인데도 Low 레벨 언어의 특성을 지닌다고 이야기하게 만든 장본인입니다. 포인터가 왜 중요하냐!! 바로 메모리를 직접

jhnyang.tistory.com

자 시작할까요?!

함수 호출 방식이란?

어떤 프로그램에서 하나의 기능을 구현 할 때 우리는 사용자 정의 함수를 만든 후, 필요할 때마다 해당 함수를 호출해서 재활용하는 방식으로 사용할거예요!

 

함수를 정의할 때 필요에 따라서 다양하게 구현할 수 있는데, 

이 방법에 따라 함수를 호출하는 방식에 차이가 생깁니다. 이를 함수 호출 방식이라고 불러요. 무슨 말인지 몰라도 나중에 예시를 보면 다 이해가 가게 될 거예요. ㅎㅎ

 

함수 호출 방식은 크게 세 가지로 나눌 수 있습니다.

1. 값에 의한 호출  Call by value 

2. 포인터에 의한 호출 Call by pointer 

3. 참조에 의한 호출 Call by reference

 

순서에 따라 각각 호출방법이 뭔지 살펴볼까요?

값에 의한 호출 Call by value

call 호출하다

by ~으로 

value 값.

 

즉 함수를 호출할 때 값으로 호출하는걸 말해요.

아주 아주 간단한 sum 함수를 만들어 설명을 진행해볼게요.

 

[기본적인 값 호출 방식]

#include <stdio.h>
int sum(int a, int b);
int main()
{
	int total = sum(3, 5);
	return 0;
}
int sum(int a, int b)
{
	return a + b;
}

main 함수에서 sum을 어떻게 호출하고 있죠? 3과 5라는 값을 넣어서 호출하고 있어요. 

sum 함수는 3과 5를 a와 b에 받아서 이를 더한 값을 리턴해주고 있죠. 즉 값을 받아서 그 값으로 지지고 복고! 

이렇게 함수를 호출할 때 값을 전달하는 방법을 값에 의한 호출, call by value라고 합니다. 

sum ( 3, 5 ) 는 결국, 첫 번째 매개변수 int a 에는 3이라는 값을 대입하고, 두 번째 매개변수 int b에는 5라는 값을 대입하는거겠죠?

메모리적으로 이렇게 생각해볼 수 있겠습니다. 그런데 값에 의한 호출만 사용하기에는 문제가 있습니다.

이를 확인하기 위해 이번에는 a와 b, 두 변수에 들어간 값을 바꿔주는 swap 함수를 작성해볼게요.

 

[두 수의 값을 교환해주는 swap 함수 - call by value]

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

우리의 목적은 swap(a,b)하면 a와 b의 값이 서로 치한되는 거예요!

매개변수 타입을 보면 정수를 받고 있죠? 이렇게 값을 인자로 받도록 받도록 짜봤어요. 일단 함수 내부만 분석해보면,

만약 num1에 3이 들어가고, num2에 5가 들어갔다고 가정하면,

temp1이라는 임시 변수를 이용해서 이렇게 최종적으로는 num1에는 num2에 들어가있던 5가 들어가게, num2에는 num1이 들어있던 3이 들어가게 만들어 줄 수 있겠죠?

#include <stdio.h>
void swap(int , int );
int main()
{
	int a = 3, b = 5;
	printf("before - a:%d, b:%d\n", a, b);
	swap(a, b);
	printf("after - a:%d, b:%d\n", a, b);
	return 0;
}
void swap(int num1, int num2)
{
	int temp = num1;
	num1 = num2;
	num2 = temp;
}

근데 막상 이렇게 작성 후 실행해보면, 

실패!

a와 b 값이 서로 치환되지 못한 것을 확인할 수 있습니다. 왜 그럴까요?? 바로 값만 넘겨주었기 때문에, 즉 값에 의한 호출 방식을 사용하였기 때문에 문제가 되었습니다. 

swap(a,b)가 호출되면 첫 번째 매개변수 int num1의 인자로는 a값이 들어가겠죠? 따라서 int num1 = a; 와 동일하게 됩니다. 그럼 위 그림처럼 num1 공간에 3이 대입되는 거예요. num2도 마찬가지입니다.

그런데 이렇게 되면 num1과 num2의 값을 잘 바뀌었는데,, 우리가 목적으로 삼고 있던 a와 b에는 전혀 영향이 가지 않은 것을 확인할 수 있습니다. num1과 num2는 함수 매개변수 저장공간이기 때문에 함수가 종료되면 사라져 버려요! 따라서 의미 없는 코드가 되버립니다.

 

나는 함수 범위의 변수들의 값(num1, num2)을 교환하고 싶은게 아니라 함수 외부 값(a, b)에 영향을 끼치고 싶은 거잖아요! 위 문제를 어떻게 해결할 수 있을까요? 

'값만 토스하는 방식이 아니라, 값 대신 a와 b의 위치(메모리주소번지)를 전달해 함수내부에서 a,b를 접근할 수 있도록 하자.' 한거죠. 이게 포인터에 의한 호출입니다. 

포인터에 의한 호출 Call by pointer

call 호출하다.

by ~로,

pointer 포인터

 

함수를 호출할 때 값을 넘기는게 아니라, 주소를 넘기는 방식을 말합니다.

#include <stdio.h>
void swap(int* , int* );   //매개변수 타입이 포인터변수로 변경되었어요!
int main()
{
	int a = 3, b = 5;
	printf("before - a:%d, b:%d\n", a, b);
	swap(&a, &b);		//포인터 연산자를 통해 a,b의 주소를 넘겨줍시다.
	printf("after - a:%d, b:%d\n", a, b);
	return 0;
}
void swap(int* num1, int* num2)
{
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

일단 실행을 해보면 이번엔 제대로 swap된 것을 볼 수 있어요.

결과

main에 호출방식을 보면 이번엔 a, b 대신에 &a (a의 주소값), &b (b이 주소값)을 인자로 넘겨줬습니다.

call by value시에는 num1 공간에 a의 값이 들어갔다면 call by pointer에는 a의 주소가 num1에 대입되는 거예요.

함수가 호출될떄 어떻게 저장되는지 살펴보았으니, 어떻게 치환이 가능한건지 살펴봅시다.

swap 과정을 이해되기 쉽게 상세히 그려봤어요. 

이렇듯 num1과 num2에 저장된 주소값에 직접 가서 값을 교환해버리는거예요!

주소를 인자로 전달해 이렇게 함수를 호출하는 방법을 포인터에 의한 함수 호출이라고 합니다. 

참조에 의한 호출 Call by reference

참조에 의한 호출은 C언어에는 없는 개념입니다. 참조자라는게 C++에서 새롭게 추가되었기 때문인데요.

참조자란 저장공간에 nickname 별명을 하나 더 붙이는 역할을 하죠 ㅎㅎ

포인터를 모르면 포인터에 의한 호출 방식을 이해하는데 어려움이 있을 수 있듯이,

참조 또한 참조자를 모르면 참조에 의한 호출 방식을 이해하는데 어려움이 있을 수 있습니다. 

reference관련 포스팅은 여기를 참고해주세요.

#include <stdio.h>
void swap(int&, int&);   
int main()
{
	int a = 3, b = 5;
	printf("before - a:%d, b:%d\n", a, b);
	swap(a, b);		
	printf("after - a:%d, b:%d\n", a, b);
	return 0;
}
void swap(int& num1, int& num2)
{
	int temp = num1;
	num1 = num2;
	num2 = temp;
}

swap매개변수 타입이 포인터변수가 아닌 참조형식으로 변경되었어요. 

실행시켜보면 역시 잘 치환된 것을 확인하실 수 있습니다.

위와 동일하게, 매개변수 타입에 인자가 대입되는 과정을 위처럼 생각할 수 있어요.

짠! 이렇게 참조자의 특성으로 저장공간을 공유하기 때문에 위와 같이 서로 값 변환이 가능한겁니다.

매개변수와 인자가 저장공간을 공유할 수 있도록 참조 방식으로 호출하는 걸 '참조에 의한 호출'이라 합니다.

플러스 알파 스스로 생각해보기 

자, 이제 우리는 '값에 의한 호출', '주소에 의한 호출(포인터)', '참조자에 의한 호출' 세 가지 모두를 살펴보았어요. 

이를 복습할 겸 스스로 해보기!? 파트를 준비했어요.

#include <stdio.h>
void swap(int*, int*);
int main()
{
	int a = 3, b = 5;
	printf("before - a:%d, b:%d\n", a, b);
	swap(&a, &b);
	printf("after - a:%d, b:%d\n", a, b);
	return 0;
}
void swap(int* num1, int* num2)
{
	int* temp = num1;
	num1 = num2;
	num2 = temp;
}

위 코드는 포인터에 의한 호출을 사용하였지만, 실제 값이 치환되지 않는 잘못된 코드인데요.

왜 치환이 되지 않는지, 메모리적으로 그림을 그려보면서 생각해봅시다. 

실제 이렇게 유추하는게 나중에 큰 도움이 돼요 :) 

 

오늘 포스팅은 여기서 이만 줄이도록 하겠습니다. 

힘들게 열심히 그렸는데 도움이 되셨다면 공감/댓글/광고보답 중 하나로 표현해주시면 어떨까요?! 

그럼 다음 포스팅에서 또 봐요~

반응형
  • SOO 2021.04.21 20:26

    공유해주셔서 감사드립니다! 덕분에 잘보고갑니다 ㅎㅎ (혹시 광고보답은 어디를 누르면 될까요? 찾다가 발견하지 못해서 남깁니다 ㅠㅠ )

    • IT 양햄찌(jhnyang) 2021.04.22 09:27 신고

      본문에 광고가 보이시면 클릭해주시고, 랜덤으로 광고가 안보이실 수 잇는데 그럴경우 생략하시면 됩니다! 으잉 이렇게나 신경써주셔서 감사드려요. 더 좋은 정보로 신경써서 찾아뵙도록 할게요 :) 방문과 댓글 감사드립니다. 좋은하루되세요~!