1. 타입
1.1. 정수의 내부
- 정수형 타입을 선택할 때 표현하고자 하는 값의 최대값을 잘 고려하여 적당한 길이의 타입을 선택해야 한다.
- 이러한 크기를 고려하지 않고 연산을 시도했다가 메모리 오버플로우가 발생할 수 있다
1.2. 음수의 표현
- 비트의 세계에서 음수를 표현할 수 있는 방법은 여러 가지가 있을 수 있다.
- 일련의 비트를 어떤 수로 해석할 것인가는 일종의 약속이기 때문에 처음 정한 약속대로 부호를 표현하기만 하면 된다.
| 이진수 | 부호 없음 | 일정 수 감소 | 부호 비트와 절대값 | 1의 보수 | 2의 보수 |
|---|
| 000 | 0 | -4 | 0 | 0 | 0 |
| 001 | 1 | -3 | 1 | 1 | 1 |
| 010 | 2 | -2 | 2 | 2 | 2 |
| 011 | 3 | -1 | 3 | 3 | 3 |
| 100 | 4 | 0 | -0 | -3 | -4 |
| 101 | 5 | 1 | -1 | -2 | -3 |
| 110 | 6 | 2 | -2 | -1 | -2 |
| 111 | 7 | 3 | -3 | -0 | -1 |
- 현대의 컴퓨터들 모두 2의 보수법으로 음수를 표현한다.
- 보수(Complement)란 어떤 수(기수)가 되기 위해 보충되어야 하는 수를 의미한다.
기수 10에 대한 3의 보수는 7이다.
2의 보수란 n비트에 대해 2의 n승을 기수로 한 보수다.
1.3. 바이트 순서
- 메모리의 저장 단위는 8비트로 구성된 바이트인데 비해 실제 저장해야할 값은 32비트나 64비트로 바이트 길이보다 훨씬 더 크다.
- 그래서 여러 개의 연속적인 바이트에 이 값들을나누어 저장해야 하는데 어떻게 나누는가에 따라 두 가지 방식이 있다.
- 예를 들어 정수 0x12345678이라는 값을 저장한다고 한다면 다음과 같이 저장할 수 있을 것이다.
- 빅엔디안 [ 12 | 34 | 56 | 78 ]
- 리틀엔디안 [ 78 | 56 | 34 | 12 ]
- 빅엔디안(순워드)
- 이 방식은 높은 자리수를 먼저 저장한다. 0x12가 가장 앞쪽 바이트에, 그리고 0x34가 그 다음 바이트에 저장되는 식이다. 모토롤라 계열의 CPU와 대부분의 RISC CPU가 이 방식을 사용한다. 사람들이 오랫동안 글을 읽을 때 왼쪽에서 오른쪽으로 읽어 왔으므로 순서에 맞게 4바이트를 나누어 저장한 것이다. 메모리에 나타난 순서대로 읽을 수 있고 자연스러우며 이해하기 쉽다.
- 리틀 엔디안(역워드)
- 이 방식은 낮은 자리수를 먼저 저장한다. 가장 뒤쪽 바이트인 0x78이 메모리의 가장 앞쪽 바이트에 저장되며 그 다음에 0x56이, 그리고 0x12는 가장 뒤쪽에 저장된다. 인텔 계열의 CPU와 DEC의 알파칩이 이 방식을 사용한다. 메모리 값을 읽을 때 거꾸로 읽어야 하므로 사람이 직접 읽기에는 다소 불편한 면이 있지만 기계가 값을 다루기는 더 효율적이고 몇 가지 연산에서 편리한 점이 있다.
- 따라서 어떠한 방식인지 모르고 메모리를 직접 조작할 경우 황당한 실수의 원인이 되기도 하므로 주의를 기울여야 한다.
- 디버깅 중에 변수가 저장된 메모리 영역을 직접 들여다본다거나 아니면 변수의 값을 바이트 단위로 직접 조립해야할 경우 주의해야 하며, 이 외에 바이트 저장 방식이 다른 이기종 컴퓨터간의 네트워크를 통신할 때, 구체적으로 팬티엄 PC와 메킨토시가 통신할 때 엔디안을 맞추어야하는 번거로움이 있다.
* 소켓은 기본적으로 빅 엔디안으로 통이되어 있으므로 인텔 계열 CPU는 보낼 때 뒤집어 보내고 받은 값도 뒤집어야 원래 값을 제대로 읽을 수 있다.
1.4. 부동 소수점
- 실수의 연산은 누적될 수록 오차를 유발할 수 있으므로 주의해야 한다.
1.5. 구조체의 정렬
- 구조체의 멤버들은 선언된 순서대로 인접한 번지에 배치된다.
- 그래서 구조체의 총 크기는 구조체의 총 크기는 구조체에 속한 멤버들의 총 크기와 같을 것 같지만 실제로는 다르다.
struct tag_st1{
char c;
double d;
};
struct tag_st1 st1={'A',1.234};
void main(){
printf("addr=%p, &c=%p, &d=%p, size=%ld\n",&st1,&st1.c,&st1.d,sizeof(st1));
}
- size가 9가 출력이 될줄 알았지만 16이 출력되었다.
- 이러한 이유는 물리적인 기계의 성능을 최대한 끌어올리기 위해서 컴파일러가 구조체를 메모리에 배치할 때 두 가지 사항을 고려하여 번지를 잡기 때문이다.
- 구조체가 시작될 번지를 고를 때 가급적이면 16바이트 경계에서 시작하도록 한다. 왜냐하면 최신 CPU들은 속도 증가를 위해 캐시를 사용하는데 캐시의 단위가 16바이트로 되어 있기 때문이다. 캐시 크기의 배수 위치에 구조체를 배치하면 이 구조체를 자주 액세스할 때 캐시 용량을 덜 차지하면서도 빠르게 액세스할 수 있다. 만약 16바이트 경계의 양쪽에 걸치게 되면 캐시도 많이 차지할 뿐더러 액세스 속도도 느려질 것이다.
- 구조체의 멤버를 배치할 때 멤버의 오프셋도 액세스하기 유리한 위치로 조정한다. 별다른 지정이 없으면 멤버의 크기에 따라 자연스러운 경계 위치에 맞추도록 되어 있는데 예를 들어 int 4바이트, double 8바이트 경계에 맞춘다. 위 예제의 경우 c가 1바이트를 차지하고 난 후 d는 다음 8바이트 경게에 배치되므로 c와 d 사이에 7바이트는 버려지고 사용되지 않는다. 이렇게 사용되지 않고 버려지는 공간을 패딩 이라고 한다.
- 컴파일러는 CPU가 메모리를 최대한 빠른 속도로 액세스할 수 있도록 구조체의 베이스와 멤버의 오프셋을 조정해서 배치하는데, 이를 구조체의 정렬(alignment)라고 한다.
- 만약 메모리를 꼭 절약하고 싶다면 gcc등의 명령행 컴파일러들은 구조체 정렬 관련 옵션을 모두 제공한다.
- 구조체 정렬 기능에 의해 멤버들이 구조체 내의 어느 오프셋에 배치될지는 미리 예측하기 어렵다.
- 만약 특정 멤버가 배치된 오프셋을 조사하고 싶다면 stddef.h에 다음과 같이 정의 되어 있는 offsetof 매크로 함수를 사용한다.
#define offsetof(TYPE, MEMBER) (size_t)&(((TYPE *)0)->MEMBER)
* 단순히 숫자 0을 주소값으로 캐스팅한다. 이때 캐스팅에 지정한 타입으로 캐스팅된다는 것은 0번지를 기준으로 멤버들을 가리킬 수 있는 템플릿(틀)을 씌우는 것과 같다.
2. 전처리기
2.1. #과 ##
#과 ##은 전처리기의 연산자로서 컴파일러가 #define전처리 과정에서만 사용하는 특수한 연산자이다.
- c언어 자체의 연산자는 아니므로 우선 순위나 결합 규칙 등은 적용되지 않는다.
- 둘 다 사용 빈도가 높지는 않지만 잘 알아 두면 매크로의 활용도를 높여 반복되는 코드를 간단하게 작성할 수 있어 작업 효율 향상을 꾀할 수 있다.
#연산자(stringizing operator)는 #define문의 인수 앞에 사용되며 피연산자를 문자열로 바꾸는 역할을 한다.
- 피연산자가 실인수로 치환된 후 양쪽에 따옴표를 붙여 치환된 결과 그대로 문자열 상수가 된다.
- 다음 예제에서 result 매크로는 인수로 전달된 수식을 printf 함수로 출력하되 수식 자체와 수식의 평가 결과를 같이 출력한다.
#define result(exp) printf(#exp"=%d\n",exp);
void main(){
result(5*3);
result(2*(3+1));
}
- 매크로로 전달된
5*3 수식이 #연산자에 의해 문자열로 치환되며 인접한 문자열은 합쳐지므로 5*3이라는 수식 자체가 printf의 서식 문자열 일부가 된다.
#연산자는 정확한 문자열 변환을 위해 몇 가지 규칙을 적용한다.
#과 형식 인수 사이의 공백, 형식 인수 다음의 공백은 무시되므로 #exp, # exp는 동일하다.
- 실인수 내의 공백은 하나만 인정되며 둘 이상의 공백은 하나만 남기고 모두 삭제된다.
- 실인수 내에 주석(
/* */)이 있으면 이 주석은 하나의 공백으로 대체된다.
- 실인수에 겹따옴표나 역슬레쉬 등 확장열(Escape character)로 처리해야 할 문자가 있다면 이 문자 앞에 확장열 선두 문자인
\가 자동으로 삽입된다.
#연산자를 사용하면 실인수를 문자열로 바꿔주므로 2진값을 바로 적어 2진수 형태의 상수를 표기하여 정수로 변환하는 매크로를 만들 수 있다.
#define BIN(a) strtol(#a,NULL,2)
printf("%lx\n",BIN(00010010001101001111000001011100));
- 이 매크로는 문자열을 거쳐 수치를 만들어내므로 효율이 좋지 못하지만 2진수 암산이 잘 안되는 사람들에는 아주 유용하다.
##연산자(merge operator) 역시 #define문 내에서만 사용되며 형식 인수 사이에 위치한다.
- 형식 인수를 분리하여 각각 치환되도록 하며 치환 후에는 주변의 공백과 함께 사라져 두 인수의 치환 결과가 하나의 토큰으로 연결될 수 있도록 한다.
#define var(a,b) (a##b)
void main(){
int var(Total, Score);
TotalScore=256;
printf("총점 = %d\n",TotalScore);
}
- var 매크로는 두 개의 형식인수를 받아 들여 이 두 명칙을 연결해서 하나의 명칭으로 만드는데 형식인수 a와 b 사이에
## 연산자가 사용되었다.
##은 주변의 공백까지 제거하므로 매크로 정의문의 ##좌우 공백은 무시된다.
- 이 연산자는 주로 일괄적인 타입 정의에 사용된다.
#define defptype(type) typedef type *p##type
void main(){
defptype(int);
defptype(double);
defptype(char);
pint pi;
int i=3;
pi=&i;
printf("i = %d\n",*pi);
}
- 행 계속 문자
\도 일종의 전처리 연산자이며 자신과 뒤쪽의 개행 문자를 없는 것으로 취급하여 하나로 연결하는 용도로 사용한다.
- 단,
\ 연산자는 기계적으로 두 행을 연결할 뿐이며 다음 행의 선두에 있는 공백까지도 윗줄에 붙이기 때문에 두 번째 줄을 들여쓰기 해서는 안되는 불편함이 있다.
- 그래서 이 방법보다는 문자열 상수를 연속으로 적는 방법이 더 편리하다.
2.2. 조건부 컴파일
- 조건부 컴파일 지시자는 지정한 조건의 진위 여부에 따라 코드의 일정 부분을 컴파일할 것인지 아닌지를 지정한다.
- 전처리문이므로 컴파일되기 전에 조건을 평가하며 코드를 컴파일 대상에 포함시키거나 제외시키는 역할을 한다.
- 이때 조건의 형태는 여러가지 형태가 있지만 주로 매크로 상수의 존재 여부나 값에 대한 평가식이 사용된다.
- 실행 중에 결정되는 변수나 값이나 함수 호출은 당연히 조건문이 될 수 없다.
- 조건부 컴파일 지시자를 잘 활용하면 한벌의 코드를 조건에 따라 다르게 컴파일하여 상이한 실행 파일을 만들어낼 수 있다.
- 만약 조건부 컴파일 기능이 없다면 실행 파일별로 소스를 따로 유지해야 하므로 무척 번거로워진다.
- 조건부 컴파일 지시자는 다양한 상황과 목적에 맞게 소스를 컴파일하여 호환성과 이식성을 확보하는 수단으로 빈번하게 활용되므로 잘 알아둬야 한다.
- 다음은 조건부 컴파일문의 가장 전형적인 예다.
#ifdef 매크로명
#endif
#ifdef 다음에 조건이 되는 매크로명을 써 주고 #endif 사이에 조건부로 컴파일할 코드를 작성한다.
- 조건부 컴파일 블록에 포함된 코드는 매크로가 존재하면 컴파일이 될 것이고 그렇지 않다면 전처리 과정에서 삭제되어 아예 없는 것을 취급된다.
#ifdef ~ #endif 블록으로 조건부 컴파일 대상 코드를 명시하므로 { }로 이 코드를 감쌀 필요는 없다.
- 예를 들어 워드 프로세서를 만드는데 전문가용과 일반용 두 버전을 만들고 싶은데 두 용도에 대한 부가적인 기능의 유무에 차이가 있다고 한다면, 다음과 같이 코드를 구성할 수 있다.
#define PROFESSIONAL
#ifdef PROFESSIONAL
#endif
#ifdef PROFESSIONAL
#endif
- 전문가용에만 포함되는 코드를 이런 조건부 컴파일 블록에 포함시켜 놓으면 이 코드들은 PROFESSIONAL 매크로 상수가 정의되었을 때만 컴파일이 된다.
- 소스 선두에
#define PROFESSIONAL 이라는 매크로 정의문을 미리 작성해 놓고 이대로 컴파일하면 전문가용 실행 파일이 생성되고, 이 정의문을 주석 처리한 후 다시 컴파일하면 고급 기능은 컴파일 대상에서 제외되는 일반용 실행 파일이 생성된다.
- 이 외의 활용방안으로 다음과 같이 개발중에 디버깅을 위해 사용할 수 있는 예제이다.
#ifdef _DEBUG
printf("변수 값 확인. i=%d\n", i);
#endif
- 디버깅용 코드는 개발중에 디버깅 편의를 위해 삽입된 임시 코드이며 실제로 릴리즈할 때는 컴파일되지 말아야 하기 때문에 위와 같이 사용할 수 있다.
#ifndef는 #ifdef와 반대의 조건을 점검하는 지시자다.
#ifndef는 매크로가 정의되어 있지 않을 때만 컴파일한다.
#ifdef ~ #endif 사이에 #else를 넣을 수도 있는데 #ifdef에 지정한 조건 이 외의 조건인 경우를 처리한다.
#else를 포함한 조건부 컴파일러 구문은 다음과 같다.
#ifdef PROFESSIONAL
#else
#endif
2.3. #if
#ifdef, #ifndef는 매크로의 존재 여부만으로 컴파일 조건으로 판단하며 매크로가 어떤 값으로 정의되어 있는지는 평가하지 않는다.
- 이에 비해
#if는 매크로의 값을 평가하거나 여러 가지 조건을 결합하여 컴파일 여부를 결정하는 좀더 복잡한 전처리문이다.
- 다음은
#if을 이용한 조건부 컴파일 구문이다.
#if 조건1
#elif 조건2
#else
#endif
#elif, #else는 필요없을 경우 생략 가능하며, #elif는 얼마든지 올 수 있다.
#if, #elif에 올 수 있는 조건문은 전처리 과정에서 진위 여부를 판단할 수 있는 것이어야 한다.
- 매크로값을 비교할 때는 상등, 비교 연산자를 사용한다.
- 조건문은 꼭 괄호로 싸지 않아도 상관없지만 괄호를 붙여주는 편이 좋다.
- 비교 대상은 정수 상수여야 하며 실수나 문자열은 매크로와 비교할 수 없다.
- 컴파일러에 따라 실수 비교를 허용하는 것들도 있는데, 조건부 컴파일을 통제하는 매크로는 대소가 있는 값이라기보다는 주로 표현식이기 때문에 실수는 별로 실용성이 없다고 할 수 있다.
- 정수값을 가지는 다른 매크로와 값을 비교하는 것은 가능하다.
- 버전 번호 같은 경우에 1.0, 1.5같이 실수로 표기하지만 매크로 상수로 버전을 표시할 때는 100, 150 등과 같이 정수화해서 사용하는 것이 일반적이다.
- 수식 내에서 간단한 사칙 연산을 할 수 있다.
- 전처리기가 연산문을 평가한 후 그 결과를 비교하므로 다소 복잡한 식은 굳이 결과를 계산해 넣을 필요없이 수식을 바로 써도 상관없다.
- 나머지 연산, 비트 연산 등도 가능하다.
그러나 증감연산, 포인터 연산, sizeof, 캐스트 연산 등은 사용할 수 없다.
- 논리 연산자로 두 개 이상의 조건을 동시에 평가할 수 있다.
- 이때 필요하다면 조건 평가의 우선 순위 지정을 위해 괄호를 사용한다.
defined연산자로 매크로의 존재 여부를 평가할 수 있다.
- 논리 연산자(의 피연산자로)와 함께 사용될 수 있다.
#if (LEVEL==8 || defined(PROFESSIONAL))
#if는 중첩이 가능한 전처리문이다.
if (LEVEL==8)
if (VER>5)
#endif
#if 0도 주석 대신 흔히 사용되는 구문이다.
- 아주 긴 소스를 잠시 주석 처리해 놓고 싶을 때는 이 부분을
#if 0 ... #endif로 감싸 버리면 항상 거짓이므로 전처리기에 의해 이 코드는 없는 것으로 취급된다.
/* */ 주석은 중첩될 수 없이 긴 소스를 주석 처리할 때 불편한 반면 #if 0은 중첩 가능하기 때문에 이런 문제가 없다.
2.4. #undef
#undef는 #define의 반대되는 동작을 하는 전처리문이다.
#define이 매크로를 정의하는데 비해 #undef는 정의되어 있는 매크로를 삭제한다.
- 전처리기는 이 명령을 만나면 지정한 매크로의 정의를 취소하고 이후부터 이 명칭에 대해서는 치환을 중지한다.
#undef 다음에 취소하고 싶은 매크로의 이름만 적어주면 된다.
- 일반적으로 매크로는 한 번 정의되면 프로젝트 전체에 걸쳐 일관된 값으로 사용되므로 이미 정의되어 있는 매크로를 취소할 경우란 그리 흔하지 않다.
- 존재하는 매크로를 #define으로 다시 정의할 경우 재정의할 수 없다는 경고(또는 에러) 메시지가 출력되므로 매크로를 먼저 지운 후 다시 정의해야 한다.
- 실전에서는 어쩔 수 없이 매크로를 재정의해야 하는 경우도 있는데 예를 들면 외부에서 가져온 헤더 파일의 매크로가 충돌될 때를 들 수 있다.
- ext1.h, ext2.h 두 헤더 파일이 모두 TIME이라는 매크로를 사용하는데 이 값이 서로 달라야 한다면 다음과 같이 헤더 파일을 포함하기 전에 매크로를 재정의하면 된다.
#define TIME 800
#include <ext1.h>
#undef TIME
#define TIME 1400
#include <ext2.h>
- 이렇게 하면 ext1.h를 컴파일할 때 TIME은 800이 되고, ext2.h를 컴파일할 때 TIME은 1400이 된다.
- 일정한 범위 내에서만 매크로의 의미를 잠시 바꾸고 싶을 때 매크로 재정의 기법이 가끔 필요하다.
- 또한 자신이 사용하는 매크로를 외부에서 정의하고 있더라도 자신만의 매크로를 꼭 정의하고 싶다면 조건부 컴파일 지시자와 함께 사용할 수도 있다.
#ifdef MACRO
#undef MACRO
#endif
#define MACRO 내가 원하는 값
- 만약 MACRO가 이미 정의되어 있다면 취소해버리고 원하는 값으로 다시 정의하는 것이다.
#define은 중복 정의를 허용하지 않는 반면 #undef는 존재하지 않는 매크로이더라도 에러로 처리되지 않는 특성이 있다.
- 그래서 위 코드에서 위 코드에서 조건부 컴파일 지시자가 꼭 필요한 것은 아니다.
2.5. 미리 정의된 매크로
- 미리 정의된 매크로(Predefined Macro)는 컴파일러가 제공하는 매크로다.
- 주로 컴파일러가 현재 상황이나 컴파일 중에 참고할만한 정보를 알려주기 위한 용도 사용한다.
#define으로 정의하지 않아도 사용할 수 있으며 재정의할 수도 없다.
- 필요할 때 참조하여 정보를 조사할 수만 있는 읽기 전용 매크로 상수라고 생각하면 된다.
| 매크로명 | 설명 | 출력 서식 | 결과 |
|---|
__DATE__ | 컴파일될 때의 날짜를 나타내는 문자열이다. | %s | May 16 2024 |
__TIME__ | 현재 소스가 최후 컴파일 된 시간을 나타내는 문자열이다. | %s | 17:52:52 |
__TIMESTAMP__ | 현재 소스가 최후로 수정된 날짜와 시간을 나타낸다. | %s | Thu May 16 17:52:52 2024 |
__FILE__ | 현재 소스 파일의 이름이다. | %s | hello.c |
__LINE__ | 이 매크로가 포함된 소스상의 줄 번호이다. 10진 정수다. | %d | 19 |
__STDC__ | 컴파일러가 ANSI C 표준을 따를 경우 1로 정의되며, 그렇지 않을 경우 정의되지 않는다. C++로 컴파일할 때는 이 매크로가 없다. | | |
- 이 매크로들은 활용하기에 따라서 여러가지 용도가 있다.
- 예를 들어 실행 중에 디버깅 정보를 출력하는 함수(assert)에서
__FILE__, __LINE__을 사용하면 현재 실행되고 있는 곳의 소스 파일과 그 위치를 쉽게 출력할 수 있으며 날짜나 시간 정보는 소스의 버전 관리에 활용될 수 있을 것이다.
2.6. #error, #line
#error 전처리문은 지정한 에러 메시지를 출력하고 전처리 과정에서 컴파일을 중지하도록 한다.
- 단독으로 사용되는 경우는 없으며 주로 조건부 컴파일 지시지와 함께 사용되어 컴파일 불가능한 상황임을 개발자에게 알려주는 역할을 한다.
#ifndef UNICODE
#error This program require unicode environment
#endif
- 이 문장의 사용 위치는 어디든 상관없으나 보통 소스 선두에 둔다.
- 운영체제의 버전이 맞지 않다거나 개발툴이 부적합하다거나 할 때도
#error 전처리문을 사용한다.
- 자신이 만든 모듈이 불특정 다수가 사용할 예정이라면 안전을 위해 이 모듈이 컴파일될 수 있는 상황을 명확하게 알려 주는 것이 좋다.
- 컴파일 조건을 문서에 적어 놓거나 주석으로 기록할 수도 있겠지만 모든 개발자가 문서를 꼼꼼하게 읽어보지 않기 때문에 이런 강제적인 에러 메시지 출력문이 필요하다.
#line 전처리문은 __LINE__, __FILE__ 매크로를 재정의한다.
- 줄 수는 정수로 주고 파일명은 문자열 형태로 지정하되 파일명을 생략할 경우 기존의 파일명이 그대로 유지된다.
#line 123 "a.c"
- 이 명령은 현재 줄을 123으로 바꾸고 파일명으로 'a.c'로 변경한다.
- 이 전처리문은 사용자가 직접 사용하는 경우가 드물며 주로 컴파일러가 에러 메시지를 조립하기 위해 내부적으로 사용한다.
3. pragma 지시자
3.1. once
- c언어의 장점 중 하나는 어느 운영체제나 플랫폼으로 쉽게 이식될 수 있는 이식성(portability)이다.
- 그러나 이 이식성은 어디까지나 소스 차원에서 이식 가능성을 의미하는 것이지 컴파일된 결과인 실행 파일은 그렇지 않다.
- c언어는 이식성이 있지만 c언어를 특정 플랫폼에 맞게 컴파일하여 고유의 실행 파일을 만들어 내는 컴파일러는 본질적으로 플랫폼에 종속적이다.
- 그래서 각 플랫폼에서 실행되는 컴파일러는 플랫폼의 고유한 기능을 수행하기 위한 지원을 해야 한다.
- 플랫폼별로 실행 파일의 특수한 구조로 인한 코드 배치 방법이 플랫폼별로 고유하다.
#pragma 지시자는 플랫폼별로 다른 이런 기능에 대한 지시사항을 컴파일러에게 전달하는 방법이다.
#문자로 시작하므로 전처리 명령처럼 보이지만 컴파일러 지시자이다.
- 다음은
#pragma 지시자의 기본 형식 이다.
#pragma 다음에 지시 사항을 전달하는 토큰 문자열이 오는데 이 토큰의 종류는 컴파일러별로 다르다.
- 플랫폼에 종속적인 기능에 대한 지시자이므로
#pragma지시자는 컴파일러에 대해서 종족적일 수밖에 없다.
- 그래서 특정 플랫폼을 위한 프로그램을 작성할 때만 사용해야 하며 꼭 이식성을 유지하려면 조건부 컴파일 지시자와 함께 사용해야 한다.
- 컴파일러는
#pragma 다음의 토큰을 인식할 수 없을 경우 단순히 무시해 버리며 컴파일은 계속 수행한다.
출처 : 혼자 연구하는 C/C++ 1 / 김상형 저 / 와우북스