수색…


소개

C 전처리 기는 코드의 실제 컴파일 전에 실행되는 간단한 텍스트 구문 분석기 / 대체기입니다. C (및 이후 C ++) 언어의 사용을 확장하고 쉽게하기 위해 다음 용도로 사용할 수 있습니다.

에이. #include 사용 하여 다른 파일 포함

비. #define 사용하여 텍스트 대체 매크로 정의

기음. #if #ifdef 사용한 조건부 컴파일

디. 플랫폼 / 컴파일러 관련 로직 (조건부 컴파일의 확장으로서)

비고

프리 프로세서 문은 소스 파일이 컴파일러로 전달되기 전에 실행됩니다. 이들은 매우 낮은 수준의 조건부 논리를 사용할 수 있습니다. 사전 처리기 구조 (예 : 객체와 같은 매크로)는 일반 함수처럼 형식이 지정되지 않으므로 (컴파일 전에 사전 처리 단계가 발생 함) 컴파일러에서 유형 검사를 시행 할 수 없으므로 신중하게 사용해야합니다.

경비원 포함

헤더 파일은 다른 헤더 파일에 포함될 수 있습니다. 따라서 여러 헤더를 포함하는 소스 파일 (컴파일 유닛)은 간접적으로 여러 헤더를 두 번 이상 포함 할 수 있습니다. 두 번 이상 포함 된 헤더 파일에 정의가 포함 된 경우 컴파일러는 (예 : 전처리 후) One Definition Rule (예 : 2003 C ++ 표준의 §3.2) 위반을 감지하여 진단 및 컴파일에 실패합니다.

때때로 헤더 가드 또는 매크로 가드라고도하는 "포함 가드"를 사용하여 여러 포함이 방지됩니다. 이는 전 처리기 #define , #ifndef , #endif 지시문을 사용하여 구현됩니다.

// Foo.h
#ifndef FOO_H_INCLUDED 
#define FOO_H_INCLUDED

class Foo    //  a class definition
{
};

#endif

인클루드 가드를 사용할 때의 주요 이점은 모든 표준 호환 컴파일러와 프리 프로세서와 함께 작동한다는 것입니다.

그러나 매크로가 프로젝트에서 사용되는 모든 헤더 내에서 고유한지 확인해야하기 때문에 가드를 포함 시키면 개발자에게 몇 가지 문제가 발생합니다. 특히, 두 개 (또는 그 이상) 헤더가 포함 보호 (guard)로 FOO_H_INCLUDED 를 사용하면 컴파일 유닛에 포함 된 첫 번째 헤더는 다른 헤더가 포함되는 것을 효과적으로 방지합니다. 프로젝트에서 헤더가 포함 된 여러 타사 라이브러리가 포함 된 경비원을 공통적으로 사용하는 경우 특정 문제가 발생합니다.

또한 포함 가드에 사용 된 매크로가 헤더 파일에 정의 된 다른 매크로와 충돌하지 않도록해야합니다.

대부분의 C ++ 구현#pragma once 지시어도 지원하므로 파일이 단일 컴파일 내에 한 번만 포함됩니다. 이것은 사실상의 표준 지시어이지만 ISO C ++ 표준의 일부는 아닙니다. 예 :

// Foo.h
#pragma once

class Foo
{
};

동안 #pragma once 방지와 관련된 몇 가지 문제가 경비하는 등 #pragma 정의에 의해 표준 인은 - - 본질적으로 특정 컴파일러 후크이며, 자동으로이를 지원하지 않는 컴파일러에 의해 무시됩니다. #pragma once 를 사용하는 프로젝트는 지원하지 않는 컴파일러로 이식하기가 더 어렵습니다.

C ++에 대한 많은 코딩 지침과 보증 표준은 헤더 파일을 #include 포함하거나 헤더에 포함 경비를 배치하기위한 목적 이외에 전처리 기의 사용을 특별히 권장하지 않습니다.

조건부 논리 및 플랫폼 간 처리

요컨대, 조건부 전처리 논리는 매크로 정의를 사용하여 컴파일 할 때 코드 논리를 사용 가능하게하거나 사용할 수 없게 만드는 것에 관한 것입니다.

세 가지 유스 케이스가 있습니다 :

  • 같은 앱 (예 : 추가 로깅)의 후보가 될 수있는 다양한 앱 프로필 (예 : 디버그, 출시, 테스트, 최적화 됨)
  • 크로스 플랫폼 컴파일 - 단일 코드 기반, 다중 컴파일 플랫폼.
  • 약간 다른 기능을 가진 여러 응용 프로그램 버전 (예 : 기본, 프리미엄 및 프로 버전의 소프트웨어)에 공통 코드 기반을 사용합니다.

예 a 파일 제거를위한 플랫폼 간 접근법 (예시) :

#ifdef _WIN32
#include <windows.h> // and other windows system files
#endif
#include <cstdio>

bool remove_file(const std::string &path) 
{
#ifdef _WIN32
  return DeleteFile(path.c_str());
#elif defined(_POSIX_VERSION) || defined(__unix__)
  return (0 == remove(path.c_str()));
#elif defined(__APPLE__)
  //TODO: check if NSAPI has a more specific function with permission dialog
  return (0 == remove(path.c_str()));
#else 
#error "This platform is not supported"
#endif
}

_WIN32 , __APPLE__ 또는 __unix__ 과 같은 매크로는 일반적으로 해당 구현에 의해 미리 정의됩니다.

예제 b : 디버그 빌드에 대한 추가 로깅 사용 :

void s_PrintAppStateOnUserPrompt()
{
    std::cout << "--------BEGIN-DUMP---------------\n"
              << AppState::Instance()->Settings().ToString() << "\n"
#if ( 1 == TESTING_MODE ) //privacy: we want user details only when testing
              << ListToString(AppState::UndoStack()->GetActionNames())
              << AppState::Instance()->CrntDocument().Name() 
              << AppState::Instance()->CrntDocument().SignatureSHA() << "\n"
#endif
              << "--------END-DUMP---------------\n"
}

예 c : 별도의 제품 빌드에서 프리미엄 기능 사용 (참고 : 이는 설명을위한 것입니다. 애플리케이션을 다시 설치하지 않아도 기능을 잠금 해제하는 것이 더 좋은 방법입니다)

void MainWindow::OnProcessButtonClick()
{
#ifndef _PREMIUM
    CreatePurchaseDialog("Buy App Premium", "This feature is available for our App Premium users. Click the Buy button to purchase the Premium version at our website");
    return;
#endif
    //...actual feature logic here
}

몇 가지 일반적인 트릭 :

호출 시간에 기호 정의 :

사전 처리기는 미리 정의 된 기호 (선택적 초기화 포함)로 호출 할 수 있습니다. 예를 gcc -E 명령 ( gcc -E 는 전 처리기 만 실행 함)

gcc -E -DOPTIMISE_FOR_OS_X -DTESTING_MODE=1 Sample.cpp

#define OPTIMISE_FOR_OS_X#define TESTING_MODE 1 이 Sample.cpp의 맨 위에 추가되는 것과 같은 방식으로 Sample.cpp를 처리합니다.

매크로가 정의되었는지 확인하려면 다음을 수행하십시오.

매크로가 정의되지 않고 해당 값이 비교되거나 검사되면 전 처리기는 거의 항상 값을 0 가정합니다. 이 작업에는 몇 가지 방법이 있습니다. 한 가지 방법은 기본 설정이 0으로 표시되고 변경 사항 (예 : 앱 빌드 프로필)을 명시 적으로 수행해야한다고 가정하는 것입니다 (예 : ENABLE_EXTRA_DEBUGGING = 0, 기본적으로 -DENABLE_EXTRA_DEBUGGING = 1로 설정). 또 다른 접근법은 모든 정의와 기본값을 명시 적으로 만드는 것입니다. 이것은 #ifndef#error 지시어를 조합하여 사용할 수 있습니다.

#ifndef (ENABLE_EXTRA_DEBUGGING)
// please include DefaultDefines.h if not already included.
#    error "ENABLE_EXTRA_DEBUGGING is not defined"
#else
#    if ( 1 == ENABLE_EXTRA_DEBUGGING )
  //code
#    endif
#endif

매크로

매크로는 객체와 같은 매크로와 함수와 유사한 매크로의 두 가지 주요 그룹으로 분류됩니다. 매크로는 컴파일 프로세스 초기에 토큰 대체로 처리됩니다. 즉, 큰 (또는 반복적 인) 코드 섹션을 전 처리기 매크로로 추상화 할 수 있습니다.

// This is an object-like macro
#define    PI         3.14159265358979

// This is a function-like macro.
// Note that we can use previously defined macros
// in other macro definitions (object-like or function-like)
// But watch out, its quite useful if you know what you're doing, but the
// Compiler doesnt know which type to handle, so using inline functions instead
// is quite recommended (But e.g. for Minimum/Maximum functions it is quite useful)
#define    AREA(r)    (PI*(r)*(r))

// They can be used like this:
double pi_macro   = PI;
double area_macro = AREA(4.6);

Qt 라이브러리는 사용자가 QObject를 확장하는 사용자 정의 클래스의 헤드에 Q_OBJECT 매크로를 선언하도록함으로써이 기술을 사용하여 메타 오브젝트 시스템을 작성합니다.

매크로 이름은 일반적으로 모든 대문자로 작성되므로 일반 코드와 쉽게 구분할 수 있습니다. 이것은 요구 사항은 아니지만 단순히 많은 프로그래머가 좋은 스타일로 생각하는 것입니다.


객체와 같은 매크로가 발생하면 간단한 복사 - 붙여 넣기 작업으로 확장되며 매크로의 이름은 정의로 바뀝니다. 함수와 같은 매크로가 발견되면 이름과 매개 변수가 모두 확장됩니다.

double pi_squared = PI * PI;
// Compiler sees:
double pi_squared = 3.14159265358979 * 3.14159265358979;

double area = AREA(5);
// Compiler sees:
double area = (3.14159265358979*(5)*(5))

이 때문에 함수와 유사한 매크로 매개 변수는 위의 AREA() 같이 괄호 안에 묶인 경우가 많습니다. 이는 매크로 확장 중에 발생할 수있는 버그, 특히 여러 개의 실제 값으로 구성된 단일 매크로 매개 변수로 인해 발생하는 버그를 방지하기위한 것입니다.

#define BAD_AREA(r) PI * r * r

double bad_area = BAD_AREA(5 + 1.6);
// Compiler sees:
double bad_area = 3.14159265358979 * 5 + 1.6 * 5 + 1.6;

double good_area = AREA(5 + 1.6);
// Compiler sees:
double good_area = (3.14159265358979*(5 + 1.6)*(5 + 1.6));

또한이 간단한 확장으로 인해 예기치 않은 부작용을 방지하기 위해 매크로에 전달 된 매개 변수에주의를 기울여야합니다. 평가 중에 매개 변수가 수정되면 확장 매크로에서 사용될 때마다 매개 변수가 수정됩니다. 일반적으로 이는 원하는 것이 아닙니다. 확장이 훼손되지 않도록 매크로가 괄호 안에 매개 변수를 묶은 경우에도 마찬가지입니다.

int oops = 5;
double incremental_damage = AREA(oops++);
// Compiler sees:
double incremental_damage = (3.14159265358979*(oops++)*(oops++));

또한 매크로는 유형 안전성을 제공하지 않으므로 형식 불일치에 대한 이해하기 어려운 오류가 발생합니다.


프로그래머가 일반적으로 세미콜론으로 줄을 끝내기 때문에 독립 실행 형 줄로 사용되는 매크로는 세미콜론을 "삼키기"위해 디자인됩니다. 이렇게하면 의도하지 않은 버그가 추가 세미콜론으로 인해 발생하지 않습니다.

