티스토리 뷰

반응형

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 = &currentPrice_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
링크
«   2025/08   »
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
글 보관함