티스토리 뷰
반응형
1) 목표
- 개발시 자주 사용되는 전처리기 이해하기
2) 설명
- 전처리기는 gcc 컴파일 하기전에 이루어지는 작업입니다.
- 전처리기는 #include, #define, #ifdef, #undef, 등 여러가지 형태가 있습니다.
- 이전 강의에서 배운 #pragma도 전처리기의 한 형태입니다.
- 여기서는 자주 사용되는 #include, #define, #ifdef 문에 대해서 알아봅니다.
// 사용자가 작성한 헤더파일을 포함시킬 수 있습니다.
#include "my_header.h"
// 매크로를 정의할 수 있습니다.
#define MAX_ARRAY 100
// 매크로를 조건연산자로도 정의할 수 있습니다.
// a,b중 작은값을 구하는 매크로입니다.
#define MIN_LEN(a,b) (((a)<(b))?(a):(b))
// 매크로를 함수로도 정의할 수 있습니다.
#define ADD_PRINT(a,b) \
{ \
printf("%d + %d = %d\n", (a), (b), (a) + (b)); \
}
// #define으로 정의된것을 제거할 수 있습니다.
#undef ADD_PRINT
// #define를 조건문으로 분기가 가능합니다.
#ifndef MY_HEADER
#include "my_header.h"
#define MY_HEADER
...
#endif
3-1) #include문 예제 프로그램
▶ 구조체 예제 프로그램 struct8.c에서 구조체 정의를 stock.h 헤더파일로 분리해 봅니다.
▶ vi stock.h
#pragma pack(1)
// 값만을 가지는 struct
typedef struct {
int currentPrice; // 현재가
int startPrice; // 시가
int highPrice; // 고가
int lowPrice; // 저가
} PRICE_ST;
// 주식 현재가 struct 의 새로운 타입을 정의
// struct의 멤버로 struct를 가질수 있습니다.
typedef struct {
char code[10]; // 주식코드
char name[80]; // 주식명
PRICE_ST price;
double qty; // 거래량
} CURRENT_PRICE_ST;
▶ vi struct9.c
#include <stdio.h>
// 구조체가 정의된 stock.h 헤더파일을 포함시킵니다.
#include "stock.h"
// call-by-value로 구조체 사용하기
void print_value(CURRENT_PRICE_ST valuePrice) {
printf("--- print_value ---\n");
printf("주식코드:[%s]\n", valuePrice.code);
printf("주식명 :[%s]\n", valuePrice.name);
printf("현재가 :[%d]\n", valuePrice.price.currentPrice);
printf("시가 :[%d]\n", valuePrice.price.startPrice);
printf("고가 :[%d]\n", valuePrice.price.highPrice);
printf("저가 :[%d]\n", valuePrice.price.lowPrice);
printf("거래량 :[%.0f]\n", valuePrice.qty);
}
// call-by-reference로 구조체 사용하기
void print_reference(CURRENT_PRICE_ST *ptrPrice) {
printf("--- print_reference ---\n");
printf("주식코드:[%s]\n", ptrPrice->code);
printf("주식명 :[%s]\n", ptrPrice->name);
printf("현재가 :[%d]\n", ptrPrice->price.currentPrice);
printf("시가 :[%d]\n", ptrPrice->price.startPrice);
printf("고가 :[%d]\n", ptrPrice->price.highPrice);
printf("저가 :[%d]\n", ptrPrice->price.lowPrice);
printf("거래량 :[%.0f]\n", ptrPrice->qty);
}
void main() {
// struct _tagCurrentPrice의 선언
CURRENT_PRICE_ST currentPrice_st = {"A005930", "삼성전자", {61000, 60500, 62000, 60300}, 1234500.0};
// 포인터 변수
CURRENT_PRICE_ST *ptrPrice_st = ¤tPrice_st;
// call-by-value 값 출력
print_value(currentPrice_st);
// call-by-reference 값 출력
print_reference(ptrPrice_st);
}
▶ 컴파일/실행
~/c-lecture (master ✘)✹✭ ᐅ gcc -o struct9 struct9.c
~/c-lecture (master ✘)✹✭ ᐅ ./struct9
--- print_value ---
주식코드:[A005930]
주식명 :[삼성전자]
현재가 :[61000]
시가 :[60500]
고가 :[62000]
저가 :[60300]
거래량 :[1234500]
--- print_reference ---
주식코드:[A005930]
주식명 :[삼성전자]
현재가 :[61000]
시가 :[60500]
고가 :[62000]
저가 :[60300]
거래량 :[1234500]
~/c-lecture (master ✘)✹✭ ᐅ
▶ 분석
- 이전 구조체 강의 예제 struct8 과 동일하게 출력됩니다.
- 앞으로 배우시게 되겠지만 하나의 실행파일은 여러개의 c프로그램으로 만들어 질 수 있습니다.
여러개의 c프로그램에서 이런 구조체 정의가 필요하면 c프로그램안에 똑같은 구조체 정의를 넣어야 합니다.
이렇게 여러곳에서 필요한 내용을 헤더파일로 따로 만들어서 필요한곳에 #include문으로 정의해 주시면 간단히 사용 가능합니다. - #include <stdio.h> 와 다르게 #include "stock.h"로 코딩되어 있습니다.
이처럼 c언어에서 제공하는 헤더파일은 <> 로 사용되고, 여러분들이 작성한 헤더파일은 ""로 사용합니다.
그러니 여러분들이 만든 헤더파일을 포함할때는 #include "header.h" 로 작성하시기 바랍니다.
3-2) #define문 예제 프로그램
▶ vi def.c
#include <stdio.h>
// 값을 재정의할 수 있습니다.
#define MAX_ARRAY 100
#define STOCK_NAME "주식명"
// 매크로를 조건연산자로도 정의할 수 있습니다.
// a,b중 작은값을 구하는 매크로입니다.
#define MIN_LEN(a,b) (((a)<(b))?(a):(b))
// 매크로를 함수로도 정의할 수 있습니다.
#define ADD_PRINT(a,b) \
{ \
printf("%d + %d = %d\n", (a), (b), (a) + (b)); \
}
void main() {
char my_array[MAX_ARRAY];
int min_len;
printf("MAX_ARRAY = [%d] : my_array 크기 = [%ld]\n", MAX_ARRAY, sizeof(my_array));
printf("STOCK_NAME = [%s]\n", STOCK_NAME);
// 작은값을 구하는 매크로
min_len = MIN_LEN(10, 5);
printf("MIN_LEN = [%d]\n", min_len);
// 출력매크로
ADD_PRINT(3, 5);
}
▶ 컴파일/실행
~/c-lecture (master ✘)✹✭ ᐅ gcc -o def def.c
~/c-lecture (master ✘)✹✭ ᐅ ./def
MAX_ARRAY = [100] : my_array 크기 = [100]
STOCK_NAME = [주식명]
MIN_LEN = [5]
3 + 5 = 8
~/c-lecture (master ✘)✹✭ ᐅ
▶ 분석
- #define 으로 값을 재정의 해서 사용합니다. 의미가 있는 숫자,문자열을 의미있는 이름으로 재정의해서 사용합니다.
숫자 100를 MAX_ARRAY로 정의, "주식명"을 STOCK_NAME으로 정의해서 프로그램안에서는 MAX_ARRAY를 사용하면 100으로 치환되고, STOCK_NAME을 사용하면 "주식명"으로 치환됩니다. - 매크로를 정의해서 사용합니다. 두개의 변수중 작은값을 구하고자 할때 if문을 사욯할수도 있고, 조건연산자를 사용할수도 있고 이렇게 매크로를 사용해서 사용할 수 도 있습니다.
min_len = MIN_LEN(10, 5); 숫자 10, 5 중 작은값을 리턴해 줍니다.
MIN_LEN은 (10 < 5 ? 10 : 5)로 정의됩니다. 그래서 10은 5보다 작지 않기 때문에 거짓(false)인 5가 리턴됩니다. - 매크로는 좀 더 길게 정의 될수 있습니다.
...
// 매크로를 함수로도 정의할 수 있습니다.
#define ADD_PRINT(a,b) \
{ \
printf("%d + %d = %d\n", (a), (b), (a) + (b)); \
}
- #define문은 한 줄로 정의가 됩니다. 그런데 코딩을 하다 보면 한줄이 길어 지는 경우도 있습니다. 이런경우
c언어에서는 줄의 마지막에 \ (역슬래쉬) 를 넣고 다음줄에 계속해서 작성해도 한줄로 인식을 합니다.
즉, 위의 문장은
// 매크로를 함수로도 정의할 수 있습니다.
#define ADD_PRINT(a,b) { printf("%d + %d = %d\n", (a), (b), (a) + (b)); }
이렇게 정의될 수 있습니다. 그런데 이렇게 한줄로 코딩을 하다보면 읽기가 어려운 경우가 있습니다.
그래서 \ (역슬래쉬)로 분리해서 읽기 편안하게 작성하는 경우가 있습니다.
ADD_PRINT(3, 5)로 불려지면 정의된 printf문이 실행됩니다.
- #undef 문은 거의 사용하는 경우가 없습니다. 기능은 #define으로 정의된걸 제거합니다.
...
// 출력매크로
ADD_PRINT(3, 5);
#undef ADD_PRINT
// 출력매크로
ADD_PRINT(3, 5);
- #undef ADD_PRINT 문 다음에 ADD_PRINT(3, 5);를 다시 호출하면 컴파일시 오류가 발생합니다.
ADD_PRINT가 정의가 안되어 있어서 발생합니다.
3-3) #ifdef문 예제 프로그램
▶ vi def2.c
#include <stdio.h>
#define APPLE "apple"
#define ORANGE "orange"
void main() {
#ifdef APPLE
printf("APPLE 정의 : [%s]\n", APPLE);
#endif
#ifndef ORANGE
printf("ORANGE 정의되어 있지 않음\n");
#else
printf("ORANGE 정의 : [%s]\n", ORANGE);
#endif
#ifdef GRAPE
printf("GRAPE 정의 : [%s]\n", GRAPE);
#elif MANGO
printf("MANGO 정의 : [%s]\n", MANGO);
#else
printf("정의되어 있지 않음\n");
#endif
}
▶ 컴파일/실행
~/c-lecture (master ✘)✹✭ ᐅ gcc -o def2 def2.c
~/c-lecture (master ✘)✹✭ ᐅ ./def2
APPLE 정의 : [apple]
ORANGE 정의 : [orange]
정의되어 있지 않음
~/c-lecture (master ✘)✹✭ ᐅ
▶ 분석
- #ifdef문은 #define으로 정의가 되어 있으면 참(true)이고 정의가 안되어 있으면 거짓(false)입니다.
if문과 동일하게 동작합니다.
#ifdef APPLE ==> APPLE이 #define으로 정의가 되어 있어서 참입니다. - #ifndef문은 #ifdef문과 반대로 정의가 안되어 있으면 참이고 정의되어 있으면 거짓입니다.
#ifndef ORANGE ==> ORANGE가 정의 되어 있어서 거짓입니다. 그래서 #else 문의 printf()문이 실행됩니다. - if문과 마찬가지로 #ifdef ~ #elif ~ #elif ~ #else문으로 사용됩니다.
#ifdef GRAPE 문은 거짓이므로 다음문 #elif MANGO를 체크합니다. 그런데 MANGO도 정의가 안되어 있어서 거짓이므로 #else문이 실행됩니다.
※ #ifdef, #ifndef 문은 끝에 반드시 #endif문으로 끝이 납니다.
3-4) #ifndef - #include문 예제 프로그램
※ 사용자 헤더파일과 자주 사용되는 패턴을 알아보겠습니다.
위에서 작성한 stock.h를 수정합니다.
▶ vi stock.h
#ifndef STOCK_HEADER
#define STOCK_HEADER
#pragma pack(1)
// 값만을 가지는 struct
typedef struct {
int currentPrice; // 현재가
int startPrice; // 시가
int highPrice; // 고가
int lowPrice; // 저가
} PRICE_ST;
// 주식 현재가 struct 의 새로운 타입을 정의
// struct의 멤버로 struct를 가질수 있습니다.
typedef struct {
char code[10]; // 주식코드
char name[80]; // 주식명
PRICE_ST price;
double qty; // 거래량
} CURRENT_PRICE_ST;
#endif
▶ 분석
- 작성한 헤더파일을 포함시켜서 사용하는데 프로그램이 복잡해지고 여러 헤더파일을 사용하다 보면 1번이상 포함시키는 경우도 발생할 수 있습니다. 그러면 컴파일시 중복 오류가 발생합니다. 그래서 1번만 포함시키려고 #ifndef문을 사용합니다.
- 헤더파일 처음에 #ifndef STOCK_HEADER 로 체크를 합니다. 처음에는 당연 STOCK_HEADER가 정의가 안되어 있어서 참이므로 아래문장이 실행됩니다.
#define STOCK_HEADER 정의가 안되어 있어서 정의를 합니다. 그리고 구조체 정의문이 나오고 마지막에 #endif문으로 끝이 납니다. 그럼 또 다시 stock.h가 포함이 되면 #ifndef STOCK_HEADER문에서 STOCK_HEADER문이 정의가 되어있으니 #endif문으로 바로 끝이 납니다. 이렇게 헤더파일 중복 포함을 배제합니다. - 강의가 진행되면서 헤더파일을 만드는일이 있으면 이런방식으로 작성하게 됩니다.
반응형
'리눅스 C-언어 초급과정' 카테고리의 다른 글
18. 구조체 중첩 (0) | 2022.11.21 |
---|---|
17. 구조체와 포인터 (0) | 2022.11.21 |
16. 구조체(struct) (0) | 2022.11.21 |
15. 문자열(배열) 다루기 (0) | 2022.11.17 |
14. 함수 파라메타 유형 (0) | 2022.11.17 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 포인터
- While
- Clang
- #include
- 문자열
- #define
- C언어
- Call-by-value
- Linux
- strcpy
- 재고
- memset
- Member
- c프로그램
- String
- 파라메타
- GCC
- 리눅스
- Call-By-Reference
- struct
- 소켓
- 배열
- #ifdef
- 전처리기
- 의유
- sharetravelplan
- memcpy
- IPC
- array
- Pointer
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
글 보관함