본문 바로가기
c언어

c언어 여러가지 전처리기와 매크로, 다중 소스 파일을 이용하여 여러 가지 문제 풀기(ASSERT 매크로 작성하기, 비트 매크로 작성하기, 여러가지 버전 정의하기(디럭스 버전과 스탠다드 버전 정..

by 개발자 L 2023. 2. 22.
반응형

c언어 여러 가지 전처리기와 매크로, 다중 소스 파일을 이용하여 여러 가지 문제 풀기(ASSERT 매크로 작성하기, 비트 매크로 작성하기, 여러 가지 버전 정의하기(디럭스 버전과 스탠더드 버전 정의하기), 리눅스 버전과 윈도 버전 분리하기, 헤더 파일 중복 포함 막기, 전처리기 사용해서 원의 면적 구하기)

네 안녕하세요, 이번 포스팅에서는 지금까지 배웠던 전처리기들과 매크로,

그리고 다중 소스 파일을 이용하여 여러 가지 문제들을 풀어보는 시간을 가져보려 합니다.

매크로를 거는 것과 다중 소스 파일을 다루는 것은 매우 중요한 부분들 중 하나이므로,

알아두셔야 합니다.

그럼 지금부터 시작하도록 하겠습니다.

 

1. ASSERT 매크로 작성하기

ASSERT 매크로는 프로그램을 디버깅할 때 자주 쓰이는 매크로입니다.

ASSERT 매크로는 어떤 전제 조건을 검사하는 목적으로 사용이 되는데,

예를 들어서 어떤 프로그램의 논리적인 버그 등으로 다른 값이 출력이 되었거나 오류를 범했을 때,

매크로를 써주게 되면 __LINE__과 __FILE__ 매크로를 이용하여

오류가 생긴 소스 파일의 이름과 해당 파일에서 오류가 있는 행 번호를 출력을 해주는 기능을 합니다.

프로그래밍을 할 때 디버깅이 중요한 만큼 정말 자주 쓰입니다.

그럼 지금부터 코드를 작성해 보도록 하겠습니다. 

#include <stdio.h>

#define ASSERT(exp) {if (!(exp)) \
{printf("가정("#exp")이 소스 파일 %s의 %d 번째 줄에서 실패했습니다.\n"\
, __FILE__, __LINE__), exit(1);}}

int main()
{
    int sum = 100;

    ASSERT(sum == 0);

    return 0;
}

이렇게 작성을 해봤습니다.

sum이라는 변수의 값이 100인데,

ASSERT를 이용해서 sum의 값이 0이라고 했을 때 결과가 어떻게 나오는지 보여드리도록 하겠습니다. 

tempCodeRunnerFile.c:11:5: note: in expansion of macro 'ASSERT'
     ASSERT(sum == 0);
     ^~~~~~
가정(sum == 0)이 소스 파일 tempCodeRunnerFile.c의 11 번째 줄에서 실패했습니다.

이런 식으로 어디서 실패를 했는지 오류 메시지를 출력함과 동시에 위치도 알려줍니다.

 

2. 비트 매크로 작성하기

이번에는 비트 매크로를 작성을 해볼 건데,

비트 매크로는 아두이노와 같은 엠베디드 시스템,

다시 말하면 하드웨어를 제어하기 위해서 작성하는 매크로입니다.

그래서 하드웨어가 가지는 특정한 비트를 1 또는 0으로 만들어줍니다.

이럴 때 사용하는 함수 매크로는 GET_BIT()인데, 제가 직접 코드를 작성해서 보여드리도록 하겠습니다. 

#include <stdio.h>

#define GET_BIT(w, k) (((w) >> (k)) && 0x01) // 변수의 w의 k번째 비트 값 반환
#define SET_BIT_ON(w, k) ((w) |= (0x01 << (k))) // 변수의 w의 k번째 비트 값을 1로 설정
#define SET_BIT_OFF(w, k) ((w) &= ~(0x01 << (k))) // 변수의 w의 k번째 비트 값dmf 0으로 설정

int main()
{
    int data = 0;

    SET_BIT_ON(data, 2); // data의 두 번째 비트를 1로 설정

    printf("%08x\n", data);
    printf("%d\n", GET_BIT(data, 2)); // data의 두 번째 비트 값 출력

    SET_BIT_OFF(data, 2); // data의 두번쨰 비트를 0으로 설정

    printf("%08x\n", data);
    printf("%d\n",  GET_BIT(data, 2)); // data의 두 번째 비트 값 출력

    return 0;
}

이렇게 작성을 해봤습니다.

그럼 결과가 어떻게 나오는지 한 번 보도록 하겠습니다.

00000004
1
00000000
0

결과 역시 잘 나온 것을 확인할 수 있습니다. 3.

 

3. 여러 가지 버전 정의하기(디럭스 버전과 스탠더드 버전 정의하기) 

이번에는 디럭스 버전과 스탠더드 버전의 프로그램을 개발했다는 가정 하에

디럭스 버전의 프로그램을 실행하거나 스탠더드 버전의 프로그램을 실행하는 코드를 작성해 보도록 하겠습니다.

그럼 바로 보여드리도록 하겠습니다. 

#include <stdio.h>

#define DELUXE

int main()
{
#ifdef DELUXE
    printf("디럭스 버전입니다.\n");
#endif

    return 0;
}

이렇게 작성을 해봤습니다.

아무것도 출력이 되지 않으면 스탠더드 버전,

디럭스 버전이라고 출력이 되면 디럭스 버전이 됩니다.

여기서 DELUXE라는 기호 상수가 살아있으면 #ifdef 전처리기가 실행되면서 디럭스 버전이 되고,

DELUXE가 죽어있을 경우에는 아무것도 뜨지 않아 스탠더드 버전이 됩니다.

그럼 결과를 보도록 하겠습니다. 

디럭스 버전입니다.

문제없이 잘 나오는 것을 확인할 수 있습니다.

반응형

 

4. 리눅스 버전과 윈도 버전 분리하기

이번에는 리눅스 버전과 윈도 버전을 분리해 보도록 하겠습니다.

프로그램, 이를테면 게임이나 우리가 지금 쓰는 IDE 프로그램 등도 버전이 여러 가지입니다.

그래서 버전의 분리가 필요합니다.

이럴 때 #ifdef에 #else를 써서 버전을 분리할 수도 있습니다.

그럼 바로 보여드리도록 하겠습니다. 

#include <stdio.h>

#define LINUX

int main()
{
#ifdef LINUX
    printf("리눅스 버전입니다.\n");
#else
    printf("윈도우 버전입니다.\n");
#endif

    return 0;
}

이런 식으로 작성을 해봤습니다.

여기서 LINUX라는 기호 상수가 살아있으면 리눅스 버전이라고 출력을 하고,

죽어있다면 윈도 버전이라고 출력을 하게 됩니다.

그럼 결과를 보도록 하겠습니다. 

리눅스 버전입니다.

결과도 잘 나오는 것을 확인할 수 있습니다.

 

5. 소스 파일의 중복 막기

이번에는 소스 파일의 중복을 막아보도록 하겠습니다.

정석적인 버전이 있고,

요즘 c언어 버전에 맞는 간단한 방법이 있습니다.

다 보여드릴 테니 보시면 좋을 것 같습니다.

그럼 바로 보여드리도록 하겠습니다.

문제는 이렇습니다. 

struct STUDENT
{
    int number;
    char name[10];
};

이러한 student.h 헤더 파일이 있다고 가정을 해봅시다.

그리고 본 소스에다가 이런 행동을 했습니다. 

#include <stdio.h>
#include "student.h"
#include "student.h"

int main()
{
    return 0;
}

여기서 보시면 아실 수 있듯이 student.h 헤더 파일이 중복이 되어 들어갔습니다.

이럴 경우에는 컴파일 시 에러가 나므로 중복을 막아줘야 합니다.

제일 먼저 정석적인 방법을 먼저 보여드리도록 하겠습니다.

 

5 - 1. 정석적인 방법

정석적인 방법은 이렇습니다.

헤더 파일에 이런 식으로 정의를 하는 것입니다. 

#ifndef STUDENT_H
#define STUDENT_H

struct STUDENT
{
    int number;
    char name[10];
};

이 뜻은 STUDENT_H라는 기호 상수가 정의가 되지 않았다면 아래를 컴파일하라는 뜻이 됩니다.

만일에 해더 파일을 처음 포함이 되는 경우라면 STUDENT_H가 정의가 되어있지 않으므로,

문장들을 컴파일하게 됩니다.

이렇게 되면 실수로 같은 헤더 파일이 포함이 되더라도 STUDENT_H가 정의가 되어있기 때문에

컴파일을 하지 않고서 알아서 지나가게 됩니다.

 

5 - 2. 요즘 쓰는 방법

요즘 쓰는 방법은 더 간단해졌습니다.

헤더 파일에 이 문장만 제일 상단에 포함시키면 됩니다. 

#pragma once

저 헤더 파일을 한 번만 쓰도록 하겠다는 뜻이기 때문에

중복을 알아서 방지해 주는 키워드입니다.

비주얼 스튜디오의 경우는 헤더 파일을 작성할 때 알아서 저게 상단에 적혀있어서

그냥 함수 원형만 적어서 저장하면 되니 매우 편리합니다.

 

6. 전처리기 사용해서 원의 면적 구하기 이번에는

전처리기를 사용해서 원의 면적을 구해보도록 하겠습니다.

전처리기를 이용해서 한글 버전과 영문 버전을 출력해 보도록 하겠습니다.

그럼 바로 시작해 보도록 하겠습니다. 

#include <stdio.h>

#define USA
#define DEBUG

#ifndef PI // 단순 매크로 PI가 정의되어있지 않다면 PI 정의
#define PI 3.141592
#endif

#ifndef SQUARE // 함수 매크로 SQUARE이 정의되어있지 않다면 SQUARE 정의
#define SQUARE(r) (r)*(r)
#endif

area(double radius)
{
    double result = 0.0;

#ifdef DEBUG // DEBUG가 정의되어 있다면 디버깅 정보 호출
#ifdef USA
    printf("area(%f) is called.\n", radius);
#else
    printf("area(%f) 가 호출되었음.\n", radius);
#endif
#endif

    result = PI * SQUARE(radius);

    return result;
}

main()
{
    double radius;

#ifdef USA // USA가 정의되어 있으면 모든 글자를 영문으로 출력
    printf("Please enter radius of a circle(inch) : ");
#else
    printf("원의 반지름을 입력하세요(cm) : ");
#endif

    scanf("%lf", &radius);

#ifdef USA
    printf("area of the circle is %f.\n", area(radius));
#else
    printf("원의 면적은 %f 입니다.\n", area(radius));
#endif

    return 0;
}

이렇게 한 번 작성을 해봤습니다.

이렇게 작성을 했을 때는 어떻게 나오는지 결과를 보도록 하겠습니다. 

Please enter radius of a circle(inch) : 5
area(5.000000) is called.
area of the circle is 78.539800.

이렇게 결과가 잘 나왔습니다.

그러면 이제 한글 버전을 출력해 보겠습니다.

한글 버전은 그냥 USA 부분을 주석 처리 하면 됩니다.

이렇게 말이죠. 

#include <stdio.h>

//#define USA
#define DEBUG

#ifndef PI
#define PI 3.141592
#endif

#ifndef SQUARE
#define SQUARE(r) (r)*(r)
#endif

int area(double radius)
{
    double result = 0.0;

#ifdef DEBUG
#ifdef USA
    printf("area(%f) is called.\n", radius);
#else
    printf("area(%f) 가 호출되었음.\n", radius);
#endif
#endif

    result = PI*SQUARE(radius);

    return result;
}

int main()
{
    double radius;

#ifdef USA
    printf("Please enter radius of a circle(inch) : ");
#else
    printf("원의 반지름을 입력하세요(cm) : ");
#endif

    scanf("%lf", &radius);

#ifdef USA
    printf("area of the circle is %f.\n", area(radius));
#else
    printf("원의 면적은 %f 입니다.\n", area(radius));
#endif

    return 0;
}

이제 결과를 보도록 하겠습니다. 

원의 반지름을 입력하세요(cm) : 5
area(5.000000) 가 호출되었음.
원의 면적은 78.539800 입니다.

결과도 제대로 나온 것을 볼 수 있습니다. 여기까지

 

여러 가지 전처리기와 매크로, 다중 소스 파일을 이용해서 여러 가지 문제들을 풀어봤습니다.

다음 포스팅부터는 동적 메모리에 대하여 알아보도록 하겠습니다.

글 읽어주신 독자분들께 진심으로 감사드립니다~

반응형

댓글