#define IF_BREAKER(Func) Func();

if (some_condition)
    // Oops.
    IF_BREAKER(some_func);
else
    std::cout << "I am accidentally an orphan." << std::endl;

이 예에서는 실수로 두 번 세미콜론이 if...else 블록을 중단하여 컴파일러가 elseif 와 일치시키지 못하게합니다. 이를 막기 위해 세미콜론은 매크로 정의에서 생략되어 세미콜론을 사용하는 즉시 세미콜론을 "삼키는"원인이됩니다.

#define IF_FIXER(Func) Func()

if (some_condition)
    IF_FIXER(some_func);
else
    std::cout << "Hooray!  I work again!" << std::endl;

말미의 세미콜론을 없애면 현재의 문장을 끝내지 않고 매크로를 사용할 수 있으므로 유익 할 수 있습니다.

#define DO_SOMETHING(Func, Param) Func(Param, 2)

// ...

some_function(DO_SOMETHING(some_func, 3), DO_SOMETHING(some_func, 42));

일반적으로 매크로 정의는 줄 끝에서 끝납니다. 그러나 매크로가 여러 행을 처리해야하는 경우 줄 끝 부분에 백 슬래시를 사용하여이를 나타낼 수 있습니다. 이 백 슬래쉬는 행의 마지막 문자 여야하며, 다음 행을 현재 행으로 병합하여 단일 행으로 처리해야한다는 것을 전 처리기에 표시합니다. 이것은 한 번에 여러 번 사용할 수 있습니다.

#define TEXT "I \
am \
many \
lines."

// ...

std::cout << TEXT << std::endl; // Output:   I am many lines.

이것은 복잡한 함수와 같은 매크로에서 특히 유용합니다. 여러 매크로를 처리해야 할 수도 있습니다.

#define CREATE_OUTPUT_AND_DELETE(Str) \
    std::string* tmp = new std::string(Str); \
    std::cout << *tmp << std::endl; \
    delete tmp;

// ...

CREATE_OUTPUT_AND_DELETE("There's no real need for this to use 'new'.")

좀 더 복잡한 함수와 같은 매크로의 경우 실제 함수와 마찬가지로 이름 충돌 가능성을 방지하거나 매크로가 끝날 때 객체가 파괴되도록하는 자체 범위를 제공하는 것이 유용 할 수 있습니다. 이것에 대한 공통적 인 관용구는 매크로가 do-while 블록으로 둘러싸인 0동안 이다. 이 블록은 일반적으로 세미콜론을 사용 하지 않으므로 세미콜론을 사용 하지 않습니다 .

#define DO_STUFF(Type, Param, ReturnVar) do { \
    Type temp(some_setup_values); \
    ReturnVar = temp.process(Param); \
} while (0)

int x;
DO_STUFF(MyClass, 41153.7, x);

// Compiler sees:

int x;
do {
    MyClass temp(some_setup_values);
    x = temp.process(41153.7);
} while (0);

가변 매크로도 있습니다. variadic 함수와 마찬가지로 다양한 인수를 취한 다음 특수 "Varargs"매개 변수 인 __VA_ARGS__ 대신 해당 인수를 모두 확장합니다.

#define VARIADIC(Param, ...) Param(__VA_ARGS__)

VARIADIC(printf, "%d", 8);
// Compiler sees:
printf("%d", 8);

확장하는 동안 __VA_ARGS__ 는 정의의 어느 위치 에나 배치 될 수 있으며 올바르게 확장됩니다.

#define VARIADIC2(POne, PTwo, PThree, ...) POne(PThree, __VA_ARGS__, PTwo)

VARIADIC2(some_func, 3, 8, 6, 9);
// Compiler sees:
some_func(8, 6, 9, 3);

