1 분 소요

C++에서 가끔 #을 붙여 쓰는 매크로 인자를 봤는데 오늘 용도를 알았다. 스트링화 연산자(stringizing operator) 라고 부른다.

2026년 수정 안내 원래 이 글에는 “#a는 변수명인 aaa의 값이 찍힌다”고 적혀 있었는데 앞뒤가 모순된 설명이었다. #a가 만드는 것은 문자열 "aaa" 다. 값과는 무관하다. 설명을 바로잡고 ## 연산자도 함께 정리한다.

# — 인자를 문자열로 바꾼다

#include <cstdio>

#define Test(a) { printf( "%s\n", #a ); }

int main()
{
    int aaa = 1000;
    Test( aaa );      // "aaa" 가 출력된다. 1000이 아니다.

    return 0;
}

#a는 전처리 단계에서 매크로 인자로 들어온 토큰을 그대로 문자열 리터럴로 바꾼다. 위 코드는 전처리 후 printf("%s\n", "aaa");가 된다. 변수 aaa의 값 1000은 전처리기가 알지도 못하고, 알 필요도 없다.

식을 그대로 넘겨도 마찬가지다.

Test( a + b * 2 );    // "a + b * 2" 가 출력된다

어디에 쓰나

변수명이나 식을 로그에 그대로 남기고 싶을 때 유용하다. 간이 assert를 만들 때가 대표적이다.

#define CHECK( expr )                                                   \
    do {                                                                \
        if ( !( expr ) )                                                \
            printf( "CHECK FAILED: %s (%s:%d)\n", #expr, __FILE__, __LINE__ ); \
    } while ( 0 )

CHECK( ptr != nullptr );
// 실패 시 → CHECK FAILED: ptr != nullptr (main.cpp:42)

로그 라이브러리에서 클래스 이름을 태그로 출력할 때도 자주 쓰인다.

## — 토큰을 붙인다

#과 짝을 이루는 연산자로 토큰 붙이기 연산자(token-pasting operator)##이 있다. 좌우 토큰을 이어붙여 하나의 식별자를 만든다.

#define DECLARE_VAR( name ) int var_##name = 0;

DECLARE_VAR( count );   // int var_count = 0;
DECLARE_VAR( index );   // int var_index = 0;

주의할 점

매크로를 한 번 더 거쳐야 값이 펼쳐진다. #은 인자를 펼치지 않고 그대로 문자열로 만들기 때문에, 매크로 상수를 문자열로 바꾸려면 단계를 하나 더 둬야 한다.

#define VERSION 3

#define STR_DIRECT( x ) #x
#define STR( x )        STR_DIRECT( x )

printf( "%s\n", STR_DIRECT( VERSION ) );   // "VERSION"  ← 펼쳐지지 않는다
printf( "%s\n", STR( VERSION ) );          // "3"        ← 한 단계 거쳐 펼쳐진다

C++이라면 매크로 자체를 줄이는 편이 좋다. 다만 “식을 소스에 적힌 그대로 문자열로 남기는” 일만큼은 매크로가 아니면 할 수 없다. C++20부터는 std::source_location으로 파일/줄/함수명은 매크로 없이 얻을 수 있다.

참고자료

댓글 남기기