मैक्रो बनाम फंक्शन सी में


101

मैंने हमेशा उदाहरणों और मामलों को देखा, जहां मैक्रो का उपयोग फ़ंक्शन का उपयोग करने से बेहतर है।

किसी ने मुझे एक समारोह की तुलना में एक मैक्रो का नुकसान उदाहरण के साथ समझा सकता है?


21
प्रश्न को उसके सिर पर घुमाएँ। किस स्थिति में एक मैक्रो बेहतर है? एक वास्तविक फ़ंक्शन का उपयोग करें जब तक आप यह प्रदर्शित नहीं कर सकते कि एक मैक्रो बेहतर है।
डेविड हेफर्नन

जवाबों:


113

मैक्रोज़ त्रुटि-प्रवण हैं, क्योंकि वे टेक्स्ट प्रतिस्थापन पर भरोसा करते हैं और टाइप-चेकिंग नहीं करते हैं। उदाहरण के लिए, यह मैक्रो:

#define square(a) a * a

पूर्णांक के साथ उपयोग किए जाने पर ठीक काम करता है:

square(5) --> 5 * 5 --> 25

लेकिन बहुत अजीब बातें करता है जब अभिव्यक्ति के साथ प्रयोग किया जाता है:

square(1 + 2) --> 1 + 2 * 1 + 2 --> 1 + 2 + 2 --> 5
square(x++) --> x++ * x++ --> increments x twice

तर्कों के चारों ओर कोष्ठक लगाने से मदद मिलती है, लेकिन इन समस्याओं को पूरी तरह से समाप्त नहीं करता है।

जब मैक्रोज़ में कई स्टेटमेंट होते हैं, तो आप कंट्रोल-फ्लो के निर्माण में परेशानी में पड़ सकते हैं:

#define swap(x, y) t = x; x = y; y = t;

if (x < y) swap(x, y); -->
if (x < y) t = x; x = y; y = t; --> if (x < y) { t = x; } x = y; y = t;

इसे ठीक करने की सामान्य रणनीति यह है कि बयानों को "do {...} जबकि (0)" लूप के अंदर रखा जाए।

यदि आपके पास दो संरचनाएं हैं जो एक ही नाम के साथ एक फ़ील्ड में होती हैं लेकिन अलग-अलग शब्दार्थ हैं, तो एक ही मैक्रो दोनों पर काम कर सकता है, अजीब स्थिति के साथ:

struct shirt 
{
    int numButtons;
};

struct webpage 
{
    int numButtons;
};

#define num_button_holes(shirt)  ((shirt).numButtons * 4)

struct webpage page;
page.numButtons = 2;
num_button_holes(page) -> 8

अंत में, मैक्रोज़ को डीबग करना मुश्किल हो सकता है, अजीब सिंटैक्स त्रुटियों या रनटाइम त्रुटियों का निर्माण करना, जिन्हें आपको समझने के लिए विस्तार करना होगा (जैसे कि जीसीई-ई के साथ), क्योंकि डीबगर इस उदाहरण में मैक्रोज़ के माध्यम से कदम नहीं उठा सकते हैं:

#define print(x, y)  printf(x y)  /* accidentally forgot comma */
print("foo %s", "bar") /* prints "foo %sbar" */

इनलाइन कार्यों और स्थिरांक मैक्रो के साथ इन समस्याओं में से कई से बचने में मदद करते हैं, लेकिन हमेशा लागू नहीं होते हैं। जहां मैक्रोज़ का उपयोग जानबूझकर बहुरूपतापूर्ण व्यवहार को निर्दिष्ट करने के लिए किया जाता है, अनजाने में बहुरूपता से बचना मुश्किल हो सकता है। C ++ में कई विशेषताएं हैं जैसे कि मैक्रोज़ के उपयोग के बिना एक प्रकार से जटिल पॉलीमॉर्फिक निर्माणों को बनाने में मदद करने के लिए टेम्पलेट्स; विवरण के लिए स्ट्रॉस्ट्रुप की C ++ प्रोग्रामिंग भाषा देखें।


43
C ++ विज्ञापन के साथ क्या है?
पचेरियर

4
सहमत, यह एक सी सवाल है, पूर्वाग्रह जोड़ने की कोई जरूरत नहीं है।
ideasman42

16
C ++ C का एक विस्तार है, जो (अन्य बातों के अलावा) सी की इस विशिष्ट सीमा को संबोधित करने के उद्देश्य से सुविधाएँ हैं, मैं C ++ का प्रशंसक नहीं हूं, लेकिन मुझे लगता है कि यह यहाँ विषय है।
22

1
मैक्रोज़, इनलाइन फ़ंक्शंस, और टेम्प्लेट का उपयोग अक्सर प्रदर्शन को बढ़ावा देने के प्रयास में किया जाता है। वे अत्यधिक उपयोग किए जाते हैं, और कोड ब्लोट के कारण प्रदर्शन को नुकसान पहुंचाते हैं, जो सीपीयू अनुदेश कैश की प्रभावशीलता को कम करता है। हम इन तकनीकों का उपयोग किए बिना सी में तेजी से सामान्य डेटा संरचनाएं बना सकते हैं।
सैम वाटकिंस

1
आईएसओ / आईईसी 9899: 1999 .16.5.1 के अनुसार, "पिछले और अगले अनुक्रम बिंदु के बीच किसी वस्तु का एक अभिव्यक्ति के मूल्यांकन द्वारा एक बार में इसका संग्रहीत मूल्य संशोधित किया जाएगा।" (पिछले और बाद के सी मानकों में भी इसी तरह का शब्दांकन मौजूद है।) इसलिए अभिव्यक्ति x++*x++को xदो बार बढ़ाने के लिए नहीं कहा जा सकता है ; यह वास्तव में अपरिभाषित व्यवहार का आह्वान करता है , जिसका अर्थ है कि संकलक ऐसा कुछ भी करने के लिए स्वतंत्र है - जो xदो बार वृद्धि कर सकता है , या एक बार, या बिल्कुल नहीं; यह एक त्रुटि के साथ गर्भपात कर सकता है या यहां तक कि राक्षसों को आपकी नाक से बाहर निकाल सकता है
साइकोनॉट

39

मैक्रो विशेषताएं :

  • मैक्रो प्रीप्रोसेस्ड है
  • कोई प्रकार की जाँच नहीं
  • कोड की लंबाई बढ़ जाती है
  • मैक्रो के उपयोग से साइड इफेक्ट हो सकता है
  • निष्पादन की गति तेज़ है
  • संकलन से पहले मैक्रो नाम को मैक्रो मान से बदल दिया जाता है
  • उपयोगी जहां छोटे कोड कई बार दिखाई देते हैं
  • मैक्रो कंपाइलर एरर्स की जांच नहीं करता है

समारोह विशेषताएं :

  • समारोह संकलित है
  • टाइप चेकिंग पूरी हो गई है
  • कोड की लंबाई समान है
  • कोई साइड इफेक्ट नहीं
  • निष्पादन की गति धीमी है
  • फंक्शन कॉल के दौरान, ट्रांसफर ऑफ कंट्रोल होता है
  • उपयोगी जहां बड़े कोड कई बार दिखाई देते हैं
  • फंक्शन चेक कम्पाइलर एरर्स

2
"निष्पादन की गति तेज है" संदर्भ की आवश्यकता है। पिछले दशक के किसी भी कुछ सक्षम संकलक इनलाइन कार्यों को ठीक कर देगा, अगर यह सोचता है कि यह प्रदर्शन लाभ प्रदान करेगा।
Voo

1
क्या निम्न-स्तरीय MCU (AVRs, यानी ATMega32) कंप्यूटिंग के संदर्भ में, मैक्रोज़ बेहतर विकल्प हैं, क्योंकि वे कॉल स्टैक नहीं बढ़ते हैं, जैसे फ़ंक्शन कॉल करते हैं?
hardyVeles 21

1
@hardyVeles ऐसा नहीं है। कंपाइलर, यहां तक ​​कि एक AVR के लिए, इनलाइन कोड को बहुत समझदारी से कर सकते हैं। यहाँ एक उदाहरण है: godbolt.org/z/Ic21iM
एडवर्ड

33

दुष्परिणाम एक बड़ा है। यहाँ एक विशिष्ट मामला है:

#define min(a, b) (a < b ? a : b)

min(x++, y)

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

(x++ < y ? x++ : y)

xएक ही बयान में दो बार बढ़ जाता है। (और अपरिभाषित व्यवहार)


मल्टी-लाइन मैक्रोज़ लिखना भी एक दर्द है:

#define foo(a,b,c)  \
    a += 10;        \
    b += 10;        \
    c += 10;

उन्हें \प्रत्येक पंक्ति के अंत में आवश्यकता होती है ।


मैक्रों तब तक कुछ भी "वापस" नहीं कर सकते जब तक आप इसे एक एकल अभिव्यक्ति नहीं बनाते हैं:

int foo(int *a, int *b){
    side_effect0();
    side_effect1();
    return a[0] + b[0];
}

जब तक आप GCC के अभिव्यक्ति कथन का उपयोग नहीं करते तब तक मैक्रो में ऐसा नहीं कर सकते। (संपादित करें: आप एक अल्पविराम ऑपरेटर का उपयोग कर सकते हैं ... यह अनदेखी की गई है ... लेकिन यह अभी भी कम पठनीय हो सकता है।)


संचालन का क्रम: (@ouah के सौजन्य से)

#define min(a,b) (a < b ? a : b)

min(x & 0xFF, 42)

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

(x & 0xFF < 42 ? x & 0xFF : 42)

लेकिन &पहले की तुलना में कम है <। तो 0xFF < 42पहले मूल्यांकन किया जाता है।


5
और स्थूल तर्कों के साथ कोष्ठक को स्थूल परिभाषा में नहीं रखने से पूर्ववर्ती मुद्दे हो सकते हैं: उदाहरण के लिए,min(a & 0xFF, 42)
ouah

आह येस। जब मैं पोस्ट अपडेट कर रहा था तब आपकी टिप्पणी नहीं देखी। मुझे लगता है कि मैं उसका भी उल्लेख करूंगा।
23

14

उदाहरण 1:

#define SQUARE(x) ((x)*(x))

int main() {
  int x = 2;
  int y = SQUARE(x++); // Undefined behavior even though it doesn't look 
                       // like it here
  return 0;
}

जहाँ तक:

int square(int x) {
  return x * x;
}

int main() {
  int x = 2;
  int y = square(x++); // fine
  return 0;
}

उदाहरण 2:

struct foo {
  int bar;
};

#define GET_BAR(f) ((f)->bar)

int main() {
  struct foo f;
  int a = GET_BAR(&f); // fine
  int b = GET_BAR(&a); // error, but the message won't make much sense unless you
                       // know what the macro does
  return 0;
}

की तुलना में:

struct foo {
  int bar;
};

int get_bar(struct foo *f) {
  return f->bar;
}

int main() {
  struct foo f;
  int a = get_bar(&f); // fine
  int b = get_bar(&a); // error, but compiler complains about passing int* where 
                       // struct foo* should be given
  return 0;
}

13

जब संदेह हो, तो फ़ंक्शंस (या इनलाइन फ़ंक्शंस) का उपयोग करें।

हालाँकि यहाँ उत्तर ज्यादातर मैक्रोज़ के साथ समस्याओं की व्याख्या करते हैं, इसके बजाय कुछ सरल दृष्टिकोण है कि मैक्रोज़ बुराई हैं क्योंकि मूर्खतापूर्ण दुर्घटनाएं संभव हैं।
आप नुकसान से अवगत हो सकते हैं और उनसे बचना सीख सकते हैं। तब मैक्रोज़ का उपयोग करें जब कोई अच्छा कारण हो।

कुछ विशिष्ट मामले हैं जहां मैक्रोज़ का उपयोग करने के फायदे हैं, इनमें शामिल हैं:

  • जेनेरिक फ़ंक्शन, जैसा कि नीचे उल्लेख किया गया है, आपके पास एक मैक्रो हो सकता है जिसका उपयोग विभिन्न प्रकार के इनपुट तर्कों पर किया जा सकता है।
  • विभिन्न प्रकार के तर्क C के उपयोग के बजाय विभिन्न कार्यों के लिए मैप कर सकते हैं va_args
    उदाहरण के लिए: https://stackoverflow.com/a/24837037/432509
  • वे कर सकते हैं वैकल्पिक रूप से इस तरह के डिबग तार के रूप में स्थानीय जानकारी, शामिल हैं:
    ( __FILE__, __LINE__, __func__)। पूर्व / पोस्ट की स्थिति के लिए जाँच करें, assertविफलता पर, या यहां तक ​​कि स्थैतिक-जोर देता है ताकि कोड अनुचित उपयोग (ज्यादातर डिबग बिल्ड के लिए उपयोगी) पर संकलित न हो।
  • इनपुट आर्ग्स का निरीक्षण करें, आप इनपुट आर्ग्स पर परीक्षण कर सकते हैं जैसे कि उनके प्रकार, आकार की जाँच, चेक structसदस्य कास्टिंग से पहले मौजूद हैं
    (पॉलीमॉर्फिक प्रकार के लिए उपयोगी हो सकते हैं)
    या एक सरणी की जाँच करें कुछ लंबाई की स्थिति से मिलता है।
    देखें: https://stackoverflow.com/a/29926435/432509
  • हालांकि इसका उल्लेख है कि फ़ंक्शंस चेकिंग टाइप करते हैं, सी मूल्यों को बहुत अधिक (उदाहरण के लिए ints / फ़्लोट्स) ले जाएगा। दुर्लभ मामलों में यह समस्याग्रस्त हो सकता है। मैक्रोज़ लिखना संभव है जो अधिक सटीक हैं फिर उनके इनपुट आर्ग के बारे में एक फ़ंक्शन। देखें: https://stackoverflow.com/a/25988779/432509
  • कार्यों के लिए रैपर के रूप में उनका उपयोग, कुछ मामलों में आप खुद को दोहराने से बचना चाहते हैं, उदाहरण के लिए ... func(FOO, "FOO");, आप एक मैक्रो को परिभाषित कर सकते हैं जो आपके लिए स्ट्रिंग का विस्तार करता है।func_wrapper(FOO);
  • जब आप कॉलर्स स्थानीय स्कोप में वैरिएबल में हेरफेर करना चाहते हैं, तो एक पॉइंटर को पॉइंटर पास करना सामान्य रूप से ठीक काम करता है, लेकिन कुछ मामलों में अभी भी मैक्रो का उपयोग करने के लिए इसकी कम परेशानी।
    (प्रति-पिक्सेल संचालन के लिए कई चर में असाइनमेंट, एक उदाहरण है जिसे आप फ़ंक्शन पर मैक्रो पसंद कर सकते हैं ... हालांकि यह अभी भी संदर्भ पर बहुत अधिक निर्भर करता है, क्योंकि inlineफ़ंक्शन एक विकल्प हो सकता है)

बेशक, इनमें से कुछ कंपाइलर एक्सटेंशन पर भरोसा करते हैं, जो मानक सी नहीं हैं। मतलब कि आप कम पोर्टेबल कोड के साथ समाप्त हो सकते हैं, या ifdefउनमें हो सकते हैं, इसलिए वे केवल लाभ उठाते हैं जब कंपाइलर समर्थन करता है।


एकाधिक तर्क तात्कालिकता से बचना

मैक्रोज़ में त्रुटियों के सबसे आम कारणों में से एक के बाद से यह नोट करना ( x++उदाहरण के लिए गुजरना , जहां एक मैक्रो कई बार बढ़ सकता है)

मैक्रोज़ को लिखना संभव है जो तर्कों के कई पलणों के साथ दुष्प्रभावों से बचते हैं।

C11 सामान्य

अगर आपको squareमैक्रो पसंद है जो विभिन्न प्रकारों के साथ काम करता है और C11 समर्थन है, तो आप ऐसा कर सकते हैं ...

inline float           _square_fl(float a) { return a * a; }
inline double          _square_dbl(float a) { return a * a; }
inline int             _square_i(int a) { return a * a; }
inline unsigned int    _square_ui(unsigned int a) { return a * a; }
inline short           _square_s(short a) { return a * a; }
inline unsigned short  _square_us(unsigned short a) { return a * a; }
/* ... long, char ... etc */

#define square(a)                        \
    _Generic((a),                        \
        float:          _square_fl(a),   \
        double:         _square_dbl(a),  \
        int:            _square_i(a),    \
        unsigned int:   _square_ui(a),   \
        short:          _square_s(a),    \
        unsigned short: _square_us(a))

कथन के भाव

यह GCC, Clang, EKOPath & Intel C ++ (लेकिन MSVC नहीं) द्वारा समर्थित एक कंपाइलर एक्सटेंशन है ;

#define square(a_) __extension__ ({  \
    typeof(a_) a = (a_); \
    (a * a); })

तो मैक्रोज़ के साथ नुकसान यह है कि आपको इनका उपयोग करने के लिए जानने की आवश्यकता है, और वे व्यापक रूप से समर्थित नहीं हैं।

एक लाभ यह है, इस मामले में, आप squareकई अलग-अलग प्रकारों के लिए एक ही फ़ंक्शन का उपयोग कर सकते हैं ।


1
"... व्यापक रूप से समर्थित है .." मैंने कहा कि आपने जो कथन अभिव्यक्ति का उल्लेख किया है वह cl.exe द्वारा समर्थित नहीं है? (एमएस के कंपाइलर)
गिदोन

1
@gideon, सही संपादित उत्तर, हालांकि उल्लेखित प्रत्येक सुविधा के लिए, यह सुनिश्चित करना आवश्यक नहीं है कि कुछ संकलक-सुविधा-समर्थन मैट्रिक्स है।
विचारमेन .42

12

किसी भी प्रकार के मापदंडों और कोड को दोहराया नहीं जाता है जिससे कोड ब्लोट हो सकता है। मैक्रो सिंटैक्स किसी भी प्रकार के अजीब किनारे के मामलों को जन्म दे सकता है जहां अर्ध-कॉलोन या पूर्वता के आदेश रास्ते में मिल सकते हैं। यहाँ एक कड़ी है जो कुछ स्थूल बुराई को प्रदर्शित करती है


6

मैक्रोज़ के लिए एक दोष यह है कि डिबगर्स स्रोत कोड पढ़ते हैं, जिसमें मैक्रोज़ का विस्तार नहीं होता है, इसलिए मैक्रो में डीबगर चलाना आवश्यक नहीं है। कहने की जरूरत नहीं है, आप एक मैक्रो के अंदर एक ब्रेकपॉइंट सेट नहीं कर सकते हैं जैसे आप कार्यों के साथ कर सकते हैं।


ब्रेकपॉइंट यहां एक बहुत महत्वपूर्ण सौदा है, इसे इंगित करने के लिए धन्यवाद।
हंस

6

कार्य प्रकार की जाँच करते हैं। यह आपको सुरक्षा की एक अतिरिक्त परत प्रदान करता है।


6

इस उत्तर में जोड़ना ।।

मैक्रोज़ को सीधे प्रोग्राम में प्रीप्रोसेसर द्वारा प्रतिस्थापित किया जाता है (क्योंकि वे मूल रूप से प्रीप्रोसेसर निर्देश हैं)। इसलिए वे अनिवार्य रूप से संबंधित फ़ंक्शन की तुलना में अधिक मेमोरी स्पेस का उपयोग करते हैं। दूसरी ओर, एक फ़ंक्शन को कॉल करने और परिणाम वापस करने के लिए अधिक समय की आवश्यकता होती है, और मैक्रोज़ का उपयोग करके इस ओवरहेड से बचा जा सकता है।

इसके अलावा मैक्रोज़ के पास कुछ विशेष उपकरण हैं जो विभिन्न प्लेटफार्मों पर प्रोग्राम पोर्टेबिलिटी की मदद कर सकते हैं।

Macros को फ़ंक्शन के विपरीत अपने तर्कों के लिए डेटा प्रकार असाइन करने की आवश्यकता नहीं है।

कुल मिलाकर वे प्रोग्रामिंग में एक उपयोगी उपकरण हैं। और मैक्रोइन्स्ट्रक्शंस और फ़ंक्शंस दोनों का उपयोग परिस्थितियों के आधार पर किया जा सकता है।


3

मैंने ऊपर दिए गए उत्तरों में, मैक्रोज़ पर किए गए फ़ंक्शंस का एक फायदा, जो मुझे लगता है कि बहुत महत्वपूर्ण है, पर ध्यान नहीं दिया:

कार्यों को तर्क के रूप में पारित किया जा सकता है, मैक्रोज़ नहीं कर सकते।

ठोस उदाहरण: आप मानक 'strpbrk' फ़ंक्शन का एक वैकल्पिक संस्करण लिखना चाहते हैं, जो किसी अन्य स्ट्रिंग के लिए खोज करने के लिए वर्णों की एक स्पष्ट सूची के बजाय स्वीकार करेगा, एक (सूचक) एक फ़ंक्शन के लिए 0 जो किसी वर्ण के होने तक वापस आ जाएगा पाया कि कुछ परीक्षा (उपयोगकर्ता द्वारा परिभाषित) पास करता है। ऐसा करने का एक कारण यह भी हो सकता है कि आप अन्य मानक पुस्तकालय कार्यों का दोहन कर सकते हैं: विराम चिह्न से भरा एक स्पष्ट स्ट्रिंग प्रदान करने के बजाय, आप इसके बजाय ctype.h का 'ispunct' पास कर सकते हैं, आदि यदि 'ispunct' केवल के रूप में लागू किया गया था। एक मैक्रो, यह काम नहीं करेगा।

अन्य कई उदाहरण हैं। उदाहरण के लिए, यदि आपकी तुलना फ़ंक्शन के बजाय मैक्रो द्वारा पूरी की जाती है, तो आप इसे stdlib.h के 'qsort' में पास नहीं कर सकते।

पायथन में एक अनुरूप स्थिति संस्करण 2 बनाम संस्करण 3 (गैर-निष्क्रिय बयान बनाम निष्क्रिय फ़ंक्शन) में 'प्रिंट' है।


1
इस उत्तर के लिए धन्यवाद
Kyrol

1

यदि आप फ़ंक्शन को मैक्रो के तर्क के रूप में पास करते हैं तो इसका मूल्यांकन हर बार किया जाएगा। उदाहरण के लिए, यदि आप सबसे लोकप्रिय मैक्रो में से एक कहते हैं:

#define MIN(a,b) ((a)<(b) ? (a) : (b))

उसके जैसा

int min = MIN(functionThatTakeLongTime(1),functionThatTakeLongTime(2));

functionThatTakeLongTime का मूल्यांकन 5 बार किया जाएगा जो कि पूर्णता को गिरा सकता है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.