ISO C (표준 C)에서는 C90(ISO/IEC 9899:1990) 까지는 #pragma 의 표준 구문이 존재하지 않았다. 즉, 순전히 기능을 컴파일러에 맡겼다. 그런데 C99(ISO/IEC 9899:1999) 에서 #pragma에 표준 구문이 추가 되었다. 그 표준 구문은 #pragma STDC .... 라고 입력하면 되는데. 이 글에서 이를 다룰 것이 아니므로 여기까지만 언급하고 넘어가겠다.
#pragma 구문의 가장 대표적인 예는 바로 이것이다.
#pragma once
결론적으로 말하면 일반적으로 header 파일(.h) 의 맨 위에 저 구문을 집어 넣으며, 중복 include 를 방지하는 데에 사용한다. 아래의 예를 보자.
[info.h]
1 2 3 4 5 6 7 8 | #pragma once typedef struct _information { int age; double height; } INFO; INFO info = { 80, 158.5 }; |
[isold.h]
1 2 3 4 5 6 7 | #include "info.h" int is_old( int age ) { if ( info.age >= age ) return 1; return 0; } |
[istall.h]
1 2 3 4 5 6 7 | #include "info.h" int is_tall( double height ) { if ( info.height >= height ) return 1; return 0; } |
[main.c]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include "isold.h" #include "istall.h" #include <stdio.h> int main() { int old = is_old(35); int tall = is_tall(193.2); if (old) printf ( "old " ); if (tall) printf ( "tall " ); putchar ( '\n' ); return 0; } |
위의 main.c 를 build 한다고 하자. main.c 는 stdio.h 외에 두 개의 파일을 include 하고 있는데, 그 두 개의 파일은 모두 info.h 를 include 하고 있다. 즉 main.c의 입장에서 보면 info.h가 두 번 include 되므로 struct _information 의 타입 재정의와 struct 의 전역 변수가 중복정의된다. 이럴 경우 info.h 의 맨 위에 #pragma once 를 넣으면 한 번만 include 되게 해 준다는 것인데....
애석하게도 #pragma once 는 모든 표준 C compiler 에서 사용 가능한 것이 아니다. 특히 대부분의 UNIX 와 Linux 에서는 지원하지 않는다. 따라서 프로그램의 이식성을 위해 "아직까지는" #pragma once 대신 아래와 같이 전통적인 방법(#ifndef, #define, #endif)으로 info.h 를 작성하는 것이 좋다.
[info.h 수정]
1 2 3 4 5 6 7 8 9 10 11 | #ifndef _INFO_H_ #define _INFO_H_ typedef struct _information { int age; double height; } INFO; INFO info = { 80, 158.5 }; #endif |
마지막으로 위에서 본인이 "아직까지는" 이라는 곳에 포인트를 둔 이유를 이야기하고자 한다. 비록 #pragma once 가 C 표준은 아니지만 현재 여러 표준 C 컴파일러에서 #pragma once 의 기능 채택 비율이 낮지 않다는 것이다. 최근에 Linux GCC 컴파일러는 #pragma once 의 기능을 한 때 없애버렸다가 다시 채택하였다(버전 3.4). 이런 추세라면 #pragma once 도 결국엔 범용적으로 쓰이지 않을까 조심스럽게 예상은 하지만.... 아직은 전통적인 방법을 추천하고 싶다.
참고 : http://en.wikipedia.org/wiki/Pragma_once
다음............
또 하나의 많이 사용되는 구문으로 #pragma pack 이라는 것이 있다. 일단 이 것이 왜 나왔는지를 살짝 이야기하겠다.
32비트 OS를 예를 들어 설명하면, CPU는 메모리를 4바이트씩 끊어서 읽는다. 따라서 메모리에서 변수 access를 빠르게 하기 위하여 중간에 1, 2바이트 씩을 비우고 할당하는 경우가 생긴다. 다음을 보자.
1 2 3 4 | struct A { char c; int i; }; |
위의 경우 sizeof( struct A ) 는 5가 예상되지만 실제로는 8 (혹은 OS의 종류에 따라서 그 이상 또는 그 이하)이 된다 . 이런 경우는 위의 struct 구문을 그대로 네트워크에 실어보낸다든지 했을 때에 변수 디코딩에 약간의 문제가 발생할 수 있다.
메모리 access 에 관계 없이 sizeof( struct A ) 가 5가 나오도록 하려고 사용하는 구문이 #pragma pack 이다. 보통 구조체와 공용체(, 그리고 C++의 경우 클래스) 사이즈의 조정이 필요한 구문의 위와 아래에 사용을 한다. 하지만 이 또한 표준이 아니므로 컴파일러 마다 #pragma pack 사용법이 다르다.
Visual C 컴파일러는 다음과 같이 사용한다.
// 현재의 pack 사이즈를 저장하지 않고 사이즈를 2로 설정
#pragma pack(2)
// 현재의 pack 사이즈를 저장하고 pack 사이즈를 1로 설정
#pragma pack(push, 1)
. . . . .
// 저장한 pack 사이즈로 변경
#pragma pack(pop)
Linux GCC 컴파일러는 다음과 같이 사용한다.
// 현재의 pack 사이즈를 1로 설정
#pragma pack(1)
. . . . .
// 최초의 pack 사이즈로 변경
#pragma pack()
단 최근의 GCC 컴파일러는 (4.0 이상) Visual C 컴파일러의 스타일도 호환 가능하다고 언급이 되어 있다.
결론은....
C 프로그래밍을 할 때 예전처럼 OS dependent 한 코드만을 작성한다면 모르겠지만, 오늘날처럼 이식성이 중요한 코드를 작성하는 경우에는 C 표준, 그리고 각 컴파일러의 지원 여부 등을 고려하여 좀 더 범용성 있는 코드를 작성하려는 노력이 필요하다.
### Wafting ....... Done !!!
### ...
### ;;
댓글 없음:
댓글 쓰기