विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ macro और conditional compilation

लेखक के बारे में:

व्यक्तिगत WeChat सार्वजनिक अकाउंट:

wechat

1.macro में विशेष symbols

कुछ प्रकार हैं: #, ##, \

1.1 stringize ऑपरेटर (#)

किसी macro के पैरामीटर के आगे # लगाने पर, preprocessor उस पैरामीटर को character array में बदल देता है, दूसरे शब्दों में: # का मतलब "stringize" है, macro परिभाषा में आया # अपने बाद वाले पैरामीटर को string में बदल देता है।

ध्यान दें: इसका उपयोग केवल ऐसे macro परिभाषाओं में हो सकता है जिनमें पैरामीटर पास होता हो, और इसे macro बॉडी में पैरामीटर के नाम से पहले रखना होगा।

उदाहरण के लिए:

#define exp(s) printf("test s is:%s\n",s)
#define exp1(s) printf("test s is:%s\n",#s)
#define exp2(s) #s 
int main() {
    exp("hello");
    exp1(hello);

    string str = exp2(   bac );
    cout<<str<<" "<<str.size()<<endl;
    /**
     * 忽略传入参数名前面和后面的空格。
     */
    string str1 = exp2( asda  bac );
    /**
     * 当传入参数名间存在空格时,编译器将会自动连接各个子字符串,
     * 用每个子字符串之间以一个空格连接,忽略剩余空格。
     */
    cout<<str1<<" "<<str1.size()<<endl;
    return 0;
}

उपरोक्त कोड बुनियादी उपयोग और space handling rules बताता है, space handling rules नीचे हैं:

  • पास किए पैरामीटर नाम के आगे-पीछे के spaces ignore होते हैं।
string str = exp2(   bac );
cout<<str<<" "<<str.size()<<endl;

आउटपुट:

bac 3
  • जब पास किए पैरामीटर नामों के बीच space हो, तो कंपाइलर सभी sub-strings को अपने-आप जोड़ देगा, हर sub-string के बीच एक space से जोड़कर, बाकी spaces ignore करके।
string str1 = exp2( asda  bac );
cout<<str1<<" "<<str1.size()<<endl;

आउटपुट:

asda bac 8

1.2 token-pasting ऑपरेटर (##)

"##" एक तरह का separator-join तरीका है, इसका काम पहले separate करना, फिर force करके join करना है। macro परिभाषा के कई formal parameters को एक actual पैरामीटर नाम में बदलता है।

ध्यान देने योग्य बातें:

(1)## से formal parameter जोड़ते समय, ## के आगे-पीछे के spaces का होना या न होना वैसा ही है।

(2)जुड़ने के बाद का actual पैरामीटर नाम, सचमुच मौजूद पैरामीटर नाम या कंपाइलर को ज्ञात macro परिभाषा होनी ही चाहिए।

(3)अगर ## के बाद का पैरामीटर खुद भी macro हो, तो ## उस macro के expansion को रोक देगा।

उदाहरण:

#define expA(s) printf("前缀加上后的字符串为:%s\n",gc_##s)  //gc_s必须存在
// 注意事项2
#define expB(s) printf("前缀加上后的字符串为:%s\n",gc_  ##  s)  //gc_s必须存在
// 注意事项1
#define gc_hello1 "I am gc_hello1"
int main() {
    // 注意事项1
    const char * gc_hello = "I am gc_hello";
    expA(hello);
    expB(hello1);
}

1.3 line continuation ऑपरेटर ()

जब परिभाषित macro एक line में पूरा न आए, तो "\" से दर्शाया जा सकता है कि अगली line इसी macro की परिभाषा जारी रखती है।

ध्यान दें, के आगे space छोड़ें।

#define MAX(a,b) ((a)>(b) ? (a) \
   :(b))  
int main() {
    int max_val = MAX(3,6);
    cout<<max_val<<endl;
}

उपरोक्त कोड देखें: sig_examp.cpp

2.do{...}while(0) का उपयोग

2.1 semantics के ग़लत समझने से बचना

उदाहरण के लिए:

#define fun() f1();f2();
if(a>0)
    fun()

यह macro expand होने पर यही बनता है:

if(a>0)
    f1();
    f2();

मक़सद था कि a>0 पर f1 f2 चले, पर वास्तव में f2 हर बार चलेगा, इसलिए यह ग़लत हो गया।

इस समस्या को हल करने के लिए, कोड लिखते समय आमतौर पर {} block उपयोग किया जा सकता है।

जैसे:

#define fun() {f1();f2();}
if(a>0)
    fun();
// 宏展开
if(a>0)
{
    f1();
    f2();
};

लेकिन दिखेगा कि उपरोक्त macro expand होने के बाद एक अतिरिक्त semicolon आ गया, वास्तविक syntax बिल्कुल सही नहीं। (भले compile और run में कोई समस्या न हो, सामान्यतः semicolon नहीं होना चाहिए)।

2.2goto control flow से बचना

कुछ फ़ंक्शनों में, हमें return statement से पहले कुछ cleanup काम करने पड़ सकते हैं, जैसे फ़ंक्शन की शुरुआत में malloc से ली गई मेमोरी खाली करना, goto हमेशा एक सरल तरीका रहा है:

int f() {
    int *p = (int *)malloc(sizeof(int));
    *p = 10; 
    cout<<*p<<endl;
#ifndef DEBUG
    int error=1;
#endif
    if(error)
        goto END;
    // dosomething
END:
    cout<<"free"<<endl;
    free(p);
    return 0;
}

लेकिन क्योंकि goto software engineering के structured सिद्धांत के अनुरूप नहीं, और कोड को समझना मुश्किल बना सकता है, इसलिए बहुत से लोग इसके उपयोग की सलाह नहीं देते, इस समय हम do{...}while(0) से वही काम कर सकते हैं:

int ff() {
    int *p = (int *)malloc(sizeof(int));
    *p = 10; 
    cout<<*p<<endl;
    do{ 
#ifndef DEBUG
        int error=1;
#endif
        if(error)
            break;
        //dosomething
    }while(0);
    cout<<"free"<<endl;
    free(p);
    return 0;
}

यहाँ फ़ंक्शन के मुख्य हिस्से को do{...}while(0) में रखा गया, goto की जगह break का उपयोग किया गया, बाद का cleanup काम while के बाद है, अब वही प्रभाव मिलता है, और कोड की पठनीयता और maintainability ऊपर वाले goto कोड से कहीं बेहतर है।

2.3 macro से उत्पन्न warnings से बचना

kernel में अलग-अलग architecture की सीमाओं के कारण, कई बार empty macro उपयोग होते हैं। compile करते समय, ये empty macro warning देते हैं, ऐसी warnings से बचने के लिए हम do{...}while(0) से empty macro परिभाषित कर सकते हैं:

#define EMPTYMICRO do{}while(0)

2.4 जटिल ऑपरेशन के लिए एक single function block परिभाषित करना

अगर आपके पास एक जटिल फ़ंक्शन है, बहुत सारे वेरिएबल्स हैं, और आप नया फ़ंक्शन नहीं जोड़ना चाहते, तो do{...}while(0) उपयोग करें, अपना कोड इसके अंदर लिखें, अंदर वेरिएबल परिभाषित किए जा सकते हैं बिना यह सोचे कि वेरिएबल का नाम फ़ंक्शन से पहले या बाद वाले से टकराएगा। यह स्थिति शायद इस बात को कहती है कि एक वेरिएबल कई जगह उपयोग हो (पर हर जगह उसका अर्थ अलग हो), हम हर do-while में scope सीमित कर सकते हैं, जैसे:

int fc()
{
    int k1 = 10;
    cout<<k1<<endl;
    do{
        int k1 = 100;
        cout<<k1<<endl;
    }while(0);
    cout<<k1<<endl;
}

उपरोक्त कोड देखें: do_while.cpp

सीखने का लेख:https://www.cnblogs.com/lizhenghn/p/3674430.html

संबंधित विषय

टिप्पणियाँ