인수가 0 인 변수 매개 변수의 경우 다른 컴파일러가 후행 쉼표를 다르게 처리합니다. Visual Studio와 같은 일부 컴파일러는 특수 구문없이 자동으로 쉼표를 삼키게됩니다. GCC와 같은 다른 컴파일러에서는 __VA_ARGS__ 바로 앞에 ##__VA_ARGS__ 합니다. 이 때문에, 이식성이 문제가되면 가변 매크로를 조건부로 정의하는 것이 좋습니다.

// In this example, COMPILER is a user-defined macro specifying the compiler being used.

#if       COMPILER == "VS"
    #define VARIADIC3(Name, Param, ...) Name(Param, __VA_ARGS__)
#elif     COMPILER == "GCC"
    #define VARIADIC3(Name, Param, ...) Name(Param, ##__VA_ARGS__)
#endif /* COMPILER */

전 처리기 오류 메시지

전처리기를 사용하여 컴파일 오류를 생성 할 수 있습니다. 이 기능은 사용자가 지원되지 않는 플랫폼이나 지원되지 않는 컴파일러에 있는지 알려주는 등 여러 가지 이유로 유용합니다.

gcc 버전이 3.0.0 또는 이전 버전 인 경우 반환 오류.

#if __GNUC__ < 3
#error "This code requires gcc > 3.0.0"
#endif

Apple 컴퓨터에서 컴파일하는 경우 Return Error가 발생합니다.

#ifdef __APPLE__
#error "Apple products are not supported in this release"
#endif

사전 정의 된 매크로

미리 정의 된 매크로는 컴파일러에서 정의한 매크로입니다 (소스 파일에서 정의한 것과는 달리). 이러한 매크로는 사용자가 다시 정의하거나 정의하지 않아야합니다.

다음 매크로는 C ++ 표준에 의해 미리 정의됩니다.

  • __LINE__ 에는이 매크로가 사용 된 행의 행 번호가 들어 있으며 #line 지시어로 변경할 수 있습니다.
  • __FILE__ 에는이 매크로가 사용되는 파일의 파일 이름이 들어 있으며 #line 지시어로 변경할 수 있습니다.
  • __DATE__ 에는 파일 컴파일의 날짜 ( "Mmm dd yyyy" __DATE__ "Mmm dd yyyy" 형식)가 들어 있습니다. 여기서 mmstd::asctime() 호출로 얻은 것처럼 형식이 지정됩니다.
  • __TIME__ 은 파일 컴파일 시간 ( "hh:mm:ss" 형식)을 포함합니다.
  • __cplusplus 는 C ++ 파일을 컴파일하는 동안 (준수하는) C ++ 컴파일러에 의해 정의됩니다. 이 값은 컴파일러 즉, 완벽하게 준수되는 표준 버전입니다 199711L C ++ 98과 C ++ 03에 대한 201103L C ++ 11에 대한 201402L C ++ 14 표준에 대한.
c ++ 11
  • __STDC_HOSTED__ 는 구현이 호스팅되는 경우 1 , 독립 실행 __STDC_HOSTED__ 경우 0 으로 정의됩니다.
c ++ 17
  • __STDCPP_DEFAULT_NEW_ALIGNMENT__ 에는 align-unaware operator new 에 대한 호출에 사용되는 정렬 인 size_t 리터럴이 들어 있습니다.

또한 다음 매크로는 구현에 의해 사전 정의 될 수 있으며 존재하지 않을 수도 있습니다.

  • __STDC__ 는 구현에 종속 된 의미를 가지며 일반적으로 파일을 C로 컴파일 할 때만 정의되어 전체 C 표준 준수를 나타냅니다. (아니면 컴파일러가이 매크로를 지원하지 않기로 결정한 경우).
