Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C 매크로의 가변 길이 인수: __VA_ARGS__와 ## 연산자 활용법

C 매크로에서 가변 길이 인수란?

C 언어에서는 printf(), scanf()처럼 함수에 가변 길이 인수(variable length arguments)를 사용할 수 있습니다. 함수에서 가변 인수를 받으려면 매개변수 목록 끝에 줄임표(...)를 선언해야 하는데, 흥미롭게도 매크로에서도 동일한 방식으로 가변 길이 인수를 지원합니다.

매크로에서 가변 인수를 처리하려면 먼저 매개변수 목록 끝에 줄임표(...)를 포함해야 하며, 매크로 본문 안에서는 __VA_ARGS__ 식별자를 통해 전달된 가변 인수들을 참조합니다. 여기에 토큰 붙여넣기 연산자(token pasting operator)인 ##을 함께 사용하면 가변 인수 앞의 쉼표를 자동으로 제거할 수 있습니다. 덕분에 가변 인수가 하나도 전달되지 않는 경우에도 컴파일 오류 없이 매크로를 안전하게 호출할 수 있습니다.

다음 예제의 LOG_MESSAGE 매크로는 printf()나 scanf()처럼 가변 길이 인수를 받습니다. 이 매크로는 파일 이름(__FILE__), 줄 번호(__LINE__), 그리고 전달된 메시지를 함께 출력합니다. 첫 번째 인수인 pr은 우선순위를 결정하는 데 사용되며, 일반 정보(INFORMATION) 문자열인지 오류(ERROR)인지를 구분합니다.

예제 코드

#include <stdio.h>
#define INFO 1
#define ERR 2
#define STD_OUT stdout
#define STD_ERR stderr
#define LOG_MESSAGE(pr, strm, msg, ...) do {\
    char *str;\
    if (pr == INFO)\
        str = "INFORMATION";\
    else if (pr == ERR)\
        str = "ERROR";\
    fprintf(strm, "[%s] : %s : %d : "msg" \n", \
    str, __FILE__, __LINE__, ##__VA_ARGS__);\
} while (0)
int main(void) {
    char *s = "Test String";
    LOG_MESSAGE(ERR, STD_ERR, "Unable to open the file"); //일반 메시지 출력
    LOG_MESSAGE(INFO, STD_OUT, "%s is passed as argument", s); //문자열 인수 전달
    LOG_MESSAGE(INFO, STD_OUT, "%d + %d = %d", 14, 16, (14 + 16)); //정수 값 전달
}

실행 결과

[ERROR] : D:\text.c : 21 : Unable to open the file
[INFORMATION] : D:\text.c : 23 : Test String is passed as argument
[INFORMATION] : D:\text.c : 25 : 14 + 16 = 30

코드 핵심 포인트

  • do { } while(0) 구조: 매크로를 단일 문장처럼 취급하게 만들어, if-else 문 등에서 매크로를 사용할 때 발생할 수 있는 예기치 않은 동작을 방지하는 관용적(idiomatic) 기법입니다.
  • __FILE__과 __LINE__: 컴파일러가 제공하는 사전 정의 매크로로, 각각 현재 소스 파일 이름과 줄 번호로 치환됩니다. 디버깅 및 로깅에 매우 유용합니다.
  • msg와 문자열 리터럴의 결합: C 언어에서는 인접한 문자열 리터럴이 자동으로 하나로 연결되므로, "[%s] : %s : %d : " 뒤에 msg가 그대로 이어져 형식 문자열을 구성합니다.
  • ##__VA_ARGS__: 가변 인수가 비어 있을 때 앞의 쉼표를 제거해 주므로, LOG_MESSAGE(ERR, STD_ERR, "메시지")처럼 인수 없이 호출해도 안전합니다.

이처럼 가변 길이 인수 매크로를 활용하면 파일 이름과 줄 번호가 자동으로 기록되는 로깅 시스템을 손쉽게 구축할 수 있어, 실무 디버깅 환경에서 널리 사용됩니다.