이번 포스팅에서는 정말 많은 개발자들이 사용하고 있는 기능이지만, 내가 사용하려고 하면 왠지 스택오버플로우를 뒤져보게 만드는 가변인자 다루기 에 대해 이야기해 보도록 하겠습니다.

가변인자 다루기 에 익숙해지면 때에 따라 매우 복잡하게 구현된 코드가 아주 심플해지는 마법을 경험하게 되기도 합니다.

가변인자 다루기 – 로그 출력

주로 가변인자를 접하게 되는 경우는 매크로를 통해 로그를 출력하고자 할 때 입니다.

다음 예제를 한번 볼까요.

#include <stdio.h>
#define debug(...) \
    fprintf(stdout, "%s:%d [%s] - ", __FILE__, __LINE__, __FUNCTION__); \
    fprintf(stdout, __VA_ARGS__); \
    fprintf(stdout, "\n");
int main(int argc, char** argv)
{
    debug("Hello world");
    return 0;
}

debug 라는 매크로를 정의했습니다. debug 매크로는 파라미터를 (…) 형태로 가변 인자로 받아 와서 로그를 출력할 때 소스코드의 파일명, 함수명, 라인 넘버를 함께 출력해 주는 기능을 하게 됩니다.

물론 매크로는 전처리기에 의해 빌드 시점에 치환될 것이기 때문에 시스템 성능 저하 문제를 일으키지도 않으면서 유용하게 사용할 수 있겠지요.

매크로 debug 에 대해 좀 자세히 볼까요

매크로의 파라미터 부분 (…) 은 가변인자를 의미합니다. 몇 개인지 모르지만 여러 파라미터가 있을 수 있다 정도로 해석하면 됩니다. 시스템 내부적으로는 스택에 순차적으로 쌓다가 마지막 파라미터 다음에 NULL 을 세팅하는 방식으로 동작합니다. 컴파일러와 HW에 따라 메모리에 어떻게 적재되는지 여부는 달라질 수 있습니다.

위의 예제에서는 “Hello world” 라는 문자열 하나만 파라미터로 전달했습니다. 따라서 __VA_ARGS__ 에도 “Hello world” 파라미터만 전달되는 것과 결과적으로 동일하게 동작하게 됩니다. 단순하게 파일명, 함수명, 라인넘버를 출력하는 이외에 별다른 이점이 없어 보이네요. 그렇다면 가변인자를 사용할 필요가 없는 걸까요.

아주 간단한 다른 예제를 한번 보겠습니다.

가변인자 다루기 로 printf 문법과 동일한 매크로 구현

#include <stdio.h>
#define debug(...) \
    fprintf(stdout, "%s:%d [%s] - ", __FILE__, __LINE__, __FUNCTION__); \
    fprintf(stdout, __VA_ARGS__); \
    fprintf(stdout, "\n");
int main(int argc, char** argv)
{
    debug("%s %d %s", "Hello", 2, "world");
    return 0;
}

첫 번째 예제와 비슷하지만, 마치 우리의 영혼의 단짝 printf 를 사용하는 것과 같은 문법입니다. 처리되는 과정을 보겠습니다.

매크로 처리 과정

먼저 파일명, 줄번호, 함수명을 찍어줍니다. 첫 번째 예제와 마찬가지로요. 그리고 __VA_ARGS__를 출력하게 되는데, 바로 이 부분에 “%s %d %s” 와 “Hello”, 2, “world” 가 순차적으로 입력이 됩니다.

전처리기에 의해 치환된 코드는 아마 다음과 논리적으로 같을 것입니다.

int main(int argc, char** argv)
{
    fprintf(stdout, "%s:%d [%s] - ", __FILE__, __LINE__, __FUNCTION__);
    fprintf(stdout, "%s %d %s", "Hello", 2, "world");
    fprintf(stdout, "\n");
}

변수의 개수가 몇 개인지 출력하고 싶은 데이터가 어떤 형태인지에 관계 없이 우리의 친구 printf 를 사용하는 것 처럼 로그 함수를 사용하면서도 파일명, 줄번호, 함수명 같은 부가 정보를 심플한 코드로 손쉽게 출력할 수 있게 되었습니다.

로그 모듈을 제작하고자 한다면 로그 레벨 같은 성분을 넣어도 좋겠지요.

예제는 로그 출력하는 모듈을 사용했지만, 사실 가변인자는 아주 다양하게 여러 가지 형태로 function이나 method에 적용할 수 있습니다.

마무리

가변인자 다루기 를 통해 로그 출력하는 매크로를 간단하게나마 살펴보았습니다. 코딩은 익숙해진 이후부터 응용력이 폭발하는 경험을 해 보셨을 겁니다. 부디 C언어 세계에서 그래도 나름 고급 프로그래밍 기법에 속하는 가변인자 다루기 를 숙지하는 데에 작게나마 도움이 되었기를 바랍니다.


Jay

Jay

S/W Engineer!!

0개의 댓글

답글 남기기

Avatar placeholder