2023. 2. 15. 16:56ㆍC언어/기본 개념
전처리: 컴파일러가 처리하는것이 아닌 전처리기라는 것이 전처리 단계해서 처리하는 것이다.
컴파일러에 소스코드를 넘기기전에 코드를 편집해주고 다듬어주는 과정이다.
컴파일 과정
소스파일 > 전처리된 소스파일 > 개체 파일 > 실행파일
전처리 컴파일 링크
전처리: 소스파일을 컴파일러가 컴파일하기 좋게끔 다듬는 과정(주석, 전처리 지시문 처리)
전처리 지시자
- 다른 파일에 있는 내용을 소스코드로 가져오는 #include
- 매크로 상수와 함수를 만드는 #define
- 이미 정의된 매크로(__FILE__, __FUNTION__ 등)
- 매크로 함수 연산자 #과 ##
- 조건부 컴파일 지시자
- #pragma 지시자
1. #include 지시자
#으로 시작하면 전처리 지시자.
지정한 파일의 내용을 읽어 지시자 위치에 붙여 넣는다.
파일명을 <> 또는 " "로 묶는다.
<>와 " " 의 차이점
ex. #include <stdio.h> 경우
시스템 헤더파일을 컴파일러가 설정한 include 디렉터리에서 찾는다.
ex. "student.h"
사용자정의 헤더 파일
소스파일이 저장된 디렉터리에서 먼저 찾고 없다면 include 디렉터리에서 다시 찾아본다.
ex. #include "C:\user\myhdr.h"
직접 경로를 지정도 할 수 있다.
전처리가 끝나면 인클루드한 파일의 내용은 복사되어 소스파일에 포함된다.
2. #define 지시자
복잡한 상수나 문장에 대해 매크로명을 정의
#define PI 3.141592 //상수
#define LIMIT 100.0 //상수
#define MSG "passed!" // 문자열
#define ERR_PRN printf("범위를 벗어났습니다.\n") //출력문
#define SUM(a,b) ((a)+(b))
#define MUL(a,b) ((a)*(b))
인수에 따라 서로 다른 결과값을 갖도록 치환한다.
매크로 함수는 부작용을 줄이기 위해 괄호를 사용.
a = SUM(1,2);
b= MUL(3, 5);
printf("a 출력 값 : %d",a);
printf("b 출력 값 : %d",b);
a 출력 값 : 3
b 출력 값 : 15
3. 이미 정의된 매크로
정의가 되어 있기 때문에 취소하거나 수정이 안 된다.
정의된 매크로 | 기능 |
__FILE__ | 전체 디렉터리 경로를 포함한 파일명 |
__FUNTION__ | 매크로명이 사용된 함수 이름 |
__LINE__ | 매크로명이 사용된 행 번호 |
__DATE__ | 컴파일을 시작한 날짜 |
__TIME__ | 컴파일을 시작한 시간 |
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <stdio.h>
void func(void);
int main(void)
{
printf("컴파일 날짜와 시간 : %s, %s\n\n", __DATE__, __TIME__);
printf("파일명 : %s\n", __FILE__);
printf("함수명 : %s\n", __FUNCTION__);
printf("행번호: %d\n////////////////////////////\n", __LINE__);
#line 100 "macro.c"
func(); //100행
return 0;
}
void func(void)
{
printf("컴파일 날짜와 시간 : %s, %s\n\n", __DATE__, __TIME__);
printf("파일명 : %s\n", __FILE__);
printf("함수명 : %s\n", __FUNCTION__);
printf("행번호: %d\n", __LINE__);
}
|
cs |
컴파일 날짜와 시간 : Feb 16 2023, 10:44:12 파일명 : C:\stu.c 함수명 : main 행번호: 8 //////////////////////////// 컴파일 날짜와 시간 : Feb 16 2023, 10:44:12 파일명 : macro.c 함수명 : func 행번호: 111 |
9행부터 코드로 인해 10행부터 100, 101, 102... 로 행 번호가 바뀐다. 그리고 파일명도 "macri.c"로 바꾸었다.
4. 매크로 함수 연산자 #과 ##
#은 매크로 함수의 인수를 문자열로 치환한다.
##은 두 인수를 붙여서 치환한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h>
#define PRINT_EXPR(x) printf(#x " = %d\n", x)
#define NAME_CAT(x,y) (x ## y)
int main(void)
{
int a1, a2;
NAME_CAT(a, 1) = 10; // a와 1을 합쳐서 a1 변수를 만든다.
NAME_CAT(a,2) = 20;
PRINT_EXPR(a1 + a2); // #x로인해 "a1 + a2" 문자로 치환 된다
PRINT_EXPR(a2 - a1);
return 0;
}
|
cs |
8, 9행을 보면 a1, a2라는 변수를 만들기 위해서 a에다가 숫자를 이어 붙여주었다.
10, 11행은 2행에 정의한 방식대로 하면 인수로 넣은 식이 문자열로 바뀌면서 식과 값을 출력할 수 있게 된다.
a1 + a2 = 30 a2 - a1 = 10 |
5.조건부 컴파일 지시자
조건에 따라 소스코드를 선택적으로 컴파일
#if ~ #elif ~ #else ~ #endif #if 조건식 컴파일 문장 #endif |
#if defined BIT16 //#ifdef BIT16 과 동일하다. 컴파일 문장 #endif |
#if (defined (BIT16) && (VER >= 6)) //VER : 버전 컴파일 할 문장 #endif |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <stdio.h>
#define VER 7
#define BIT16 // 치환 부분 X
int main(void)
{
int max;
#if VER >= 6
printf("버전 %d입니다.\n", VER);
#endif
#ifdef BIT16
max = 32767;
#else
max = 2165468515;
#endif
printf("max = %d", max);
return 0;
}
|
cs |
#undef //매크로명 정의 취소 #error // 메시지를 출력하고 컴파일 중단 ex.#error 컴파일러 버전은 6.0이상이어야합니다. |
6.pragma 지시자
컴파일러의 컴파일 방법을 제어
ex. #pragma pack(1)
#pragma warning(disable:4996)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <stdio.h>
#pragma pack(push, 1) //바이트 얼라인먼트를 1로 // 패딩 바이트 포함하지 않는다.
typedef struct
{
char ch;
int in;
}Sample1;
#pragma pack(pop) // 바꾸기 전에 바이트 얼라인먼트 적용
typedef struct
{
char ch;
int in;
}Sample2;
int main(void)
{
printf("Sample1 구조체의 크기 : %d바이트\n", sizeof(Sample1));
printf("Sample2 구조체의 크기 : %d바이트\n", sizeof(Sample2));
return 0;
}
|
cs |
2행 push는 바이트 얼라인먼트를 바꿀 때 규칙을 기억한다는 뜻이다.
Sample2는 가장 큰 크기(int형)에 맞춰서 공간을 확보하기 때문에 8바이트가 된다.
'C언어 > 기본 개념' 카테고리의 다른 글
분할 컴파일 (0) | 2023.02.23 |
---|---|
파일 개방 및 입출력 (0) | 2023.02.14 |
구조체 활용, 공동체, 열거형 (0) | 2023.02.10 |
구조체 (0) | 2023.02.09 |
동적 할당 저장 공간의 활용 (0) | 2023.02.09 |