brunch

매거진 Sinclair

You can make anything
by writing

C.S.Lewis

by Sinclair Sep 01. 2016

착한 선행처리기

우리 만남은 우연이 아니야 II




# ## \: 선행처리기 고유의 연산자들


#과 ## 그리고 \는 선행처리기만의 고유한 연산자들입니다. 지금 여러분들 눈에는 이것들이 데데해 보이거나 데면데면하고 생소하게 느껴지겠지만 잘 사용하면 프로젝트를 보다 쉽게 구현하게 해주는 아주 고마운 녀석들입니다.

그리고 C의 다른 연산자들처럼 일반적인 연산자 규칙을 따르지는 않습니다.

먼저 설명하자면 #은 피 연산자를 문자열로 만들어 줍니다. 그리고 ##은 주변의 공백을 모두 없애고 양쪽의 피 연산자를 하나로 결합시킵니다. 만약에 연산자 #과 ## 그리고 다른 매크로가 섞여있다면 #이 먼저 동작하여 문자열로 만들고 그 다음 ##으로 결합이 되고 나머지 매크로 치환이 일어납니다.

그리고 \은 다음 줄과 연결하여 아래 줄이 현재 줄과 같은 연결된 하나의 줄임을 알립니다. 주의할 것은 \ 연산자 다음에 어떤 문자나 심지어 공백이든 함께 적게 되면 그것은 escape문자가 되기 때문에 반드시 줄의 마지막 칸에 사용해야 합니다. 예전 OS환경과 컴파일환경에서는 인식할 수 있는 한 줄의 길이가 80바이트였기 때문에 긴 수식을 처리하기 위해 나온 연산자이지만 현재는 거의 모든 컴파일러가 \를 사용하지 않아도 ;(세미콜론)이 나오기 전까지는 하나의 문장으로 인식하고 있습니다.  



#include <stdio.h>  

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/

#define printResult1(exp) printf("exp = %d\n\n" , (exp))

#define printResult2(exp) printf(#exp " = %d\n\n" , (exp))

#define result(i) printResult##i(3*7)

#define re(i,j,k) (i+j+k)*i##j##k

// 이런 식으로 여러 개를 결합하는 것도 가능합니다.


#define toWCHAR(s) L ## #s

// 이렇게 연산자 두 개를 섞어 사용해도 됩니다.

// toWCHAR(Sinclair)라고 하면 L"Sinclair"가 됩니다.


int main(int joy , char ** happy) {

        i\

nt choice ; // 아주 극단적이지만 한 줄인 것처럼 제대로 동작합니다

    printResult2(re(1,2,3)) ; // re(1,2,3)이 그대로 찍혀 나올 겁니다.

    if( joy < 2 ) {

        puts("Usage: 프로그램이름 1 | 프로그램이름 2") ;

        return -1 ;

    } // end if


    if( (choice = atoi(happy[1])) == 1 )

        result(1) ;

    else if( choice == 2 )

        result(2) ;

    else {

        puts("Usage: 프로그램이름 1 | 프로그램이름 2") ;

        return -2 ;

    } // end else


#define myPuts(msg) printf(#msg "\n")

// 선언한 아래에서만 동작합니다.     

    my\

Puts(Sinclair) ; // 이것도 동작합니다 '\'뒤엔 주석도 사용 못합니다.

    myPuts("Sinclair") ;

    myPuts("Sin""clair") ;

    myPuts("S""i""n""c""l""a""i""r") ;

/*

    '의 사용에 주의하세요!!

    myPuts(O'Sinclair) ;          // 중간에 혼자 나오는 '는 절대 안 됩니다.

    myPuts(O\'Sinclauir) ;        // 이것도 불가능

    myPuts("O'Sinclauir") ;       // 가능

    myPuts(O''Sinclauir) ;        // 가능

*/

    return 0 ;

} // end main()  






// 선행 처리기를 거쳐 실제 컴파일 된 소스를 보면 아래와 같습니다.

// 모든 선행처리 문법이 처리되고 주석도 사라지고 없습니다.

int main(int joy , char ** happy) {

int choice ;   //<-----

printf("re(1,2,3)" " = %d\n\n" , ((1 +2 +3)*123)) ;  //<------

if( joy < 2 ) {

puts("Usage: 프로그램이름 1 | 프로그램이름 2") ;

return -1 ;

}


if( (choice = atoi(happy[1])) == 1 )

printf("exp = %d\n\n" , (3*7)) ;

else if( choice == 2 )

printf("3*7" " = %d\n\n" , (3*7)) ;

else {

puts("Usage: 프로그램이름 1 | 프로그램이름 2") ;

return -2 ;

}   

printf("Sinclair" "\n") ;

printf("\"Sinclair\"" "\n") ;

printf("\"Sin\"\"clair\"" "\n") ;

printf("\"S\"\"i\"\"n\"\"c\"\"l\"\"a\"\"i\"\"r\"" "\n") ;  

return 0 ;

}   