c ++ 11
  • __STDC_VERSION__ 은 구현에 따라 의미가 있으며 그 값은 일반적으로 C 버전이며 __cplusplus 는 C ++ 버전과 유사합니다. (또는 컴파일러가이 매크로를 지원하지 않기로 결정한 경우에도 정의되지 않습니다.)
  • 기본 문자 집합의 좁은 인코딩 값이 와이드 카운터 파트의 값과 같지 않을 경우 __STDC_MB_MIGHT_NEQ_WC__1 로 정의됩니다 (예 : if (uintmax_t)'x' != (uintmax_t)L'x' ))
  • __STDC_ISO_10646__wchar_t 가 유니 코드로 인코딩되어 있고 지원되는 최신 유니 코드 개정을 나타내는 yyyymmL 형식의 정수로 확장됩니다.
  • 구현시 엄격한 포인터 안전성 이있는 경우 __STDCPP_STRICT_POINTER_SAFETY__1 로 정의됩니다 (그렇지 않으면 포인터 안전성완화됩니다 )
  • __STDCPP_THREADS__ 는 프로그램이 하나 이상의 실행 스레드를 가질 수있는 경우 1 로 정의됩니다 ( 독립 실행 형 구현에 적용 - 호스트 된 구현 은 항상 둘 이상의 스레드를 가질 수 있음)

__func__ 은 매크로가 아니라 미리 정의 된 함수 - 로컬 변수입니다. 그것은 사용되는 함수의 이름을 구현 정의 형식의 정적 문자 배열로 포함합니다.

표준 사전 정의 된 매크로 외에 컴파일러는 사전 정의 된 매크로 세트를 가질 수 있습니다. 컴파일러 문서를 참조해야합니다. 예 :

일부 매크로는 일부 기능에 대한 지원을 쿼리하기위한 것입니다.

#ifdef __cplusplus // if compiled by C++ compiler
extern "C"{ // C code has to be decorated
   // C library header declarations here
}
#endif

다른 것들은 디버깅에 매우 유용합니다 :

c ++ 11
bool success = doSomething( /*some arguments*/ );
if( !success ){
    std::cerr << "ERROR: doSomething() failed on line " << __LINE__ - 2
              << " in function " << __func__ << "()"
              << " in file " << __FILE__
              << std::endl;
}

그리고 평범한 버전 관리를위한 다른 것들 :

int main( int argc, char *argv[] ){
    if( argc == 2 && std::string( argv[1] ) == "-v" ){
        std::cout << "Hello World program\n"
                  << "v 1.1\n" // I have to remember to update this manually
                  << "compiled: " << __DATE__ << ' ' << __TIME__ // this updates automagically
                  << std::endl;
    }
    else{
        std::cout << "Hello World!\n";
    }
}

X- 매크로

컴파일 타임에 반복 코드 구조를 생성하는 관용 기술.

X- 매크로는 목록과 목록 실행의 두 부분으로 구성됩니다.

예:

#define LIST \
    X(dog)   \
    X(cat)   \
    X(racoon)

// class Animal {
//  public:
//    void say();
// };

#define X(name) Animal name;
LIST
#undef X

int main() {
#define X(name) name.say();
    LIST
#undef X

    return 0;
}

전처리기에 의해 다음과 같이 확장됩니다.

Animal dog;
Animal cat;
Animal racoon;

int main() {
    dog.say();
    cat.say();
    racoon.say();

    return 0;
}    

목록이 커지면서 (100 개 이상의 요소),이 기술은 과도한 복사 붙여 넣기를 피하는 데 도움이됩니다.

출처 : https://en.wikipedia.org/wiki/X_Macro

또한 : X- 매크로


LIST 를 사용하기 전에 seamingly 무관 한 X 를 정의하는 것이 바람직하지 않은 경우 매크로 이름을 인수로 전달할 수도 있습니다.

#define LIST(MACRO) \
    MACRO(dog) \
    MACRO(cat) \
    MACRO(racoon)

