C언어에서 함수를 호출하여 결과를 얻는 방법은 일반적으로 return으로 얻는 방법을 가장 많이 사용합니다.

int max(int value1, int value2)
{
    return value1 > value2 ? value1 : value2;
}

위와 같이 두 데이터중에서 큰 데이터를 return하는 프로그램과 같이 그 결과를 return으로 얻는 것입니다. 이 경우 단점은 함수 호출 시에 1개의 데이터만 얻을 수 있습니다. 만약, 한번에 2개 이상의 데이터에 대한 결과를 얻고 싶을 때에는 어떻게 할까요? 그것은 함수의 파라미터로 데이터를 받는 것입니다.

#include <stdio.h>

int divide(int data1, int data2, int div, int mod)
{
    if(data2 == 0) {
        return -1;
    }
    div = data1 / data2;
    mod = data1 % data2;
    
    return 0;
}

int main(int argc, char **argv)
{
    int d1 = 100;
    int d2 = 7;
    int d3 = 0;
    int d4 = 0;
    
    divide(d1, d2, d3, d4);
    printf("%d / %d = %d\n", d1, d2, d3);
    printf("%d %% %d = %d\n", d1, d2, d4);
    
    return 0;
}

결과>
100 / 7 = 0
100 % 7 = 0

위의 예제에서 두 수를 나눈 몫과 나머지를 계산해서 한번에 얻는 함수를 만들었습니다. 그 결과는 

100 / 7 = 14

100 % 7 = 2

를 예상했지만, 

 

100 / 7 = 0

100 % 7 = 0

이 나왔습니다.

 

C언어는 함수를 호출할 때에 파라미터로 변수를 전달하면 그 변수가 바로 전달되는 것이 아니라 그 변수의 값을 복사를 하여 복사본을 만들고 그 복사본을 전달하게 됩니다.  또한 그 복사본은 함수의 호출이 끝남과 동시에 사라지게 됩니다. 이렇게 복사본을 전달했기 때문에 복사본에 데이터를 아무리 수정을 해도 원본은 변경되지 않게 됩니다. 이와 같이 함수를 호출할 때에 원본이 전달되지 않고 복사본을 전달하여 함수를 실행하는 방식을 Call by Value(값에 의한 호출)라고 합니다. 

 

Call by value에 대한 테스트용으로 가장 많이 사용하는 함수가 바로 swap( )함수를 예를 들 수 있습니다. 

 

예). Call by value 예제.

#include <stdio.h>

void swap(double value1, double value2)
{
    double temp = value1;
    
    value1 = value2;  
    value2 = temp;
    
    printf("swap() 호출중: value1 = %.1lf, value2 = %.1lf\n", value1, value2);    
}

int main(int argc, char **argv)
{
    double data1 = 100.0;
    double data2 = 200.0;
    
    printf("swap() 호출전: data1 = %.1lf, data2 = %.1lf\n", data1, data2);

    swap(value1, value2);
    
    printf("swap() 호출후: data1 = %.1lf, data2 = %.1lf\n", data1, data2);

    return 0;
}

 

결과>

swap() 호출전 : data1 = 100.0, data2 = 200.0
swap() 호출중 : value1 = 200.0, value2 = 100.0
swap() 호출후 : data1 = 100.0, data2 = 200.0

위의 swap()함수는 내부에서 value1과 value2를 바꿔서 저장하는 함수로 구현되었습니다. 함수 내에서는 값이 변경되었지만, 함수의 호출이 끝나면 변경된 값이 반영이 되지 않았습니다. 이유는 함수의 파라미터로 데이터를 전달할 때에는 복사본을 전달하기 때문에 복사본만 바뀌었고, 그 복사본은 함수가 호출이 끝나면 사라지기 때문입니다. 

 

그러면 C언어에서 내부에서 계산된 결과를 파라미터로 다시 전달하여 값을 변경할 수는 없을까요? 이때 사용하는 방법이 파라미터로 Pointer 변수를 전달하여 변경된 값을 얻는 방법이 있습니다.

 

 

예). Call by Reference 예제

#include <stdio.h>

void swap(double *value1, double *value2)
{
    double temp = *value1;
    
    *value1 = *value2;  
    *value2 = temp;
    
    printf("swap() 호출중: *value1 = %.1lf, *value2 = %.1lf\n", *value1, *value2);    
}

int main(int argc, char **argv)
{
    double data1 = 100.0;
    double data2 = 200.0;
    
    printf("swap() 호출전: data1 = %.1lf, data2 = %.1lf\n", data1, data2);

    swap(&value1, &value2);
    
    printf("swap() 호출후: data1 = %.1lf, data2 = %.1lf\n", data1, data2);

    return 0;
}

 

결과>

swap() 호출전 : data1 = 100.0, data2 = 200.0
swap() 호출중 : *value1 = 200.0, *value2 = 100.0
swap() 호출후 : data1 = 200.0, data2 = 100.0

위와 같이 호출을 하면, 내부에서 변경한 값이 변경되어 반영된 것을 확인할 수 있습니다.

 

이와 같이 함수의 파라미터에 주소를 전달하는 방식을 Call by reference(참조에 의한 호출)라고 합니다. 학술적으로 표현하는 이름은 명확하게 구분해서 부르기도 합니다만, 일반적으로 C언어의 책에서 표현하는 이름은 Call by reference라고 합니다. 

 

그런데, 위에서 Call by value를 설명할 때에 함수는 함수를 호출할 때에 복사본을 전달하기 때문에 그 값이 변경되지 않는다고 했었는 데, 왜 변경이 되었는냐?고 의문이 있을 수 있습니다.

맞습니다. C언어는 항상 파라미터로 변수를 넘기면 변수의 원본은 놔두고 데이터를 복사해서 복사본을 전달하게 됩니다. (원래 C언어는 call by value만 존재합니다.)

 

위의 swap( )함수의 파라미터가 double * 타입입니다. 즉, double *value1; 로서 변수 value1은 주소를 저장하는 변수가 됩니다. value1에 저장된 값이 메모리 번지이므로 C언어는 value1의 값(즉, 주소)에 대한 복사본을 만들어서 전달하게 됩니다. 위의 예에서 *value2 =  temp; 처럼 사용하게 되면 value2에 저장된 값은 번지이고, *value2는 그 번지의 값을 temp로 바꾸어라는 의미가 되므로 값이 변경됩니다. 다만 C언어에서는 Call by value의 한 형태로서 Pointer를 전달하는 것을 Call by reference라고 표현합니다. C++이나 Pascal은 Call by reference를 지원해줍니다.

C++에서는

#include <stdio.h>

void swap(double &value1, double &value2)
{
    double temp = value1;
    
    value1 = value2;  
    value2 = temp;
}

int main(int argc, char **argv)
{
    double data1 = 100.0;
    double data2 = 200.0;
    
    printf("swap() 호출전: data1 = %.1lf, data2 = %.1lf\n", data1, data2);

    swap(data1, data2);
    
    printf("swap() 호출후: data1 = %.1lf, data2 = %.1lf\n", data1, data2);

    return 0;
}

형식으로 함수를 정의할 때에 변수앞에 &를 붙이고, 함수를 call할 때에는 &없이 그냥 사용하는 형식으로 Call By Reference를 지원하고, Pascal언어에서는 함수를 정의할 때에 파라미터 변수 앞에 var 예약어를 붙임으로써 Call by reference임을 명시적으로 표현하게 됩니다.

 

예제). Pointer를 이용한 문자열 길이를 얻는 예제

#include <stdio.h>

int strlen(const char *str)
{
    int len = 0;
    while(*str) {
    	len++;
        str++;
    }
    return len;
}

int main(int argc, char **argv)
{
    char value[100] = "Test Program.";
    int  len;
    
    len = strlen(value);
    printf("value = [%s]\n", value);
    printf("lenght = %d\n", len;
    
    return 0;
}

결과>
value = [Test Program.]
length = 13

이 예제를 만든 이유는 C언어는 call by value만 지원한다는 것을 보여주기 위한 예제입니다. 위와 같이 strlen( )함수를 구현할 때에 내부에서 str++을 사용하여 str에 저장된 번지를 계속 바꾸고 있었고, 결과적으로는 그 문자열의 끝인 0x00이 있는 곳까지 이동하였습니다 그러나, 함수가 호출된 후에 value를 출력하면 변경사항이 없다는 것을 알 수 있습니다. C언어에서는 Call by value 중에서 Pointer를 전달하는 형태를 Call by reference라고 합니다. 

 

결론적으로 말하면, C언어에서는 함수 호출 방식은 동작 원리 상으로 Call by value만 존재하지만, Pointer를 전달하는 것을 Call by reference라고 한다고 이해를 하시면 됩니다. 시험 볼 때에는 Call by value만 있다고 하면 틀리겠죠. (아마도 이글을 보시는 분들 중에서 뭐라고 하실 분들이 많이 있을 지도....)

 

 

위의 divide()함수의 call by reference

int divide(int data1, int data2, int *div, int *mod)
{
    if(data2 == 0) {
        return -1;
    }
    *div = data1 / data2;
    *mod = data1 % data2;
    
    return 0;
}

 

 

 

 

 

C Programming Language 문법

1. C 프로그래밍 언어는? 2. C언어 개발 환경 (실습 환경) 3. C언어의 컴파일 과정 4. C 소스 파일 구성 5. 주석문(Comment) 6. 식별자 명명 규칙 7. C 프로그래밍의 시작 - 함수 8. 변수와 상수 (정수형) 9. 변..

www.it-note.kr

 

블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

Tag ,

댓글을 달아 주세요