티스토리 뷰


C언어 중 한 가지 재밌는 기능이 바로 이 가변 인자 함수이다.

처음에는 이러한 것에 대해서 전혀 의문을 품고 있지 않다가 나중에 한참 C를 배우다가

'어? 이거 어떻게 구현했지?'

라는 의문을 품게 되는 재미있는 요소이다.

C언어를 처음으로 하게 되면 배우게 되는 구문..

void main()
{
  printf("Hello World!");
}

뭐, 여기서는 그렇게 의문을 가지진 않을 것이다. 헬로월드는 뻔한 내용이기도 하니깐. 그런데 이것을 이렇게 바꿀 수도 있다..

void main()
{
  printf("%s" , "Hello World!");
}

그래..여기까진 좋다 이거지...그런데 printf를 아무 생각없이 이렇게 쓰다가 문득 앞에 있는 format string("%s")이 %d, %c, %s 등등 의 무한하게 설정이 가능하고 그 수에 따라 뒤에 따라오는 인자의 수가 매번 달라지는 것을 깨닫게 되는 순간 뒤통수를 딱 맞는 기분이 들 것이다.

어떻게 구현한 것일까?

처음엔 이렇게 생각했다..인자를 한 255개까지 제한하고 있지 않을까..-_-

int printf(char*,int) , int printf(char* , int , int); ..... int printf(char* , int * 255개);

뭐...그냥 이렇게 생각하고 보니 인자가 int만 들어오는게 아니잖아! char, float, double등등 기본형의 조합을 255가지 하려면...header파일의 용량이 몇 메가는 될듯 하다...

그럼 도대체 아무 생각없이 막 써왔던 이런 printf, scanf 등은 어떻게 구현했을까?

툴을 이용해서 이들 인자를 살펴보게 되면

int printf(const char* , ...);

이라고 되어있다..도대체 '...'이 뭐길래?

이 '...'은 바로 '가변 인자 함수(Variable argument function)'을 나타내는 것이다.

그럼 어떻게 쓰느냐? 간단하게 살펴보자...

선언은 위와 같이 ...으로 하고..예제 소스를 보자..(CodeProject 참고)

 int add(int x, ...)
{
    va_list list;
   //initialize the va_list i.e char*
   // variable 'list' by the address
   // of first unknown variable argument
   // by a call of va_start() macro
    va_start(list,x);
    int result=0;

    for(;;)
    { // in loop, retreive each argument
      // Second argument to va_arg is datatype
      // of expected argument
        int p=va_arg(list,int);
    if(p==0)
       break;
    result+=p;
     }
    va_end(list); // cleanup , set 'lsit' to NULL
    return result;
  }

딱 봐도 간단하다...

변수 va_list가 있고
사용하는 함수들은 va_start , va_arg, va_end가 있다..

va_list는 stdio.h에 정의 되어있고..

typedef char* va_list

으로 정의 되어있다. va_start는 매크로로 설정되어서 아래와 같다..

#define va_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v))

이거의 의미는 va_list를 인자로 받고, &v로 v의 주소를 구해서 _INTSIZEOF(v)를 더함으로써 인자로 넘어온 v의 다음인자의 주소값을 가져오는 것이다. _INTSIZEOF(v)는 주소값만큼 나누어 떨어지게 하는 것으로, 아래와 같이 정의되어있다.

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

주소는 4바이트씩 끊어서 계산하므로(32bit OS 상에서) 그것을 논리 연산자를 통해서 나누어 떨어지게 하는 것이다.

#define va_arg(ap,t)(*(t*)((ap +=_INTSIZEOF(t))-_INTSIZEOF(t)))

va_arg는 ap에 t의 타입 크기만큼 ap를 옮기고, 그 결과에 다시 t의 크기만큼 뺀다. 이 말은 ap를 다음 인자의 포인터로 넘기고 다시 앞으로와서 그 값을 리턴하는 것이다. 타입의 종류를 잘 설정해야겠다.

#define va_end(ap) ( ap = (va_list)0)

그리고 va_end는 ap를 초기화하게 된다.

이용의 예는 위에 예제소스만 가지고도 충분히 구현이 가능할 것이다.그럼 아래는 MSDN에서 제시하고 있는 requirements이다..


Routine Required header Optional headers Compatibility

va_arg

<stdio.h> and <stdarg.h>

<varargs.h>*

ANSI, Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003

va_end

<stdio.h> and <stdarg.h>

<varargs.h>*

ANSI, Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003

va_start

<stdio.h> and <stdarg.h>

<varargs.h>*

ANSI, Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003





아래는 참고한 사이트...여기 다 나와있다! - 마지막인자가 call by reference일 경우의 문제 해결도 있다..
http://www.codeproject.com/KB/cpp/argfunctions.aspx?df=100&forumid=15556&exp=0&select=503481


위키피디아 참고할만한 내용..다른 언어들도 많이 나와있다!
http://en.wikipedia.org/wiki/Varargs

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함