brunch

매거진 Sinclair

You can make anything
by writing

C.S.Lewis

by Sinclair Feb 07. 2016

함수와 표준 I/O

내가 그의 이름을 불러 주었을 때 IV




Call by value와 Call by reference  


일반적으로 함수를 호출하는 방법은 두 가지가 있습니다. 값으로 호출하는 call by value와 변수의 reference로 호출하는 call by reference입니다. 물론 다양한 방법들이 더 있지만 이 두가지가 주로 사용되는 방법들입니다.
                               

Call by value를 사용하는 언어: C

Call by reference를 사용하는 언어: FORTRAN

두 가지 모두 사용하는 언어: Java, C++


 

Call by reference는 함수를 호출하여 인자를 변경하면 그 변경된 인자가 호출한 쪽에도 그대로 유지됩니다. 하지만 Call by value는 변경된 인자는 함수 내에서만 의미가 있을 뿐 호출한 함수에는 아무런 영향을 주지 않습니다.

그렇다면 Call by reference와 Call by value 중 어떤 것이 좋을까요?
프로그램을 못 짜던 시절에는 Call by reference가 더 좋은 줄 알았습니다. FORTRAN의 read프로시저는 C의 scanf()처럼 주소 값을 사용하지 않아도 됩니다. 주소 값이나 포인터 없이도 인자를 변경할 수 있기 때문입니다.


간단하게 프로그램으로 비교해보면 알 수 있습니다.  


만약에 C언어가 Call by reference를 지원한다면 다음과 같을 것 입니다.  


#include <stdio.h>  

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/   

void swap1( int , int ) ;  

main() {  

        int aData = 10 , bData = 20 ;  

        printf("%d %d\n" , aData , bData) ;

        swap1(aData , bData) ;

        printf("%d %d\n" , aData , bData) ;  

        return 0 ;  

} // end main()   


void swap1( int first , int second ) {

        int temp ;  

        temp = first ;

        first = second ;

        second = temp ;  

} // end swap1()    




C++에서라면 이렇게 사용할 수 있습니다.  


#include <stdio.h>  

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/   

void swap1( int& , int& ) ;

// C++에서는 Call by reference를 이렇게 사용합니다.  

main() {  

        int aData = 10 , bData = 20 ;  

        printf("%d %d\n" , aData , bData) ;

        swap1(aData , bData) ;

        printf("%d %d\n" , aData , bData) ;  

        return 0 ;  

} // end main()   


void swap1( int& first , int& second ) { // just on C++

        int temp ;  

        temp = first ;

        first = second ;

        second = temp ;  

} // end swap1()    




하지만 실제 C에서 실행해 보면 데이터는 교환되지 않습니다.

C언어는 Call by value를 지원하기 때문입니다.

제대로 교환이 되려면 이렇게 바꿔야 합니다.  


#include <stdio.h>  

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/   

void swap2( int * , int * ) ;  

main() {

        int aData = 10 , bData = 20 ;  

        printf("%d %d\n" , aData , bData) ;

        swap2(&aData , &bData) ;

        printf("%d %d\n" , aData , bData) ;  

        return 0 ;  

} // end main()   


void swap2( int * first , int * second ) {  

        int temp ;  

        temp = *first ;

        *first = *second ;

        *second = temp ;  

} // end swap2()    




왜 printf()를 부를 땐 변수를그대로 인자로 사용하지만 scanf()를 부를 땐 반드시 포인터 또는 변수의 주소 값으로 인자를 사용해야 하는지 이제 감이 오십니까? 

printf()는 인자를 그냥 그대로 화면에 출력하기만 하면 됩니다. 값이 변경 되지도 않고 변경할 필요도 없습니다. 출력을 했더니 값이 변했다? 말이 됩니까? 이게 뭐니? 이게 이게…


하지만 scanf()는 함수 내부에서 입력된 값으로 넘겨주는 인자를 변경해야 합니다. scanf()를 호출했는데 키보드로 입력한 값으로 인자 값이 변하지 않으면 우리가 scanf()를 왜 호출했을까요? 왜 불러요? 그냥? 아이구, 그렇게 심심하세요?


다시 말하자면 인자를 단순 참조하려면 변수 그대로, 넘겨주는 인자를 변경하려면 인자의 주소 값으로 사용해야 합니다. 어떤 타입이냐는 중요하지 않습니다. 다만 원칙은 이것입니다. 어떤 타입이든지 인자를 변경하려면 주소 값으로, 단순 참조라면 그대로 사용하는 것입니다. 정말 간단하지 않습니까? 물론 단순 참조를 할 때 주소 값을 사용해도 상관없지만 포인터가 정말 그렇게 사랑스런가요?



막상 이렇게 포인터가 등장하고 나니 갑자기 Call by reference가 더 좋아 보입니까? 하지만 이렇게 멋져 보이는 Call by reference에는 몇 가지 문제점이 있습니다. Call by reference는 함수를 호출하면 항상 인자가 변경될 수 있습니다. 그러므로 값의 변경 여부를 알기 위해서는 호출되는 모든 함수를 추적해야 합니다. 모든 함수의 내부를 알거나 그럴 수 없다면 함수를 부르기 전에 값을 확인하고 함수를 호출한 뒤에 다시 값을 확인하는 엄청난 작업을 해야 합니다.

당연히 디버깅 작업이 굉장히 어려워 질 것 입니다. 반면 Call by value의 경우, 변수를 그대로 넘기면 값이 절대 변하지 않고 변수의 주소 값을 넘기면 값이 변할 수 있다는 것을 압니다. 함수를 호출하면서 바로 알 수 있습니다.

또한 function(1004); 이나 function(a+b); 와 같은 상수나 수식으로 함수를 호출하는 것이 Call by value에서는 가능하지만 상수나 수식에는 reference가 존재하지 않으므로 Call by reference는 불가능 합니다. 어떠세요? C언어가 Call by value를 지원하는 것은 나름대로 이유가 있지 않겠습니까?   












#Sinclair #씽클레어 #싱클레어 #씽클레어도씨 #씨언어 #씨프로그래밍  #C언어 #Cprogramming #C_Programming #C #Programming #Clanguage #C_Language

매거진의 이전글 함수와 표준 I/O
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari