मैक्रो में जाहिरा तौर पर निरर्थक का उपयोग क्यों करते हैं और यदि-और बयान क्यों?


787

कई C / C ++ मैक्रोज़ में मैं मैक्रो का कोड देख रहा हूं जो अर्थहीन do whileलूप की तरह दिखता है । यहाँ उदाहरण हैं।

#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) { f(X); g(X); } else

मैं नहीं देख सकता कि क्या do whileकर रहा है। इसके बिना सिर्फ इसे क्यों नहीं लिखा जाए?

#define FOO(X) f(X); g(X)

2
दूसरे के साथ उदाहरण के लिए, मैं voidअंत में टाइप की अभिव्यक्ति जोड़ूंगा ... जैसे ((शून्य) 0)
फिल 1970

1
याद दिलाएं कि do whileनिर्माण रिटर्न स्टेटमेंट के अनुकूल नहीं है, इसलिए if (1) { ... } else ((void)0)निर्माण में मानक सी में अधिक संगत उपयोग हैं और जीएनयू सी में, आप मेरे उत्तर में वर्णित निर्माण को पसंद करेंगे।
कूर

जवाबों:


829

do ... whileऔर if ... elseयह इतना है कि अपने मैक्रो के बाद अर्धविराम हमेशा एक ही बात मतलब है कि बनाने के लिए कर रहे हैं। मान लीजिए कि आपके पास अपने दूसरे मैक्रो जैसा कुछ है।

#define BAR(X) f(x); g(x)

अब यदि आप BAR(X);एक if ... elseबयान में उपयोग करना चाहते हैं , जहां यदि विवरण के शरीर घुंघराले कोष्ठक में लिपटे हुए नहीं थे, तो आपको एक बुरा आश्चर्य होगा।

if (corge)
  BAR(corge);
else
  gralt();

उपरोक्त कोड में विस्तार होगा

if (corge)
  f(corge); g(corge);
else
  gralt();

जो वाक्यात्मक रूप से गलत है, क्योंकि अन्य अब के साथ संबद्ध नहीं है। यह मैक्रो के भीतर घुंघराले ब्रेसिज़ में चीजों को लपेटने में मदद नहीं करता है, क्योंकि ब्रेसिज़ के बाद एक अर्धविराम वाक्यगत रूप से गलत है।

if (corge)
  {f(corge); g(corge);};
else
  gralt();

समस्या को ठीक करने के दो तरीके हैं। पहला यह है कि मैक्रो के भीतर स्टेटमेंट को स्टेट करने के लिए एक कॉमा का उपयोग बिना उसकी अभिव्यक्ति के कार्य करने की क्षमता को लूटे बिना किया जाता है।

#define BAR(X) f(X), g(X)

बार का उपर्युक्त संस्करण BARउपरोक्त कोड को इस प्रकार विस्तारित करता है, जो कि वाक्यविन्यास रूप से सही है।

if (corge)
  f(corge), g(corge);
else
  gralt();

यह काम नहीं करता है यदि आपके बजाय f(X)कोड का एक अधिक जटिल निकाय है जिसे अपने स्वयं के ब्लॉक में जाने की आवश्यकता है, उदाहरण के लिए स्थानीय चर घोषित करने के लिए। सबसे सामान्य मामले में समाधान कुछ का उपयोग करने के लिए है जैसे do ... whileमैक्रो को एक एकल बयान के लिए उपयोग किया जाता है जो भ्रम के बिना अर्धविराम लेता है।

#define BAR(X) do { \
  int i = f(X); \
  if (i > 4) g(i); \
} while (0)

आपको उपयोग करने की आवश्यकता नहीं है do ... while, आप कुछ के साथ भी खाना बना सकते हैं if ... else, हालांकि जब इसके if ... elseअंदर फैलता if ... elseहै तो एक " झूलने वाला" होता है , जो मौजूदा झूलने की समस्या को और भी कठिन बना सकता है, जैसा कि निम्नलिखित कोड में है। ।

if (corge)
  if (1) { f(corge); g(corge); } else;
else
  gralt();

बिंदु संदर्भों में अर्धविराम का उपयोग करना है जहां एक झूलने वाला अर्धविराम गलत है। बेशक, यह (और शायद यह होना चाहिए) इस बिंदु पर तर्क दिया जा सकता है कि BARवास्तविक कार्य के रूप में घोषित करना बेहतर होगा , न कि मैक्रो।

सारांश में, do ... whileसी प्रीप्रोसेसर की कमियों के आसपास काम करने के लिए है। जब वे C स्टाइल गाइड आपको C प्रीप्रोसेसर को बंद करने के लिए कहते हैं, तो इस तरह की बात वे करते हैं।


18
यह हमेशा के लिए, जबकि और बयानों में ब्रेसिज़ का उपयोग करने के लिए एक मजबूत तर्क नहीं है? यदि आप हमेशा ऐसा करने का एक बिंदु बनाते हैं (जैसा कि मिसरा-सी के लिए आवश्यक है, उदाहरण के लिए), ऊपर वर्णित समस्या दूर हो जाती है।
स्टीव मेलनिकॉफ़

17
अल्पविराम उदाहरण होना चाहिए #define BAR(X) (f(X), g(X))अन्यथा ऑपरेटर पूर्वता शब्दार्थ को गड़बड़ कर सकता है।
स्टीवर्ट

20
@DawidFerenczy: हालांकि आप और मैं-दोनों साढ़े चार साल पहले एक अच्छी बात करते हैं, हमें वास्तविक दुनिया में रहना होगा। जब तक हम यह गारंटी नहीं दे सकते कि ifहमारे कोड में सभी स्टेटमेंट इत्यादि ब्रेसिज़ का उपयोग करते हैं, तब मैक्रोज़ को इस तरह लपेटना समस्याओं से बचने का एक सरल तरीका है।
स्टीव मेलनिकॉफ

8
नोट: if(1) {...} else void(0)फ़ॉर्म do {...} while(0)मैक्रोज़ के लिए अधिक सुरक्षित है, जिसके पैरामीटर कोड हैं जो मैक्रो विस्तार में शामिल हैं, क्योंकि यह ब्रेक के व्यवहार में परिवर्तन नहीं करता है या कीवर्ड जारी रखता है। उदाहरण के लिए: for (int i = 0; i < max; ++i) { MYMACRO( SomeFunc(i)==true, {break;} ) }अप्रत्याशित व्यवहार का कारण बनता है जब MYMACROपरिभाषित किया जाता है #define MYMACRO(X, CODE) do { if (X) { cout << #X << endl; {CODE}; } } while (0)क्योंकि ब्रेक मैक्रो कॉल साइट पर लूप के बजाय मैक्रो के लूप को प्रभावित करता है।
क्रिस क्लाइन

5
@ace void(0)एक टाइपो था, मेरा मतलब था (void)0। और मेरा मानना ​​है कि यह "झूलने की समस्या" को हल करता है: ध्यान दें कि इसके बाद कोई अर्धविराम नहीं है (void)0। उस मामले में एक और झूलना (जैसे if (cond) if (1) foo() else (void)0 else { /* dangling else body */ }) एक संकलन त्रुटि को ट्रिगर करता है। यहां इसका एक जीवंत उदाहरण प्रदर्शित किया गया है
क्रिस क्लाइन

153

मैक्रोज़ पाठ के कॉपी / पेस्ट किए गए टुकड़े हैं जो पूर्व-प्रोसेसर वास्तविक कोड में डाल देंगे; मैक्रो के लेखक को उम्मीद है कि प्रतिस्थापन वैध कोड का उत्पादन करेगा।

इसमें सफल होने के लिए तीन अच्छे "टिप्स" हैं:

मैक्रो को वास्तविक कोड की तरह व्यवहार करने में मदद करें

सामान्य कोड आमतौर पर एक अर्ध-उपनिवेश द्वारा समाप्त होता है। क्या यूजर व्यू कोड की जरूरत नहीं है ...

doSomething(1) ;
DO_SOMETHING_ELSE(2)  // <== Hey? What's this?
doSomethingElseAgain(3) ;

इसका मतलब है कि उपयोगकर्ता कंपाइलर से यह उम्मीद करता है कि यदि सेमी-कोलोन अनुपस्थित है, तो वह त्रुटि उत्पन्न कर सकता है।

लेकिन वास्तविक वास्तविक अच्छा कारण यह है कि किसी समय, मैक्रो के लेखक को संभवतः मैक्रो को वास्तविक फ़ंक्शन (शायद इनलेट) के साथ बदलने की आवश्यकता होगी। तो मैक्रो को वास्तव में एक जैसा व्यवहार करना चाहिए ।

इसलिए हमें अर्ध-बृहदान्त्र की जरूरत है।

एक वैध कोड का निर्माण करें

जैसा कि jfm3 के उत्तर में दिखाया गया है, कभी-कभी मैक्रो में एक से अधिक निर्देश होते हैं। और अगर मैक्रो का उपयोग एक इफ स्टेटमेंट के अंदर किया जाता है, तो यह समस्याग्रस्त होगा:

if(bIsOk)
   MY_MACRO(42) ;

इस मैक्रो का विस्तार इस प्रकार किया जा सकता है:

#define MY_MACRO(x) f(x) ; g(x)

if(bIsOk)
   f(42) ; g(42) ; // was MY_MACRO(42) ;

gफंक्शन का मान की परवाह किए बिना निष्पादित किया जाएगा bIsOk

इसका मतलब है कि हमें स्थूल में एक गुंजाइश जोड़ना होगा:

#define MY_MACRO(x) { f(x) ; g(x) ; }

if(bIsOk)
   { f(42) ; g(42) ; } ; // was MY_MACRO(42) ;

एक मान्य कोड 2 का निर्माण करें

यदि मैक्रो कुछ इस तरह है:

#define MY_MACRO(x) int i = x + 1 ; f(i) ;

हमें निम्नलिखित कोड में एक और समस्या हो सकती है:

void doSomething()
{
    int i = 25 ;
    MY_MACRO(32) ;
}

क्योंकि यह विस्तार होगा:

void doSomething()
{
    int i = 25 ;
    int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}

यह कोड निश्चित रूप से संकलन नहीं करेगा। तो, फिर से, समाधान एक गुंजाइश का उपयोग कर रहा है:

#define MY_MACRO(x) { int i = x + 1 ; f(i) ; }

void doSomething()
{
    int i = 25 ;
    { int i = 32 + 1 ; f(i) ; } ; // was MY_MACRO(32) ;
}

कोड फिर से सही व्यवहार करता है।

अर्ध-कोलन + स्कोप इफेक्ट्स का संयोजन?

एक C / C ++ मुहावरा है जो इस प्रभाव को उत्पन्न करता है: Do / जबकि लूप:

do
{
    // code
}
while(false) ;

ऐसा करने से गुंजाइश बन सकती है, इस प्रकार मैक्रो का कोड एनकैप्सुलेट करता है, और अंत में एक अर्ध-बृहदान्त्र की आवश्यकता होती है, इस प्रकार कोड की आवश्यकता होती है।

बोनस?

C ++ कंपाइलर डू / लूप को ऑप्टिमाइज़ करेगा, क्योंकि तथ्य के बाद की स्थिति झूठी है, जिसे संकलन समय पर जाना जाता है। इसका मतलब है कि एक मैक्रो की तरह:

#define MY_MACRO(x)                                  \
do                                                   \
{                                                    \
    const int i = x + 1 ;                            \
    f(i) ; g(i) ;                                    \
}                                                    \
while(false)

void doSomething(bool bIsOk)
{
   int i = 25 ;

   if(bIsOk)
      MY_MACRO(42) ;

   // Etc.
}

के रूप में सही ढंग से विस्तार होगा

void doSomething(bool bIsOk)
{
   int i = 25 ;

   if(bIsOk)
      do
      {
         const int i = 42 + 1 ; // was MY_MACRO(42) ;
         f(i) ; g(i) ;
      }
      while(false) ;

   // Etc.
}

और फिर संकलित और अनुकूलित के रूप में दूर है

void doSomething(bool bIsOk)
{
   int i = 25 ;

   if(bIsOk)
   {
      f(43) ; g(43) ;
   }

   // Etc.
}

6
ध्यान दें कि इनलाइन समारोह बदलती जाती है के लिए मैक्रो को बदलने कुछ मानक पूर्व-निर्धारित मैक्रो, जैसे निम्नलिखित कोड शो में एक परिवर्तन फंक्शन और लाइन : # शामिल <stdio.h> #define Fmacro () printf ( "% s% d \ n", फंक्शन , लाइन ) इनलाइन शून्य फ़िनलाइन () {प्रिंटफ़ ("% s% d \ n", फंक्शन , लाइन ); } int main () {Fmacro (); Finline (); वापसी 0; } (बोल्ड शब्दों को डबल अंडरस्कोर - खराब
फॉर्मेटर

6
इस उत्तर के साथ कई छोटे लेकिन पूरी तरह से असंगत मुद्दे नहीं हैं। उदाहरण के लिए: void doSomething() { int i = 25 ; { int i = x + 1 ; f(i) ; } ; // was MY_MACRO(32) ; }सही विस्तार नहीं है; xविस्तार में होना चाहिए 32. एक अधिक जटिल मुद्दे के विस्तार है क्या है MY_MACRO(i+7)। और दूसरा विस्तार है MY_MACRO(0x07 << 6)। वहाँ बहुत कुछ है जो अच्छा है, लेकिन वहाँ कुछ है जो मैं और अनारक्षित टी है।
जोनाथन लेफ़लर

@Gnubie: मेरा मामला है कि आप अभी भी यहां हैं और आपने अब तक इसका पता नहीं लगाया है: आप बैकस्लैश के साथ टिप्पणियों में तारांकन और अंडरस्कोर से बच सकते हैं, इसलिए यदि आप \_\_LINE\_\_इसे टाइप करते हैं तो यह __LINE__ के रूप में प्रस्तुत करता है। IMHO, कोड के लिए कोड स्वरूपण का उपयोग करना बेहतर है; उदाहरण के लिए, __LINE__(जिसे किसी विशेष हैंडलिंग की आवश्यकता नहीं है)। पीएस मुझे नहीं पता कि क्या यह 2012 में सच था; वे तब से इंजन में काफी सुधार कर चुके हैं।
स्कॉट

1
सराहना करते हुए कि मेरी टिप्पणी छह साल देर से है, लेकिन अधिकांश सी संकलक वास्तव में इनलाइन inlineकार्य नहीं करते हैं (मानक के अनुसार अनुमति दी गई है)
एंड्रयू

53

@ jfm3 - आपके पास सवाल का एक अच्छा जवाब है। आप यह भी जोड़ना चाह सकते हैं कि मैक्रो मुहावरा संभवतः अधिक खतरनाक को रोकता है (क्योंकि कोई त्रुटि नहीं है) सरल 'अगर' कथन के साथ अनपेक्षित व्यवहार:

#define FOO(x)  f(x); g(x)

if (test) FOO( baz);

इसका विस्तार:

if (test) f(baz); g(baz);

जो संकलित रूप से सही है, इसलिए कोई संकलक त्रुटि नहीं है, लेकिन संभवतः अनपेक्षित परिणाम है कि जी () को हमेशा कहा जाएगा।


22
"शायद अनायास ही"? मैंने कहा होगा "निश्चित रूप से अनजाने में", अन्यथा प्रोग्रामर को बाहर निकालने और गोली मारने की आवश्यकता है (जैसा कि एक कोड़ा के साथ गोल करने का विरोध किया गया)।
लॉरेंस Dol

4
या उन्हें एक बढ़ा-चढ़ाकर दिया जा सकता है, अगर वे तीन-अक्षर वाली एजेंसी के लिए काम कर रहे हों और उस कोड को व्यापक रूप से इस्तेमाल होने वाले ओपन सोर्स प्रोग्राम में सम्मिलित कर रहे हों ... :-)
R .. GitHub STOP HELPING ICE

2
और यह टिप्पणी हाल ही में Apple OS में पाए गए SSL सर्टिफिकेशन वेरिफिकेशन बग में गोटो फेल लाइन की याद दिलाती है
गेरार्ड सेक्सटन

23

उपरोक्त उत्तर इन निर्माणों का अर्थ समझाते हैं, लेकिन दोनों के बीच एक महत्वपूर्ण अंतर है जिसका उल्लेख नहीं किया गया था। वास्तव में, पसंद करने का एक कारण हैdo ... while करने के लिए if ... elseनिर्माण।

if ... elseनिर्माण की समस्या यह है कि यह आपको अर्धविराम लगाने के लिए मजबूर नहीं करता है । इस कोड की तरह:

FOO(1)
printf("abc");

यद्यपि हमने अर्धविराम (गलती से) छोड़ दिया, कोड का विस्तार होगा

if (1) { f(X); g(X); } else
printf("abc");

और चुपचाप संकलन करेंगे (हालांकि कुछ संकलक अगम्य कोड के लिए चेतावनी जारी कर सकते हैं)। लेकिन वोprintf बयान पर कभी अमल नहीं किया जाएगा।

do ... whileनिर्माण में ऐसी समस्या नहीं है, क्योंकि while(0)अर्धविराम के बाद एकमात्र वैध टोकन है।


2
@ रीचर्डहेनसेन: फिर भी उतना अच्छा नहीं है, क्योंकि मैक्रो इनवोकेशन पर नज़र डालने से आपको पता नहीं चलता है कि यह एक बयान या एक अभिव्यक्ति तक फैलता है या नहीं। यदि कोई बाद में मान लेता है, तो वह लिख सकता है FOO(1),x++;जो फिर से हमें एक झूठी सकारात्मक देगा। बस उपयोग करें do ... whileऔर वह यह है।
याकोव गल्का

1
गलतफहमी से बचने के लिए मैक्रो का दस्तावेजीकरण पर्याप्त होना चाहिए। मैं सहमत हूं कि do ... while (0)यह बेहतर है, लेकिन इसका एक नकारात्मक पहलू है: ए breakया लूप continueको नियंत्रित करेगा do ... while (0), न कि मैक्रो इनवोकेशन वाले लूप को। तो ifचाल अभी भी मूल्य है।
रिचर्ड हेन्सन

2
मैं नहीं देखता कि आप कहाँ डाल सकते हैं breakया continueवह आपके मैक्रोज़ do {...} while(0)छद्म-पाश के अंदर दिखाई देगा । मैक्रो पैरामीटर में भी यह एक सिंटैक्स त्रुटि करेगा।
पैट्रिक श्लुटर

5
निर्माण के do { ... } while(0)बजाय उपयोग करने का एक अन्य कारण if whatever, इसकी मुहावरेदार प्रकृति है। do {...} while(0)निर्माण बड़े पैमाने पर, अच्छी तरह से जाना जाता है और कई प्रोग्रामर द्वारा एक बहुत प्रयोग किया जाता है। इसका औचित्य और प्रलेखन आसानी से ज्ञात है। ifनिर्माण के लिए ऐसा नहीं है । इसलिए कोड की समीक्षा करते समय यह कम करने का प्रयास करता है।
पैट्रिक शाल्टर

1
@tristopia: मैंने देखा है कि लोग मैक्रोज़ लिखते हैं जो कोड के ब्लॉक को तर्क के रूप में लेते हैं (जो मैं जरूरी नहीं सुझाता)। उदाहरण के लिए #define CHECK(call, onerr) if (0 != (call)) { onerr } else (void)0:। इसका उपयोग इस तरह किया जा सकता है CHECK(system("foo"), break;);, जहाँ break;पर CHECK()इनवोकेशन को संलग्न करने वाले लूप को संदर्भित करना है ।
रिचर्ड हैनसेन

16

हालांकि यह उम्मीद की जाती है कि कंपाइलर do { ... } while(false);छोरों को दूर करते हैं , एक और समाधान है जिसे उस निर्माण की आवश्यकता नहीं होगी। समाधान कॉमा ऑपरेटर का उपयोग करना है:

#define FOO(X) (f(X),g(X))

या इससे भी अधिक:

#define FOO(X) g((f(X),(X)))

हालांकि यह अलग-अलग निर्देशों के साथ अच्छी तरह से काम करेगा, यह उन मामलों के साथ काम नहीं करेगा जहां चर का निर्माण किया जाता है और इसका उपयोग किया जाता है #define:

#define FOO(X) (int s=5,f((X)+s),g((X)+s))

इसके साथ किसी को भी निर्माण करते समय / का उपयोग करने के लिए मजबूर किया जाएगा।


धन्यवाद, चूंकि अल्पविराम ऑपरेटर निष्पादन आदेश की गारंटी नहीं देता है, इसलिए यह घोंसले को लागू करने का एक तरीका है।
मारियस

15
@ मार्स: गलत; अल्पविराम ऑपरेटर एक दृश्य है और इस तरह से करता है गारंटी निष्पादन आदेश। मुझे संदेह है कि आपने इसे फ़ंक्शन तर्क सूचियों में अल्पविराम के साथ भ्रमित कर दिया है।
R .. गिटहब स्टॉप हेल्पिंग ICE

2
दूसरे विदेशी सुझाव ने मेरा दिन बना दिया।
स्पाइडी

बस यह जोड़ना चाहते थे कि कंपाइलरों को प्रोग्राम के अवलोकन योग्य व्यवहार को संरक्षित करने के लिए मजबूर किया जाता है, इसलिए ऐसा करने के लिए / जबकि दूर एक बड़ी बात नहीं है (यह मानते हुए कि कंपाइलर अनुकूलन सही हैं)।
मार्को ए।

@MarcoA। जब आप सही होते हैं, तो मैंने अतीत में पाया है कि कंपाइलर ऑप्टिमाइज़ेशन, जबकि कोड के फ़ंक्शन को बिल्कुल संरक्षित करता है, लेकिन लाइनों के चारों ओर बदलकर जो एकवचन संदर्भ में कुछ भी नहीं लगता है, मल्टीथ्रेडेड एल्गोरिदम को तोड़ देगा। बिंदु में प्रकरण Peterson's Algorithm
मारियस

11

Jens Gustedt's P99 प्रीप्रोसेसर लाइब्रेरी (हाँ, यह तथ्य कि इस तरह की चीज़ मेरे दिमाग में भी मौजूद है!) if(1) { ... } elseनिम्नलिखित को परिभाषित करके एक छोटे लेकिन महत्वपूर्ण तरीके से निर्माण पर सुधार करती है :

#define P99_NOP ((void)0)
#define P99_PREFER(...) if (1) { __VA_ARGS__ } else
#define P99_BLOCK(...) P99_PREFER(__VA_ARGS__) P99_NOP

इसके लिए तर्क यह है कि, do { ... } while(0)निर्माण के विपरीत , breakऔर continueअभी भी दिए गए ब्लॉक के अंदर काम करते हैं, लेकिन ((void)0)अगर सिकोलॉन को मैक्रो कॉल के बाद छोड़ा जाता है, तो एक सिंटैक्स त्रुटि बनाता है, जो अन्यथा अगले ब्लॉक को छोड़ देगा। (वास्तव में यहाँ "झूलने" की समस्या नहीं है, क्योंकि elseयह निकटतम हैif , जो कि मैक्रो में एक है।)

यदि आप उन चीजों के प्रकार में रुचि रखते हैं जो सी प्रीप्रोसेसर के साथ अधिक-या-कम सुरक्षित रूप से किए जा सकते हैं, तो उस लाइब्रेरी को देखें।


जबकि बहुत चालाक, यह संभावित खतरों पर कंपाइलर चेतावनियों के साथ बमबारी करने का कारण बनता है।
खंडित

1
आप आम तौर पर एक निहित वातावरण बनाने के लिए मैक्रोज़ का उपयोग करते हैं, अर्थात, आप एक लूप को नियंत्रित करने के लिए मैक्रो के अंदर कभी भी break(या continue) का उपयोग नहीं करते हैं जो कि शुरू हुआ / बाहर समाप्त हुआ, यह सिर्फ खराब शैली है और संभावित निकास बिंदुओं को छुपाता है।
mirabilos

बूस्ट में एक प्रीप्रोसेसर लाइब्रेरी भी है। इसके बारे में क्या दिमाग है?
रेने

इसके साथ जोखिम else ((void)0)यह है कि कोई व्यक्ति लिख रहा है YOUR_MACRO(), f();और यह वाक्य-रचना के लिए मान्य होगा, लेकिन कभी कॉल न करें f()। इसके साथ do whileएक सिंटैक्स त्रुटि है।
melpomene

@melpomene तो किस बारे में else do; while (0)?
कार्ल लेई

8

कुछ कारणों से मैं पहले उत्तर पर टिप्पणी नहीं कर सकता ...

आप में से कुछ ने स्थानीय चर के साथ मैक्रोज़ दिखाए, लेकिन किसी ने उल्लेख नहीं किया कि आप किसी मैक्रो में किसी भी नाम का उपयोग नहीं कर सकते हैं! यह किसी दिन उपयोगकर्ता को काटेगा! क्यों? क्योंकि इनपुट तर्कों को आपके मैक्रो टेम्पलेट में प्रतिस्थापित किया जाता है। और अपने मैक्रो उदाहरणों में आप शायद सबसे अधिक इस्तेमाल किया जाने वाला चर नाम i का उपयोग करते हैं ।

उदाहरण के लिए जब निम्नलिखित मैक्रो

#define FOO(X) do { int i; for (i = 0; i < (X); ++i) do_something(i); } while (0)

निम्नलिखित फ़ंक्शन में उपयोग किया जाता है

void some_func(void) {
    int i;
    for (i = 0; i < 10; ++i)
        FOO(i);
}

मैक्रो इच्छित वैरिएबल i का उपयोग नहीं करेगा, जो कि some_func की शुरुआत में घोषित किया गया है, लेकिन स्थानीय वैरिएबल, जो कि घोषित किया जाता है ... जबकि मैक्रो के लूप।

इस प्रकार, एक मैक्रो में आम चर नामों का उपयोग कभी न करें!


सामान्य पैटर्न उदाहरण के लिए मैक्रोज़ में चर नामों में अंडरस्कोर जोड़ना है int __i;
Blaisorblade

8
@ ब्लेज़रब्लड: वास्तव में यह गलत और अवैध सी है; कार्यान्वयन द्वारा अग्रणी अंडरस्कोर उपयोग के लिए आरक्षित हैं। आपके द्वारा इस "सामान्य पैटर्न" को देखने का कारण रीडिंग सिस्टम हेडर ("कार्यान्वयन") से है जिसे स्वयं को इस आरक्षित नाम स्थान पर सीमित करना चाहिए। अनुप्रयोगों / पुस्तकालयों के लिए, आपको अपने स्वयं के अस्पष्ट, बिना-टकराने वाले नामों को बिना अंडरस्कोर, उदा mylib_internal___iया समान के चुनना चाहिए ।
R .. गिटहब स्टॉप हेल्पिंग ICE

2
र एक '' होस्ट '' एक) की।
ब्लेसरॉब्लेड

3
@R .. यह बिल्कुल सही नहीं है: एक पूंजी या दूसरे अंडरस्कोर के बाद के प्रमुख अंडरस्कोर सभी संदर्भों में कार्यान्वयन के लिए आरक्षित हैं। कुछ और के बाद अंडरस्कोर करना स्थानीय क्षेत्र में आरक्षित नहीं है।
लेउशेंको

@ लीशेंको: हाँ, लेकिन यह भेद पर्याप्त रूप से सूक्ष्म है कि मुझे लोगों को यह बताना सबसे अच्छा लगता है कि हम ऐसे नामों का उपयोग न करें। जो लोग सूक्ष्मता को समझते हैं, वे पहले से ही जानते हैं कि मैं विवरणों पर चर्चा कर रहा हूं। :-)
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

6

व्याख्या

do {} while (0)और if (1) {} elseयह सुनिश्चित करने के लिए कि मैक्रो का विस्तार केवल 1 निर्देश तक किया जाता है। अन्यथा:

if (something)
  FOO(X); 

इसका विस्तार होगा:

if (something)
  f(X); g(X); 

और नियंत्रण कथन के g(X)बाहर निष्पादित किया जाएगा if। यह प्रयोग करने से बचा जाता है do {} while (0)और if (1) {} else


बेहतर विकल्प

GNU स्टेटमेंट एक्सप्रेशन (मानक C का हिस्सा नहीं) के साथ, आपके पास इसका उपयोग करने से बेहतर तरीका है do {} while (0)और if (1) {} elseइसे हल करना है ({}):

#define FOO(X) ({f(X); g(X);})

और यह सिंटैक्स रिटर्न मानों (नोट do {} while (0)नहीं है) के साथ संगत है, जैसा कि:

return FOO("X");

3
मैक्रो में ब्लॉक-क्लैम्पिंग {} का उपयोग मैक्रो कोड को बंडल करने के लिए पर्याप्त होगा ताकि सभी को एक ही इफ़-कंडीशन पथ के लिए निष्पादित किया जाए। इधर-उधर का उपयोग तब होता है जब मैक्रो उपयोग किए जाने वाले स्थानों पर अर्धविराम लगाने के लिए उपयोग किया जाता है। इस प्रकार मैक्रो को अधिक फ़ंक्शन समान रूप से व्यवहार करते हुए लागू किया जाता है। इसका उपयोग किए जाने पर अनुगामी अर्धविराम के लिए आवश्यकता शामिल है।
अलेक्जेंडर स्टोह्र

2

मुझे नहीं लगता कि यह उल्लेख किया गया था इसलिए इस पर विचार करें

while(i<100)
  FOO(i++);

में अनुवादित किया जाएगा

while(i<100)
  do { f(i++); g(i++); } while (0)

ध्यान दें कि i++मैक्रो द्वारा दो बार मूल्यांकन कैसे किया जाता है। इससे कुछ दिलचस्प त्रुटियां हो सकती हैं।


15
यह करने के लिए कुछ भी नहीं करना है ... जबकि (0) निर्माण।
ट्रेंट

2
सच। लेकिन मैक्रोज़ बनाम कार्यों के विषय पर फिर से ध्यान दें और एक मैक्रो लिखने के लिए जो एक फ़ंक्शन के रूप में व्यवहार करता है ...
जॉन निल्सन

2
उपरोक्त एक के समान, यह एक उत्तर नहीं है, बल्कि एक टिप्पणी है। विषय पर: इसीलिए आप केवल एक बार सामान का उपयोग करें:do { int macroname_i = (i); f(macroname_i); g(macroname_i); } while (/* CONSTCOND */ 0)
mirabilos
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.