티스토리 뷰
C언어 가변 인자(가변 파라미터)를 사용해보자
1. 가변인자란 무엇일까?
printf 함수를 써보셨나요? 우리는 자연스럽게 printf("%d * %d = %d", 3, 5, 3*5)
라고 쓰고 있습니다. 가만보면 printf라는 함수는 인자를 1개만 넣어도 되고, 2개만 넣어도 되고, 3개, 4개 그 이상을 넣어도 문제없이 돌아갑니다. c언어에서 이런것이 가능한가요?
오늘은 이런 마법을 부릴 수 있게 하는 가변 인자라는 것에 대해 포스팅을 하겠습니다.
2. printf의 원형
printf 함수의 원형을 찾아보신 분이 계실지 모르겠습니다. printf의 원형을 찾아보면 다음과 같습니다.
int printf(const char* format, ...)
- 참조: 컴파일러에 따라 printf의 실제 구현과는 원형(prototype)이 다를 수 있습니다.
printf
함수의 두 번째 인자로 사용되는 ...
이 가변 인자 혹은 가변 파라미터라고 불리는 것입니다. 본 포스팅 서두에 언급한것처럼 printf
를 쓸 때, 인자(파라미터)로 아무것도 넘겨주지 않을 수도 있고, 혹은 여러 개의 인자를 넘겨줄 수도 있습니다. 가변 인자가 무엇인지 알았으니 이제 차근차근 설명해 드리도록 하겠습니다.
먼저 가변 인자 함수를 만들기 위해서는 stdarg.h
헤더파일을 포함해야합니다. 이 헤더 파일에 가변인자함수를 만들 때 필요한 각종 매크로 들이 정의되어 있습니다. 그리고 최소 1개 이상의 고정 인수가 있어야 합니다. 가변인자를 나타내는 ...
은 파라미터 순서 상 가장 뒤에 있어야 합니다.
3. 가변 인자를 사용하는 첫 번째 예제
말로만 설명하면 헷갈리실테니 먼저 소스코드 예제를 보여드리겠습니다.
#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
로 만들어진 포인터에게 가변인자 중 첫 번째 인자의 주소를 가르쳐주는 중요한 매크로입니다. 이 녀석의 모양은 사실 이렇게 생겼습니다. (Microsoft Visual Studio 기준)#define va_start(ap, v) ( (ap) = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
- ap:
va_list
로 만든 포인터가 담깁니다. - v: 마지막 고정인수가 담깁니다.
- _ADDRESSOF(v) => &(v), 즉 주소로 바꿔주는 매크로입니다.
- _INTSIZEOF(n) => ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ), 비트 연산이 들어가는데 자세한 계산까지는 알 필요 없습니다. 마지막 고정인수의 사이즈를 구해서 그 다음 인자의 시작주소. 즉, 가변인자의 시작주소까지의 메모리상의 거리 를 구해주는 매크로입니다.
- ap:
va_arg : 특정 가변인자를 가리키고 있는
va_list
의 포인터를 다음 가변인자로 이동시켜 주는 매크로입니다. 이 녀석의 모양은 아래와 같습니다.#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
- ap:
va_list
로 만든 포인터가 담깁니다. - t:
int
나long
,double
과 같은 타입 이름이 담깁니다. - [참고]
char
,short
의 경우에는int
로 대신 쓰고,flaot
의 경우에는double
로 대신 쓴 이후 형 변환을 해주어야 한다고 합니다. (예.char ch = (char) va_arg(ap, int);
)
- ap:
va_end : 사용한 가변인자 변수를 끝낼때 사용합니다. 단순히 모양을 보면 NULL 포인터로 돌려주는 매크로인데, 프로그램상 어떤 경우가 생길 지 모르니까 놓치지 말고 써주도록 합시다.
#define va_end(ap) ( ap = (va_list)0 )
- ap:
va_list
로 만든 포인터가 담깁니다.
- ap:
4. vsprintf/vnsprintf 의 사용
그리고 가변인자함수를 사용할 때, 많이 사용하는 함수가 하나 더 있는데요.
int vsprintf(char* dest, const char* format, va_list args)
int vsnprintf(char* dest, size_t maxCount, const char* format, va_list args)
- 함수명: vsprintf / vnsprintf
- 필요헤더: stdio.h
- 리턴타입: int
- 파라미터:
- dest:
format
에 따라 만들어진 내용이 담길 버퍼 - format: 포맷
- args: 가변 파라미터
- 리턴값: 문자열의 길이
- dest:
- 함수설명: dest 변수에 형식에 따라 만들어진 문자열이 저장된다.
바로 요놈입니다! sprintf와 상당히 비슷하게 생긴 이놈. 이 놈은 다양한 타입의 가변 인자들을 %d
, %s
, %c
등의 형식을 읽어서 알아서 예쁘게 포장해주는 함수입니다.
만약 이런 함수가 없고, 가변인자에 따라 우리가 수동으로 코딩을 해야 한다고 가정하면 다음과 같은 작업을 해야할 겁니다. %d
를 발견했을 때는 타입이 int
형이므로 다음 가변 인자를 va_arg(ap, int)
로 받아와서 담고, %s
를 발견했을 때는 char *
형이므로 va_arg
........
물론 이렇게 직접 만들어주어도 되지만!! 자동으로 포장해주는 함수가 만들어져 있잖아요! 그럼 우린 얘를 쓰면 되는겁니다. ㅎㅎ 어디다 쓰는지는 아래 예제를 보도록 하겠습니다. 제가 가변인자를 사용하는 90%는 아래와 같은 목적때문입니다!
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void errorPrintf(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
errorPrintf("Don't divide by %d\n", b);
return 0;
}
[출력결과]
[ERROR] Don't divide by 0
아주 간단하죠? 위와 같이 에러 처리용 함수로 만들어 사용할 수 있습니다. 이름도 errorPrintf
이런식으로 바꿔서 사용하게 됨으로써, 프로그램 코드를 볼 때 "아! 이 부분은 에러처리부분!" 하며 넘어갈 수 있도록 코드 가시성이 높아지구요.
프로그램 동작 중 발생하는 에러에 대해 errno
로 저장하는 프로그래밍이 되어 있다면 어떤 에러가 발생했는지 앞, 또는 뒤에 덧붙여서 출력해 줄 수도 있습니다. (물론 출력이 아니고 fprintf
등을 이용하여 프로그램 에러로그 안에 담을 수도 있습니다.)
아무튼 이렇게 가변인자에 대한 사용을 알아보았습니다! 이상으로 오늘의 포스팅을 마치도록 하겠습니다.
- 참조: 원래 매개변수와 인자(parameter, argument) 등의 용어는 명백히 구별되지만, 본문에서는 거의 같은 의미로 놓고 사용 되었기에 용어가 왔다갔다 할 수 있습니다. 두 용어의 의미 상 구별을 하지 않았다는 점, 다시 한 번 말씀드립니다.
'C, C++ > C, C++' 카테고리의 다른 글
c언어 매크로 사용법 - #, ## 연산자 (2) | 2014.03.02 |
---|---|
C언어 bsearch() - 이진탐색 함수 (0) | 2014.02.16 |
c/c++ sprintf, snprintf 함수 (13) | 2014.01.21 |
C언어 qsort() 함수 (6) | 2014.01.02 |
배열의 개수를 세는 _countof 매크로 (0) | 2013.12.31 |
- Total
- Today
- Yesterday
- zone
- 우분투 16.04
- angular2
- observable
- 리눅스 터미널 색상
- typeScript
- git proxy
- NgZone
- lua table
- 안시 컬러
- 스위프트
- JavaScript
- 타입스크립트
- itoa
- qemu linux arm
- ECMA2015
- Swift
- QT
- 챗봇
- 안시 색상
- vim
- ansi color
- Rx.js
- 폰트 조정
- ZONES
- C언어
- terminal 색
- Zone.js
- Angular
- git 설정
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |