C++/문법 및 메소드(STL)

[C++][Build] 전처리 단계 - #pragma 지시문

pledge24 2025. 4. 14. 16:59

썸네일: https://www.youtube.com/watch?v=3jsqavQ5faU

#pragma 지시문

  • #pragma 지시문은 C/C++ 표준 키워드가 아닌 컴파일러에 종속된 키워드들로, 컴파일러의 종류에 따라 기능이나 문법이 다르다.
  • #pragma는 #include나 #define처럼 전처리기에 의해 처리되지만, 전처리기는 이를 단순히 해석하여 컴파일러에게 전달하고, 실제 동작은 컴파일러가 수행한다.
  • 이러한 #pragma를 활용하면 코드의 경고 제어, 최적화 설정, 메모리 정렬, 헤더 중복 방지 등 다양한 작업을 수행할 수 있다.

대표적인 #pragma 지시문들

  • #pragma once
  • #pragma pack
  • #pragma region
  • #pragma comment

#pragma once

#pragma once
  • #pragma once는 헤더 파일이 동일한 번역 단위(translation unit, TU) 내에서 여러 번 포함되는 경우 한 번만 포함되도록 하는 지시문이다.
    • 즉, #pragma once는 #ifndef가 했던 “헤더 가드”의 역할을 한다. (헤더 가드에 대한 설명은 여기를 참고)
  • #pragma once는 올바른 헤더 가드의 역할을 하기 위해 헤더 파일 최상단에 위치해야 한다.
// header.h
#pragma once
// Code placed here is included only once per translation unit

 

 

#ifndef와 #prgama once의 헤더 가드 비교

  • 아래 코드처럼 #prgama once의 헤더 가드는 사용하기 간편하기 때문에 헤더 가드로써의 활용 빈도가 #ifndef보다 더 높다.
// =====#ifndef 방식의 헤더가드=====
#ifndef A_H // A_H는 a.h 헤더 파일을 의미
#define A_H

class A {};

#endif

// =====#pragma once 방식의 헤더가드=====
#pragma once

class A {};

 


#pragam pack()

#pragma pack(N)
#pragma pack(push, N)
#pragma pack(pop, N)
  • #pragma pack()은 구조체 멤버의 메모리 정렬(alignment) 방식을 조절할 때 사용한다.
  • #pragma pack()은 인자로 무엇을 넣느냐에 따라 두 가지 방식으로 사용할 수 있다. 첫 번째 방법으론 #pragma pack(N)이 있고, 두 번째 방법으론 #pragma pack(push/pop)이 있다.

메모리 정렬(Memory Alignment)이란?

  • 메모리 정렬은 CPU가 메모리에 더 빠르게 접근하기 위해 일정한 바이트 단위로 데이터를 정렬하는 것을 의미한다.
  • 메모리 정렬을 할 경우 데이터들은 정렬 경계(alignment boundary)에 맞추어 저장된다. 이때 메모리 정렬에 의해 데이터가 맞춰져 저장된 모습을 “데이터가 경계에 놓여있다”라고 표현한다.
    • 예를 들어, 데이터가 4의 배수에 맞추어 정렬되었다면, 이를 “더블 워드 경계에 놓여있다”라고 한다. (더블 워드는 4바이트를 의미한다.)
    • 컴퓨터의 메모리 주소 체계(32bits, 64bits)에 맞게 정렬해야 메모리에 접근이 빠르기 때문에,  메모리 정렬의 기본값은 4바이트(32bits) 또는 8바이트(64bits)이다.
  • 연속된 데이터를 저장해야 하는 경우, CPU는 메모리 정렬을 유지하기 위해 데이터 사이에 빈 메모리 공간을 추가한다. 이 빈 공간을 "패딩(padding)"이라고 부르며, 같은 데이터라도 배치에 따라 패딩의 크기가 달라진다.

패딩(padding) 예제

// 현재 사용하는 메모리 주소 체계는 64bits(8Byte)

struct S1
{
    long long a = 0x24; // 8바이트
    char c = 'a'; // 1바이트
    int x = 0x11; // 4바이트
};

=> S1 구조체 크기: 16바이트, 패딩: 3바이트
[24 00 00 00 00 00 00 00] // long long a(8Byte)
[61 cc cc cc 11 00 00 00] // char c(1Byte) + "padding"(3Byte) + int x(4Byte)

struct S2
{
    char c = 'a'; // 1바이트
    long long a = 0x24; // 8바이트
    int x = 0x11; // 4바이트
}; 

=> S2 구조체 크기: 24바이트, 패딩: 11바이트
[61 cc cc cc cc cc cc cc] // char c(1Byte) + "padding"(7Byte)
[24 00 00 00 00 00 00 00] // long long a(8Byte)
[11 00 00 00 cc cc cc cc] // int x(4Byte) + "padding"(4Byte)

 

 

#pragma pack()의 첫 번째 사용 방법: #pragma pack(N)

  • 첫 번째 사용 방법인 #pragma pack(N)은 N바이트 단위로 메모리를 정렬하라는 뜻으로, 기존에 4바이트 또는 8바이트로 정렬하던 시스템을 무시하고 N바이트로 정렬하도록 지정한다. (유효한 N은 1, 2, 4, 8, 16이다.)
  • 이 방식을 사용할 때 한 가지 주의할 점이 있는데, 한 번 적용하면 계속 유지되기 때문에 이후 모든 구조체에 적용된다는 것이다. 따라서, 특정 구조체에만 적용하고 싶다면 적용 후 다시 원상태(#pragma pack(8))로 되돌려놓아야 한다.
#pragma pack(1)  // 이후의 구조체들은 1바이트 정렬됨

struct A {
    char a;
    int b;  // 평소엔 8바이트 경계에 맞추지만 지금은 1바이트 기준
};

// #pragma pack(8)  // 이후 구조체들에는 기존 정렬 방식을 사용하고 싶을때 활성화

 

 

#pragma pack()의 두 번째 사용 방법: #pragma pack(push/pop)

  • 두 번째 사용 방법인 #pragma pack(push/pop)은 정렬 스택을 사용하는 방법으로, 정렬 스택 top에 저장된 정렬 방식으로 메모리 정렬을 설정한다.
  • 이 방식은 일시적으로 정렬 방식 바꾸고 원상 복구하고 싶을 때 유용하다.
  • 사용 방식은 다음과 같다.
    • #pragma pack(push, N) : N바이트 메모리 정렬로 설정하고, N값을 스택에 push 한다.
      • N을 지정하지 않고 비워두면, 현재 정렬 값을 push 한다.
    • #pragma pop(pop) : 스택에서 한 번 pop 하고, pop 한 값으로 메모리 정렬을 설정한다.
    • #pragma pop(pop, N) : 스택에서 한 번 pop 하고, N값으로 메모리 정렬을 설정한다. (이 기능은 Visual Studio와 같은 MSVC 전용 기능이다.)
#pragma pack(push, 1)  // 현재 정렬 설정 저장(push)하고 1바이트 정렬로 설정

struct A {
    char a;
    int b;
};

#pragma pack(pop)  // 정렬 설정을 이전 값으로 복원

 

 


#pragma region

#pragma region 지역_이름
#pragma endregion [주석]
  • #pragma region은 MSVC 확장 기능으로, 추가적인 기능은 없고 그저 코드 블록을 접거나 펼 수 있도록 하는 기능이다.
  • #pragma region “이름”으로 코드 블록의 시작을 표시하고, #pragma endregion으로 코드 블록의 끝을 표시한다.
    • #pragma endregion 뒤에 주석을 적으면 접었을 때 주석이 표시된다.
#pragma region X
void func1() { /*...*/ } 
void func2() { /*...*/ }
void func3() { /*...*/ }
#pragma endregion X 지역을 설명하는 주석
  • 아래 사진과 같이 #pragma region으로 지역을 설정하면 왼쪽에 접는 화살표가 생기며, 접었을 때 적어놓은 주석이 표시되는 걸 볼 수 있다.

접기 전(왼쪽), 접은 후(오른쪽)

 

 


#pragma comment()

#pragma comment( comment_유형 [ , "comment-문자열" ])
  • #pragma comment는 MSVC 확장 기능으로, 링커 옵션을 소스 코드에 직접 명시할 때 사용한다.
  • comment_유형에는 compiler, lib, linker 등의 미리 정의된 식별자가 들어가며, comment-문자열에는 "/include:__mySymbol”와 같이 링커 옵션이 포함된 문자열이나 "user32.lib”와 같이 라이브러리 문자열이 들어간다.

 

주요 용도: 라이브러리 자동 연결

  • #pragma comment를 사용하는 주된 이유는 라이브러리를 자동으로 연결하기 위해서다.
  • #pragma comment의 comment_유형을 lib로 설정한 다음, 추가할 라이브러리 이름을 comment-문자열 위치에 적어주면, Visual Studio 프로젝트 설정에서 별도의 추가 없이 해당 라이브러리가 자동으로 링킹 된다.
  • 주로 헤더 파일에 적어서 사용한다.

라이브러리 연결 예제: Window Socket

// someHeaderFile.h
#pragma comment(lib, "ws2_32.lib") // 윈도우 소켓 라이브러리 자동 연결

 

 


이외에도 많은 #pragma 지시문들이 있다.

  • #pragma에서 제공하는 지시문들은 굉장히 많다. 다른 지시문에 대한 자세한 내용은 아래 링크를 확인하면 된다.

https://learn.microsoft.com/ko-kr/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170

 

Pragma 지시문 및 __pragma 및 _Pragma 키워드

pragma MsVC(Microsoft Visual C 및 C++)에서 사용할 수 있는 지시문에 대해 설명합니다.

learn.microsoft.com

 

 


참고 자료

https://modoocode.com/103

https://learn.microsoft.com/ko-kr/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170