이제 목록을 확장 할 때 매크로를 사용해야하는지 명시 적으로 지정합니다 (예 :

#define FORWARD_DECLARE_ANIMAL(name) Animal name;
LIST(FORWARD_DECLARE_ANIMAL)

MACRO 호출 할 때마다 추가 매개 변수 (목록과 관련하여 상수)를 사용해야하는 경우 가변 매크로를 사용할 수 있습니다

//a walkaround for Visual studio
#define EXPAND(x) x

#define LIST(MACRO, ...) \
    EXPAND(MACRO(dog, __VA_ARGS__)) \
    EXPAND(MACRO(cat, __VA_ARGS__)) \
    EXPAND(MACRO(racoon, __VA_ARGS__))

첫 x 째 인수는 LIST 에 의해 제공되는 반면, 나머지는 LIST 호출에서 사용자에 의해 제공됩니다. 예 :

#define FORWARD_DECLARE(name, type, prefix) type prefix##name;
LIST(FORWARD_DECLARE,Animal,anim_)
LIST(FORWARD_DECLARE,Object,obj_)

확장 할 것이다

Animal anim_dog;
Animal anim_cat;
Animal anim_racoon;
Object obj_dog;
Object obj_cat;
Object obj_racoon;        

#pragma once

전부는 아니지만 대부분의 C ++ 구현 은 파일이 단일 컴파일 내에 한 번만 포함되도록하는 #pragma once 지시문을 지원합니다. ISO C ++ 표준의 일부가 아닙니다. 예 :

// Foo.h
#pragma once

class Foo
{
};

동안 #pragma once 방지와 관련된 몇 가지 문제가 경비 포함 하는 #pragma 정의에 의해 표준 인은 - - 본질적으로 특정 컴파일러 후크이며, 자동으로이를 지원하지 않는 컴파일러에 의해 무시됩니다. #pragma once 사용하는 프로젝트는 표준을 준수하도록 수정해야합니다.

일부 컴파일러 - 특히 미리 컴파일 된 헤더 를 사용하는 컴파일러 - #pragma once 는 컴파일 프로세스의 속도를 상당히 높일 수 있습니다. 마찬가지로 일부 전처리 기는 가드를 포함하는 헤더를 추적하여 컴파일 속도를 높입니다. #pragma once 포함하고 경비원을 포함시키는 순 혜택은 구현에 따라 다르며 컴파일 시간이 증가하거나 감소 할 수 있습니다.

#pragma once 과 함께 경비를 포함 헤더 파일에 권장되는 레이아웃 창에 MFC 기반 응용 프로그램을 작성 및 Visual Studio의에 의해 생성 된이었다 add class , add dialog , add windows 마법사가. 따라서 C ++ Windows 응용 프로그램에서 이들을 결합하는 것이 매우 일반적입니다.

전 처리기 연산자

# 연산자 또는 stringizing 연산자는 매크로 매개 변수를 문자열 리터럴로 변환하는 데 사용됩니다. 인수가있는 매크로에만 사용할 수 있습니다.

// preprocessor will convert the parameter x to the string literal x
#define PRINT(x) printf(#x "\n")

PRINT(This line will be converted to string by preprocessor);
// Compiler sees
printf("This line will be converted to string by preprocessor""\n");

컴파일러는 두 문자열을 연결하고 마지막 printf() 인수는 줄 바꿈 문자가 끝에 오는 문자열 리터럴이됩니다.

전처리 기는 매크로 인수 앞뒤에 공백을 무시합니다. 따라서 print 명령문 아래에서 같은 결과를 얻을 수 있습니다.

PRINT(   This line will be converted to string by preprocessor );

문자열 리터럴의 매개 변수에 double quote ()와 같은 이스케이프 시퀀스가 ​​필요한 경우 전 처리기에서 자동으로 삽입합니다.

PRINT(This "line" will be converted to "string" by preprocessor); 
// Compiler sees
printf("This \"line\" will be converted to \"string\" by preprocessor""\n");

## 연산자 또는 토큰 붙여 넣기 연산자는 매크로의 두 매개 변수 또는 토큰을 연결하는 데 사용됩니다.

// preprocessor will combine the variable and the x
#define PRINT(x) printf("variable" #x " = %d", variable##x)

int variableY = 15;
PRINT(Y);
//compiler sees
printf("variable""Y"" = %d", variableY);

최종 출력은

variableY = 15


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow