출처 : http://norus.tistory.com/19#comment12673780
printf의 원형을 찾아보신 분이 계실지 모르겠지만, printf의 원형을 찾아보면 다음과 같습니다.
int Printf (const char* Format, ...)
(실제 정의되어 있는 부분과 약간 차이가 있을 수 있습니다.)
위에 보이시는 "..." 이 부분이 가변 인자 혹은 가변 파라미터라고 불리우는 것입니다.
printf를 쓸 때 느끼셨겠지만, 파라미터로 아무것도 넘겨주지 않을 수도 있고, 여러개를 넘겨줄 수도 있습니다.
가변 인자가 무엇인지 알았으니 이제 차근차근 설명해 드리도록 하겠습니다.
우선, 가변 인자 함수를 만들기 위해서는 stdarg.h 헤더가 필요합니다. 이 헤더에 가변인자함수를 만들 때 필요한 매크로 들이 정의되어 있습니다.
그리고 최소 1개 이상의 고정 인수가 있어야 합니다. 가변인자를 나타내는 "..." 은 파라미터 순서 상 가장 뒤에 있어야 합니다.
말로만 설명하면 헷갈리실테니 먼저 소스코드 예제를 보여드리겠습니다.
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 | #include <stdio.h> #include <stdarg.h> int sum( int count, ...) { int res = 0; va_list ap; int i; va_start (ap, count); for (i=0; i>count; i++) res += va_arg (ap, int ); va_end (ap); return res; } int main() { printf ( "%d\n" , sum(10, 1,2,3,4,5,6,7,8,9,10)); return 0; } |
<출력 결과>
55
위 예제는 가변인자함수를 이용해서 모든 파라미터를 더해주는 Sum 함수를 만든 것입니다.
자 그럼 지금부터 가변인자함수에 필요한 애들을 소개하겠습니다.
- va_list : 모양은 멋지게 생겼지만 사실은 char * 로 정의되어 있는 녀석입니다. 이 포인터가 각 인자의 시작주소를 가리키게 됩니다.
- va_start : 요놈이 va_list로 만들어진 포인터에게 가변인자 중 첫 번째 인자의 주소를 가르쳐주는 중요한 매크로입니다.
이 녀석의 모양은 사실 이렇게 생겼습니다.
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
매크로의 첫 번째 인자로는 va_list로 만든 포인터가 담기고,
매크로의 두 번째 인자로는 마지막 고정 인수가 담겨야 합니다.
* _ADDRESSOF(v) => &(v) , 즉 주소로 바꿔주는 매크로입니다.
* _INTSIZEOF(n) => ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) , 비트 연산이 들어가는데 자세한 계산까지는 알 필요 없을 것 같고 마지막 고정인수의 size를 구해서 그 다음 인자의 시작주소. 즉 가변인자의 시작주소까지의 메모리상의 "거리" 를 구해주는 매크로입니다.
- va_arg : 얘는 특정 가변인자를 가리키고 있는 va_list의 포인터를 다음 가변인자로 이동시켜 주는 매크로입니다.
이 녀석의 모양은 아래와 같습니다.
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
매크로의 첫 번째 인자로는 va_list로 만든 포인터가 담기고,
매크로의 두 번째 인자로는 타입 이름이 담깁니다. (int, long, double 등등)
* 참고: char, short 의 경우에는 int로 대신 쓰고, flaot의 경우에는 double로 대신 쓴 이후 형 변환을 해주어야 한다고 합니다. (예. char ch = (char) va_arg(ap, int); )
- va_end : 얘는 가변인자를 끝내는 역할입니다. 단순히 모양을 보면 NULL 포인터로 돌려주는 매크로인데, 프로그램상 어떤 경우가 생길 지 모르니까 놓치지 말고 써주도록 합시다!
#define _crt_va_end(ap) ( ap = (va_list)0 )
매크로의 인자로 va_list로 만든 포인터가 담깁니다.
그리고 가변인자함수를 사용할 때, 또 많이 사용하는 함수가 하나 있는데요.
int vsprintf (char* Dest, const char* Format, va_list Args)
int vsnprintf (char* Dest, size_t MaxCount, const char* Format, va_list Args)
- 함수명 : vsprintf
- 필요헤더 : stdio.h
- 리턴타입 : int
- 파라미터 : 1. Format에 따라 만들어진 내용이 담길 버퍼,
2. 포멧
3. 가변 파라미터 - 리턴값 : 문자열의 길이
- 함수역할 : Dest 변수에 형식에 따라 만들어진 문자열이 저장된다.
바로 요놈입니다!
sprintf와 상당히 비슷하게 생긴 이놈,
이 놈은 다양한 타입의 가변 인자들을 %d, %s, %c 등의 형식을 읽어서 알아서 예쁘게 포장해주는 함수입니다.
우리가 %d 를 발견했을 때는 타입이 int 형이므로 다음 가변 인자를 va_arg(ap, int) 로 받아와서 담고, %s를 발견했을 때는 char * 형이므로 va_arg........
물론 이렇게 직접 만들어주어도 되지만!! 자동으로 포장해주는 함수가 만들어져 있잖아요! 그럼 우린 얘를 쓰면 되는겁니다. ㅎㅎ
어디다 쓰는지는 아래 예제를 보도록 하겠습니다. 제가 가변인자를 사용하는 90%는 아래와 같은 목적때문입니다!
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 | #include <stdio.h> #include <stdarg.h> #include <string.h> void Err_print( char * fmt, ...) { char buf[512] = {0,}; va_list ap; strcpy (buf, "Error: " ); va_start (ap, fmt); vsprintf (buf + strlen (buf), fmt, ap); va_end (ap); puts (buf); } int main() { int a = 10, b = 0; if (b != 0) printf ( "%d\n" , a / b); else Err_print( "Don't divide by %d\n" , b); return 0; } |
<출력결과>
Error: Don't divide by 0
아주 간단하죠? 위와 같이 Error 처리용 함수로 만들어 사용할 수 있습니다.
이름도 Error_printf 이런식으로 바꿔서 사용하게 됨으로써, 프로그램 코드를 볼 때 "아! 이 부분은 에러처리부분!" 하며 넘어갈 수 있는 등, 코드 가시성이 높아지구요.
프로그램 동작 중 발생하는 error에 대해 errno로 저장하는 프로그래밍이 되어 있다면 어떤 에러가 발생했는지 앞, 또는 뒤에 덧붙여서 출력해 줄 수도 있습니다. (물론 출력이 아니고 fprintf 등을 이용하여 프로그램 에러로그 안에 담을 수도 있습니다.)
아무튼 이렇게 가변인자에 대한 사용을 알아보았습니다!
이상으로 오늘의 포스팅을 마치도록 하겠습니다.
* 원래 매개변수와 인자(parameter, argument) 등의 용어는 명백히 구별되지만, 본문에서는 거의 같은 의미로 놓고 사용 되었기에 용어가 왔다갔다 할 수 있습니다. 두 용어의 의미 상 구별을 하지 않았다는 점, 다시 한 번 말씀드립니다.
'C/C++' 카테고리의 다른 글
소켓통신함수 Socket close()와 shutdown() 차이 (0) | 2015.05.13 |
---|---|
Windows Data Type (0) | 2015.03.18 |
wchar_t <-> char (0) | 2013.05.30 |
[C++]OpenGL, OpenCV를 이용하여, PNG파일 불러오기(초간단) (0) | 2013.04.29 |
[C언어] 문자열 치환함수 (0) | 2012.04.27 |