C언어(2020년)

26. 가변인자 함수 만들기

리더2333 2020. 11. 4. 02:15
반응형

printf( ) 함수의 경우

printf("Helle World\n"); 라고 쓰면 인자가 1개 이다.

printf("%d\n", 10); 라고 쓰면 인자가 2개 이다.

printf("%d, %d\n", 10, 20); 라고 쓰면 인자가 3개이다.

이런식으로 인자가 변하는 함수는 어떻게 구현 되어있을까?

또는 어떻게 구현할 수 있을까?

먼저....

#include <stdio.h>

// args는 고정 매개변수
void printfMy(int args, ...)
{
    printf("%d ", args);
}

int main()
{
    printfMy(1, 10);
    printfMy(2, 10, 20);
    printfMy(3, 10, 20, 30);
    printfMy(4, 10, 20, 30, 40);

    return 0;
}

이와 같이 했을 때 ... 은 인자가 가변이라는 뜻이다.

하지만 실행 결과 1,2,3,4 즉, args 만 출력이 되었다.

가변인자의 값을 출력하려면 어떻게 해야 할까?

 

#include <stdio.h>
#include <stdarg.h>    // va_list, va_start, va_arg, va_end 를 사용하기 위해서

void printfMy(int args, ...)
{
    va_list ap;
    va_start(ap, args);
    for (int i = 0; i < args; i++)
    {
        int num = va_arg(ap, int);
        printf("%d ", num);
    }
    va_end(ap);
    printf("\n");
}

int main()
{
    printfMy(1, 10);
    printfMy(2, 10, 20);
    printfMy(3, 10, 20, 30);
    printfMy(4, 10, 20, 30, 40);

    return 0;
}
// 결과
// 10
// 10 20
// 10 20 30
// 10 20 30 40

va_list ap

va_start( ) 함수의 첫번째 인자로 argument pointer 가 들어간다.

여기에서 argument pointer 는 va_list ap; 즉, ap 이다.

이 ap 는 인자로 들어온 여러개의 값이 배열과 같이 연달아 있는 메모리의 첫 시작 주소가

va_start( ) 함수에 의해 얻어진다.

(ap 는 관례적으로 ap 라고 쓰며 char* 로 정의되어 있다.)

 

va_start(ap, 마지막고정인수)

va_start( ) 함수에는 반드시 고정인수가 들어가야 한다.(두번째 인자로 들어감)

printfMy(고정인수, ...);

printfMy 함수의 첫번째 인수가 고정인수인데,

va_start( ) 함수는 이 고정인수의 주소를 이용하여 첫 가변인수의 주소를 구하기 때문이다.

 

참고)

고정인수가 여러개일경우에는 마지막 고정인수가 들어가야 한다.

prinfgMy(고정인수1, 고정인수2, ...)

 

va_arg(ap, 인수타입)

va_arg( ); 함수는 실제로 가변인수를 얻어오는 함수인데,

va_start( ) 함수가 첫번째 가변인수의 주소를 ap 에 구해 놓았으니,

우리는 va_arg 를 이용하여 읽어들이면 된다.

예를들어 ap 위치에 있는 정수를 읽고 싶으면 va_arg(ap, int) 를 호출하고

실수를 읽고 싶으면 va_arg(ap, double) 을 호출하면 된다.

이 함수는 ap 의 위치를 자료형타입에 맞게 읽어 주고 리턴해주며,

또한 ap 를 다음 가변인수의 주소로 옮겨준다.

 

va_end(ap)

이 함수는 ap 의 값을 초기화 해준다. 아니, 실제로는 아무런 동작을 하지 않는다.

플랫폼에 따라서 가변인수를 읽은 후에 초기화 해주는 필요가 있을 수 있고,

관례적으로 사용하긴 하는데, 적어도 인텔 계열의 CPU 에서는 아무것도 하지 않는다.

그러나 미래에 어떻게 바뀔지 모르니, 넣어주는것이 옳다고 생각한다.

 

 

 

 

#include <stdio.h>
#include <stdarg.h>    // va_list, va_start, va_arg, va_end 를 사용하기 위해서

void printfMy(const char* types, ...)
{
    va_list ap;
    int i = 0;
    va_start(ap, types);
    while (types[i] != '\0') // 문자열의 끝 '\0' 이면 while 문을 벗어남
    {
        switch (types[i])
        {
        case 'd':
            printf("%d ", va_arg(ap, int));
            break;
        case 'f':
            printf("%f ", va_arg(ap, double));
            break;
        case 'c':
            printf("%c ", va_arg(ap, char));
            break;
        case 's':
            printf("%s ", va_arg(ap, char*));
            break;
        default:
            break;
        }
        i++;
    }
    va_end(ap);
    printf("\n");
}

int main()
{
    printfMy("sdfc", "Hello World", 30, 102.86f, 'a');
    return 0;
}

printfMy 함수에서 " " 내부의

s 자리에 "Hello World를

d 자리에 30을

f 자리에 102.86f을

c 자리에 'a' 를 출력할 수 있도록 하였다.

 

 

 

참고) GCC 의 경우

char,bool,short 은
va_arg(ap, int);

float 는
va_arg(ap, double)
로 읽어야 한다.

반응형

'C언어(2020년)' 카테고리의 다른 글

28. union  (0) 2020.11.04
27. 전처리기  (0) 2020.11.04
25. Call by Value, Call by reference, Call by Address  (0) 2020.11.04
24. 함수  (0) 2020.11.04
23. define  (0) 2020.11.04