#error #line #pragma


#error는 주로 따옴표 없는 문장과 함께 사용하고 이 문장을 만나면 컴파일러는 무조건 컴파일을 중단하고 함께 있는 문장을 표준에러 창으로 출력합니다. 그러므로 주로 단독으로 사용하지 않고 거의 조건부 컴파일 문장과 함께 사용하게 됩니다.

#line은 이미 선언되어 있는 매크로 값 중에서 줄 번호인 __LINE__과 현재 소스 파일이름인 __FILE__의 값을 변경시키는 매크로입니다. #line 자연수 "새로운이름" 이렇게 사용합니다. 만약에 파일이름을 주지 않았을 경우 현재 파일이름이 그대로 유지되지만, 줄 번호 없이 파일이름만 적는 것은 에러입니다.

C언어가 탄생하게 된 배경에는 Multics를 대항하여 새롭게 만든 OS인 Unix가 등장합니다. 그 Unix를 설계할 목적으로 만든 언어가 바로 C 입니다. 그런데 참으로 놀라운 이 OS는 이름만 Unix(유일하다는 의미)지 실제로 다양한 종류의 유닉스가 존재하고 있습니다. 얼마나 많고 다양한지 온 몸에 용이나 호랑이 그림을 그린 행님들이 주로 사용하시는 계보까지 존재할 정도입니다. 그런데 이렇게 다양한 Unix에서 똑같은 C언어를 사용할 수 있다는 것은 실로 놀라운 일입니다. 만약에 Unix 마다 다른 C언어 문법이 존재하고 그걸 다 공부해야 하고 알아야 한다면(실제로 처음엔 컴파일러 마다 다르고 Unix 마다 다르고 막 그랬다고 하던데), C책도 성경책처럼 창세기, 출애굽기, 등등 머 이런 식으로 나왔을 겁니다. 실제로 그랬다면 저는 아마 버얼써 혀 깨물고 죽었을 지도 모릅니다. 하여간 이눔의 승질머리 하곤 쯧쯔쯔… 쑥과 마늘을 먹어도 이 모양입니다.


그런데 #pragma 이놈은 C언어의 놀라운 이식성을 제공하는 선행처리기 문법입니다. OS 마다, 컴파일러 마다 달라질 수 있는 것을 이 놈이 막아주고 있는 것입니다. 프로젝트나 프로그램이 조금만 커지게 되면 아마 이 녀석의 놀라운 파워를 여러분들이 직접 만나게 되거나 이미 만나 분들도 있을 겁니다. 다만 아쉬운 점은 C언어의 이식성은 실행 파일의 수준이 아니라 코드 수준에서 제공되는 것이므로 반드시 OS 마다 새로 컴파일을 해야 한다는 것입니다. 뿐만 아니라 #pragma는 컴파일러 마다 다른 문법을 제공하기 때문에 개발자가 꼭 읽어야 할 싸가지 중 하나인 컴파일러 레퍼런스를 참고해야 알 수 있습니다. 우리는 앞에서 구조체를 배우며 한번 다뤄 봤습니다. 

굳이 한가지 덧붙이자면 어느 정도 수준에 오르기 전에는 그다지 알 필요도 없는 녀석입니다. 그리고 사실 고백하자면 저도 이 녀석이 있는지 몰랐을 때도 프로그램 잘 짜구 잘 먹구 살았음돠. 그치만 아는 게 힘이라는 것을 사알짝 강조하겠습니다. 헉~ 대체 누굽니까? 모르는 게 약이라고 소곤거리는 사람들이… 저기, 저어기 당신들 아주 나빴어요. 쀍~ ^^;;  




선행처리기의 고마운 선물


__LINE__, __FILE__, __DATE__, __TIME__, __STDC__


모든 선행처리기 안에 이미 선언되어 있는 표준으로 정해놓은 아름다운 매크로들입니다. 컴파일시간을 기준으로 하고 있으며 순서대로 현재 줄 번호(int), 현재 파일 이름(char*), 현재 날짜(char*), 현재 시간(char*), 표준 C컴파일러로 동작하는지의 여부(1)를 알려줍니다. __STDC__가 선언되지 않았다면 표준 C컴파일러가 아닌 경우입니다.  



#include <stdio.h>  

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/  

int main() {

    printf("%s %s %s", "지금 날짜는 ", __DATE__, "입니다.\n") ;

    printf("%s %s %s" "지금 시간은 ", __TIME__, "입니다.\n") ;

    printf("%s %s %s", "파일 이름은 ", __FILE__, "입니다.\n") ;

    printf("%s %d %s", "현재 줄 번호는 ", __LINE__, "입니다.\n") ;

#if __STDC__ == 1

    puts("You are on ANSI C!!");

#else

    puts("You are not on ANSI C!!");

#endif

    return 0 ;

} // end main()  




또한 표준 C99에 포함되어 주로 디버깅 용도로 많이 사용되고 있는 __func__와 __VA_ARGS__도 있습니다. 각각 현재 함수명과 가변인자를 나타내는 ...을 통째로 전달받는 멋진 녀석들입니다. __VA_ARGS__는 가변인자 처리함수와 다르게 기준이 되는 인자 없이 그냥 사용할 수 있습니다.  



#include <stdio.h>

#include <stdarg.h>  

/*

* copyleft (l) 2006 - 2017 programmed by Sinclair

*/

#define FRMT(f,e) #e " = " f, e

#define DEBUG(...) \

            debug(__FILE__, __LINE__, __func__, __VA_ARGS__)


void debug(const char * file, int line, const char * func,

                      const char * format, ...)

{

    va_list ap;

    fprintf(stderr, "[DEBUG] file = %s, line = %d, function = %s: ",

                                                    file, line, func);

    va_start(ap, format);

    vfprintf(stderr, format, ap);

    va_end(ap);

    fprintf(stderr, "\n");

} // end debug() 


int main() {

    int value = 10 ;

    DEBUG(FRMT("%d",value)) ;

    // debug(__FILE__, __LINE__, __func__,

    //                    "value" " = " "%d", value) ;

    // [DEBUG] file = source.c, line = 26, function = main: value = 10

    return 0 ;

} // end main()   



이것들 외에 컴파일러에 따라 더 많은 선행처리기의 선물들이 있습니다. 꼭 컴파일러 레퍼런스를 참고하기 바랍니다. 지금 당장 찾아 봐야 합니다. 내일은 너무 늦습니다.



오늘날 참으로 안타까운 현실은 우리들 대부분이 놀이동산이 어디 있는지 알고 전자상가는 어디 있는지 알아도 동네 도서관이 어디 있는지 모른다는 것입니다. 간혹 위치는 알고 있다 하더라도 도서관에 일주일에 한번 이상 찾아가는 사람은 거의 없다는 것입니다.

제가 북경에 보름간 머물면서 강의했을 때 시간을 쪼개서 지도를 들고 다니며 북경 시내에 있는 대학들과 도서관을 방문했었습니다. 우리네 대학들과는 다르게 많은 사람으로 넘쳐나지만 결코 시끄럽지 않은 그곳에서 중국인들의 저력을 뼈저리게 느끼고 돌아왔습니다. 여러 해 전에 모 방송국에서 우리나라도 부족한 도서관을 지어야 한다고 온갖 난리를 피우며 책 좀 읽자고 했던 적이 있습니다. 그런데 지금은 감감 소식입니다. 벌써 필요한 만큼의 도서관 다 지었나요? 겨우 대 여섯 개 짓고 나서는 이젠 다른 얘길 합니다. 물론 시각 장애우들의 눈도 떠야 합니다. 당연히 도둑맞은 문화재도 모두 다 찾아 와야 합니다. 하지만 도서관도 짓고 책도 읽으면서 하면 안될까요? 다시 한 번 부탁 드립니다. 책을 읽읍시다. 그 속에 길이 있습니다. 진짭니다.











其次는 致曲이니 曲能有誠이니 

기차    치곡       곡능유성

誠則形하고 形則著하고 著則明하고

성즉형       형즉저       저즉명

明則動하고 動則變하고 變則化니

명즉동       동즉변       변즉화

唯天下至誠이야 爲能化니라

유천하지성       위능화


작은 일도 무시하지 않고 최선을 다해야 한다.

작은 일에도 최선을 다하면 정성스럽게 된다.

정성스럽게 되면 겉에 배어 나오고

겉에 배어 나오면 겉으로 드러나고

겉으로 드러나면 이내 밝아지고

밝아지면 남을 감동시키고

남을 감동시키면 이내 변하게 되고

변하면 생육된다.

그러니 오직 세상에서 지극히 정성을 다하는 사람만이

나와 세상을 변하게 할 수 있는 것이다.


- 2014년 영화 '역린逆鱗' 속 대사  <중용 23장>  중에서









C언어 및 기타 프로그래밍 관련 질문은 오픈 카톡으로

group talk - https://is.gd/yourc

1:1 talk - https://is.gd/aboutc

#Sinclair #씽클레어 #싱클레어 #씽클레어도씨 #씨언어 #씨프로그래밍  #C언어 #Cprogramming #C_Programming #C #Programming #Clanguage #C_Language

작품 선택
키워드 선택 0 / 3 0
댓글여부
afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari