C Language
प्रीप्रोसेसर और मैक्रोज़
खोज…
परिचय
सभी पूर्वप्रक्रमक कमांड हैश (पाउंड) प्रतीक #
। AC मैक्रो सिर्फ एक प्रीप्रोसेसर कमांड है जिसे #define
प्रीप्रोसेसर निर्देश का उपयोग करके परिभाषित किया गया है। प्रीप्रोसेसिंग चरण के दौरान, सी प्रीप्रोसेसर (सी कंपाइलर का एक हिस्सा) बस मैक्रो के शरीर को प्रतिस्थापित करता है जहां भी इसका नाम दिखाई देता है।
टिप्पणियों
जब एक संकलक कोड में एक मैक्रो का सामना करता है, तो यह सरल स्ट्रिंग प्रतिस्थापन करता है, कोई अतिरिक्त संचालन नहीं किया जाता है। इस वजह से, प्रीप्रोसेसर द्वारा परिवर्तन सी कार्यक्रमों के दायरे का सम्मान नहीं करते हैं - उदाहरण के लिए, एक स्थूल परिभाषा एक ब्लॉक के भीतर होने तक सीमित नहीं है, इसलिए एक '}'
से अप्रभावित है जो एक ब्लॉक स्टेटमेंट को समाप्त करता है।
प्रीप्रोसेसर डिजाइन द्वारा होता है, पूर्ण रूप से ट्यूरिंग नहीं - कई प्रकार की संगणना होती है जो केवल प्रीप्रोसेसर द्वारा नहीं की जा सकती है।
आमतौर पर कंपाइलरों में एक कमांड लाइन फ्लैग (या कॉन्फ़िगरेशन सेटिंग) होती है जो हमें प्रीप्रोसेसिंग चरण के बाद संकलन बंद करने और परिणाम का निरीक्षण करने की अनुमति देती है। POSIX प्लेटफार्मों पर यह ध्वज है -E
। इसलिए, इस ध्वज के साथ gcc चलाना stdout में विस्तारित स्रोत को प्रिंट करता है:
$ gcc -E cprog.c
अक्सर प्रीप्रोसेसर को एक अलग प्रोग्राम के रूप में लागू किया जाता है, जिसे कंपाइलर द्वारा लागू किया जाता है, उस प्रोग्राम का सामान्य नाम cpp
। कई पूर्वप्रक्रमक समर्थन सूचना का उत्सर्जन करते हैं, जैसे कि पंक्ति संख्या के बारे में जानकारी - जिसका उपयोग डिबगिंग जानकारी उत्पन्न करने के लिए संकलन के बाद के चरणों द्वारा किया जाता है। मामले में प्रीप्रोसेसर जीसीसी पर आधारित होता है, -पी विकल्प ऐसी जानकारी को दबा देता है।
$ cpp -P cprog.c
सशर्त समावेशन और सशर्त फ़ंक्शन हस्ताक्षर संशोधन
सशर्त कोड का एक खंड को शामिल करने के प्रीप्रोसेसर (जैसे कई निर्देशों है #if
, #ifdef
, #else
, #endif
, आदि)।
/* Defines a conditional `printf` macro, which only prints if `DEBUG`
* has been defined
*/
#ifdef DEBUG
#define DLOG(x) (printf(x))
#else
#define DLOG(x)
#endif
#if
स्थिति के लिए सामान्य सी रिलेशनल ऑपरेटरों का उपयोग किया जा सकता है
#if __STDC_VERSION__ >= 201112L
/* Do stuff for C11 or higher */
#elif __STDC_VERSION__ >= 199901L
/* Do stuff for C99 */
#else
/* Do stuff for pre C99 */
#endif
if
कथन में #if
निर्देश सी के समान व्यवहार करता है, तो इसमें केवल अभिन्न स्थिर भाव होंगे, और कोई जाति नहीं। यह एक अतिरिक्त एकल ऑपरेटर, का समर्थन करता है defined( identifier )
है, जो देता है 1
अगर पहचानकर्ता परिभाषित किया गया है, और 0
अन्यथा।
#if defined(DEBUG) && !defined(QUIET)
#define DLOG(x) (printf(x))
#else
#define DLOG(x)
#endif
सशर्त समारोह हस्ताक्षर संशोधन
ज्यादातर मामलों में किसी एप्लिकेशन के रिलीज़ बिल्ड में कम से कम ओवरहेड होने की उम्मीद की जाती है। हालांकि एक अंतरिम बिल्ड के परीक्षण के दौरान, अतिरिक्त लॉग और पाई गई समस्याओं के बारे में जानकारी सहायक हो सकती है।
उदाहरण के लिए मान लें कि कुछ फ़ंक्शन SHORT SerOpPluAllRead(PLUIF *pPif, USHORT usLockHnd)
जो टेस्ट बिल्ड बनाते समय वांछित है कि इसके उपयोग के बारे में एक लॉग उत्पन्न होगा। हालाँकि इस फ़ंक्शन का उपयोग कई स्थानों पर किया जाता है और यह वांछित है कि लॉग बनाते समय, जानकारी का हिस्सा यह जानना है कि फ़ंक्शन कहाँ से कहा जा रहा है।
तो सशर्त संकलन का उपयोग करके आप फ़ंक्शन को घोषित करने वाली शामिल फ़ाइल में निम्नलिखित जैसे कुछ हो सकते हैं। यह फ़ंक्शन के मानक संस्करण को फ़ंक्शन के डिबग संस्करण के साथ बदल देता है। प्रीप्रोसेसर का उपयोग फ़ंक्शन को कॉल को बदलने के लिए किया जाता है SerOpPluAllRead()
फ़ंक्शन के कॉल के साथ SerOpPluAllRead_Debug()
दो अतिरिक्त तर्कों के साथ, फ़ाइल का नाम और फ़ंक्शन का लाइन नंबर जहां फ़ंक्शन का उपयोग किया जाता है।
सशर्त संकलन का उपयोग यह चुनने के लिए किया जाता है कि डिबग संस्करण के साथ मानक फ़ंक्शन को ओवरराइड करना है या नहीं।
#if 0
// function declaration and prototype for our debug version of the function.
SHORT SerOpPluAllRead_Debug(PLUIF *pPif, USHORT usLockHnd, char *aszFilePath, int nLineNo);
// macro definition to replace function call using old name with debug function with additional arguments.
#define SerOpPluAllRead(pPif,usLock) SerOpPluAllRead_Debug(pPif,usLock,__FILE__,__LINE__)
#else
// standard function declaration that is normally used with builds.
SHORT SerOpPluAllRead(PLUIF *pPif, USHORT usLockHnd);
#endif
यह आपको फ़ंक्शन के मानक संस्करण SerOpPluAllRead()
को एक संस्करण के साथ ओवरराइड करने की अनुमति देता है जो उस फ़ाइल का नाम और लाइन नंबर प्रदान करेगा जहां फ़ंक्शन कहा जाता है।
एक महत्वपूर्ण विचार है: इस फ़ंक्शन का उपयोग करने वाली किसी भी फ़ाइल में हेडर फ़ाइल शामिल होनी चाहिए जहां फ़ंक्शन को संशोधित करने के लिए प्रीप्रोसेसर के लिए इस दृष्टिकोण का उपयोग किया जाता है। अन्यथा आप एक लिंकर त्रुटि देखेंगे।
फ़ंक्शन की परिभाषा निम्नलिखित की तरह दिखाई देगी। क्या इस स्रोत करता अनुरोध है कि पूर्वप्रक्रमक का नाम बदलने के कार्य करने के लिए है SerOpPluAllRead()
होने के लिए SerOpPluAllRead_Debug()
और दो अतिरिक्त तर्क, फ़ाइल का नाम जहां समारोह बुलाया गया था करने के लिए एक सूचक और लाइन नंबर शामिल करने के लिए तर्क सूची को संशोधित करने के जिस फ़ाइल में फ़ंक्शन का उपयोग किया जाता है।
#if defined(SerOpPluAllRead)
// forward declare the replacement function which we will call once we create our log.
SHORT SerOpPluAllRead_Special(PLUIF *pPif, USHORT usLockHnd);
SHORT SerOpPluAllRead_Debug(PLUIF *pPif, USHORT usLockHnd, char *aszFilePath, int nLineNo)
{
int iLen = 0;
char xBuffer[256];
// only print the last 30 characters of the file name to shorten the logs.
iLen = strlen (aszFilePath);
if (iLen > 30) {
iLen = iLen - 30;
}
else {
iLen = 0;
}
sprintf (xBuffer, "SerOpPluAllRead_Debug(): husHandle = %d, File %s, lineno = %d", pPif->husHandle, aszFilePath + iLen, nLineNo);
IssueDebugLog(xBuffer);
// now that we have issued the log, continue with standard processing.
return SerOpPluAllRead_Special(pPif, usLockHnd);
}
// our special replacement function name for when we are generating logs.
SHORT SerOpPluAllRead_Special(PLUIF *pPif, USHORT usLockHnd)
#else
// standard, normal function name (signature) that is replaced with our debug version.
SHORT SerOpPluAllRead(PLUIF *pPif, USHORT usLockHnd)
#endif
{
if (STUB_SELF == SstReadAsMaster()) {
return OpPluAllRead(pPif, usLockHnd);
}
return OP_NOT_MASTER;
}
स्रोत फ़ाइल शामिल
#include
प्रीप्रोसेसिंग निर्देशों का सबसे आम उपयोग निम्नानुसार हैं:
#include <stdio.h>
#include "myheader.h"
#include
कथन को संदर्भित की गई फ़ाइल की सामग्री से बदल देता है। कोण कोष्ठक (<>) सिस्टम पर स्थापित हेडर फ़ाइलों को संदर्भित करता है, जबकि उद्धरण चिह्न ("") उपयोगकर्ता द्वारा आपूर्ति की गई फ़ाइलों के लिए हैं।
मैक्रोज़ स्वयं एक बार अन्य मैक्रो का विस्तार कर सकते हैं, क्योंकि यह उदाहरण दिखाता है:
#if VERSION == 1
#define INCFILE "vers1.h"
#elif VERSION == 2
#define INCFILE "vers2.h"
/* and so on */
#else
#define INCFILE "versN.h"
#endif
/* ... */
#include INCFILE
मैक्रो रिप्लेसमेंट
मैक्रो रिप्लेसमेंट का सबसे सरल रूप एक manifest constant
को परिभाषित करना manifest constant
, जैसे कि
#define ARRSIZE 100
int array[ARRSIZE];
यह एक फ़ंक्शन-जैसे मैक्रो को परिभाषित करता है जो एक चर को 10
गुणा करता है और नए मूल्य को संग्रहीत करता है:
#define TIMES10(A) ((A) *= 10)
double b = 34;
int c = 23;
TIMES10(b); // good: ((b) *= 10);
TIMES10(c); // good: ((c) *= 10);
TIMES10(5); // bad: ((5) *= 10);
प्रतिस्थापन कार्यक्रम पाठ की किसी भी अन्य व्याख्या से पहले किया जाता है। TIMES10
के लिए पहली कॉल में परिभाषा से A
को b
से बदल दिया जाता है और फिर कॉल के स्थान पर इतना विस्तृत पाठ डाला जाता है। ध्यान दें कि TIMES10
की यह परिभाषा इसके समकक्ष नहीं है
#define TIMES10(A) ((A) = (A) * 10)
क्योंकि यह A
के प्रतिस्थापन का मूल्यांकन कर सकता है, दो बार, जिसके अवांछित दुष्प्रभाव हो सकते हैं।
निम्नलिखित एक फ़ंक्शन-जैसे मैक्रो को परिभाषित करता है, जो इसके तर्कों का अधिकतम मूल्य है। यह किसी भी संगत प्रकार के तर्कों के लिए काम करने और फंक्शन कॉलिंग के ओवरहेड के बिना इन-लाइन कोड जनरेट करने के फायदे हैं। इसके एक या दूसरे तर्कों के दूसरी बार (साइड इफेक्ट सहित) का मूल्यांकन करने और एक फ़ंक्शन से अधिक कोड उत्पन्न करने का नुकसान होता है यदि कई बार आह्वान किया गया हो।
#define max(a, b) ((a) > (b) ? (a) : (b))
int maxVal = max(11, 43); /* 43 */
int maxValExpr = max(11 + 36, 51 - 7); /* 47 */
/* Should not be done, due to expression being evaluated twice */
int j = 0, i = 0;
int sideEffect = max(++i, ++j); /* i == 4 */
इस वजह से, ऐसे मैक्रो जो कई बार अपने तर्कों का मूल्यांकन करते हैं, आमतौर पर उत्पादन कोड से बचा जाता है। C11 के बाद से _Generic
फीचर है जो ऐसे कई _Generic
से बचने की अनुमति देता है।
मैक्रो विस्तार (परिभाषा के दाईं ओर) में प्रचुर कोष्ठक सुनिश्चित करते हैं कि तर्क और परिणामी अभिव्यक्ति ठीक से बंधे हैं और उस संदर्भ में अच्छी तरह से फिट होते हैं जिसमें मैक्रो कहा जाता है।
निर्देश में त्रुटि
यदि प्रीप्रोसेसर एक #error
निर्देश का सामना करता है, तो संकलन रोक दिया जाता है और इसमें शामिल नैदानिक संदेश मुद्रित होता है।
#define DEBUG
#ifdef DEBUG
#error "Debug Builds Not Supported"
#endif
int main(void) {
return 0;
}
संभावित उत्पादन:
$ gcc error.c
error.c: error: #error "Debug Builds Not Supported"
#if 0 कोड अनुभागों को ब्लॉक करने के लिए
यदि कोड के ऐसे भाग हैं जिन्हें आप हटाने पर विचार कर रहे हैं या अस्थायी रूप से अक्षम करना चाहते हैं, तो आप इसे ब्लॉक टिप्पणी के साथ टिप्पणी कर सकते हैं।
/* Block comment around whole function to keep it from getting used.
* What's even the purpose of this function?
int myUnusedFunction(void)
{
int i = 5;
return i;
}
*/
हालाँकि, यदि आपने जिस ब्लॉक कोड को ब्लॉक कमेंट से घेर रखा है, उस सोर्स में ब्लॉक स्टाइल कमेंट्स हैं, तो मौजूदा ब्लॉक कॉमेंट्स की समाप्ति * आपकी नई ब्लॉक कमेंट को अमान्य बना सकती है और संकलन समस्याओं का कारण बन सकती है।
/* Block comment around whole function to keep it from getting used.
* What's even the purpose of this function?
int myUnusedFunction(void)
{
int i = 5;
/* Return 5 */
return i;
}
*/
पिछले उदाहरण में, फ़ंक्शन की अंतिम दो पंक्तियों और अंतिम '* /' को संकलक द्वारा देखा जाता है, इसलिए यह त्रुटियों के लिए संकलन करेगा। एक सुरक्षित तरीका उस कोड के चारों ओर #if 0
निर्देश का उपयोग करना है जिसे आप ब्लॉक करना चाहते हैं।
#if 0
/* #if 0 evaluates to false, so everything between here and the #endif are
* removed by the preprocessor. */
int myUnusedFunction(void)
{
int i = 5;
return i;
}
#endif
इसके साथ एक लाभ यह है कि जब आप वापस जाना चाहते हैं और कोड ढूंढना चाहते हैं, तो अपनी सभी टिप्पणियों को खोजने के बजाय "#if 0" की खोज करना बहुत आसान है।
एक और बहुत महत्वपूर्ण लाभ यह है कि आप #if 0
के साथ कोड बाहर टिप्पणी कर सकते हैं। यह टिप्पणियों के साथ नहीं किया जा सकता है।
#if 0
का उपयोग करने का एक विकल्प एक ऐसे नाम का उपयोग करना है जो #defined
नहीं होगा, लेकिन अधिक वर्णनात्मक है कि कोड क्यों अवरुद्ध किया जा रहा है। उदाहरण के लिए, यदि कोई फ़ंक्शन है जो बेकार मृत कोड प्रतीत होता है तो आप #if defined(POSSIBLE_DEAD_CODE)
या #if defined(FUTURE_CODE_REL_020201)
कोड का उपयोग कर सकते हैं एक बार अन्य कार्यक्षमता की आवश्यकता होती है। फिर जब उस स्रोत को हटाने या सक्षम करने के लिए वापस जा रहे हैं, तो स्रोत के उन वर्गों को ढूंढना आसान है।
टोकन चिपकाना
टोकन चिपकाने से दो मैक्रो तर्कों को एक साथ गोंद करने की अनुमति मिलती है। उदाहरण के लिए, front##back
से frontback
। एक प्रसिद्ध उदाहरण Win32 का <TCHAR.H>
हैडर है। मानक सी में, एक विस्तृत चरित्र स्ट्रिंग घोषित करने के लिए L"string"
लिख सकता है। हालाँकि, विंडोज एपीआई एक विस्तृत चरित्र तार और संकीर्ण चरित्र तार के बीच में परिवर्तित करने के लिए अनुमति देता है बस #define
द्वारा UNICODE
। स्ट्रिंग TCHAR.H
को लागू करने के लिए, TCHAR.H
उपयोग करता है
#ifdef UNICODE
#define TEXT(x) L##x
#endif
जब भी कोई उपयोगकर्ता TEXT("hello, world")
लिखता है, और UNICODE को परिभाषित किया जाता है, तो C प्रीप्रोसेसर L
और मैक्रो तर्क को समेट लेता है। L
साथ concatenated "hello, world"
देता L"hello, world"
।
पूर्वनिर्धारित मैक्रों
एक पूर्वनिर्धारित मैक्रो एक मैक्रो है जो पहले से ही सी पूर्व प्रोसेसर द्वारा समझा जाता है कार्यक्रम को परिभाषित करने की आवश्यकता के बिना। उदाहरणों में शामिल
अनिवार्य पूर्व निर्धारित मैक्रोज़
-
__FILE__
, जो वर्तमान स्रोत फ़ाइल (एक स्ट्रिंग शाब्दिक) का फ़ाइल नाम देता है, - वर्तमान लाइन संख्या (पूर्णांक स्थिर) के लिए
__LINE__
, - संकलन तिथि (एक स्ट्रिंग शाब्दिक) के लिए
__DATE__
, - संकलन समय (एक स्ट्रिंग शाब्दिक) के लिए
__TIME__
।
एक संबंधित पूर्वनिर्धारित पहचानकर्ता भी है, __func__
(ISO / IEC 9899: 2011 §6.4.2.2), जो एक मैक्रो नहीं है:
पहचानकर्ता
__func__
को अनुवादक द्वारा स्पष्ट रूप से घोषित किया जाएगा जैसे, प्रत्येक फ़ंक्शन परिभाषा के उद्घाटन ब्रेस के तुरंत बाद, घोषणा:static const char __func__[] = "function-name";
दिखाई दिया, जहाँ फ़ंक्शन-नाम lexically-enclosing फ़ंक्शन का नाम है।
डिबगिंग उद्देश्यों के लिए __FILE__
, __LINE__
और __func__
विशेष रूप से उपयोगी हैं। उदाहरण के लिए:
fprintf(stderr, "%s: %s: %d: Denominator is 0", __FILE__, __func__, __LINE__);
प्री- C99 कंपाइलर, __func__
कर सकते हैं या नहीं __func__
कर सकते हैं या इसमें एक मैक्रो हो सकता है जो समान रूप से नामित हो। उदाहरण के लिए, C89 मोड में __FUNCTION__
का उपयोग किया गया।
नीचे मैक्रोज़ कार्यान्वयन पर विस्तार से पूछने की अनुमति देते हैं:
-
__STDC_VERSION__
C मानक का संस्करण कार्यान्वित किया गया। इस प्रारूप का उपयोग कर पूर्णांक एक निरंतर हैyyyymmL
(मूल्य201112L
C11, मूल्य के लिए199901L
C99 के लिए, यह C89 / C90 के लिए निर्धारित नहीं किया गया था) -
__STDC_HOSTED__
1
यदि यह एक होस्ट किया गया कार्यान्वयन है, तो0
। -
__STDC__
यदि1
, कार्यान्वयन C मानक के अनुरूप है।
अन्य पूर्व निर्धारित मैक्रोज़ (गैर अनिवार्य)
आईएसओ / आईईसी 9899: 2011 §6.10.9.2 पर्यावरण मैक्रो:
__STDC_ISO_10646__
फार्म की एक पूर्णांक लगातारyyyymmL
(उदाहरण के लिए, 199712L)। यदि इस प्रतीक को परिभाषित किया जाता है, तो यूनिकोड में प्रत्येक वर्ण को आवश्यक सेट, जब टाइपwchar_t
ऑब्जेक्ट में संग्रहीत किया जाता है, उस वर्ण के लघु पहचानकर्ता के समान मान होता है। यूनिकोड आवश्यक सेट में वे सभी वर्ण शामिल हैं जो आईएसओ / आईईसी 10646 द्वारा परिभाषित हैं, साथ ही सभी संशोधनों और तकनीकी गलियारे के साथ, निर्दिष्ट वर्ष और महीने के रूप में। यदि कुछ अन्य एन्कोडिंग का उपयोग किया जाता है, तो मैक्रो को परिभाषित नहीं किया जाएगा और उपयोग की जाने वाली वास्तविक एन्कोडिंग कार्यान्वयन-परिभाषित है।
__STDC_MB_MIGHT_NEQ_WC__
पूर्णांक स्थिरांक 1, जो इंगित करता है कि,wchar_t
लिए एन्कोडिंग में, मूल वर्ण सेट के सदस्य को पूर्णांक वर्ण स्थिरांक में लोन वर्ण के रूप में उपयोग किए जाने पर इसके मान के बराबर कोड मान की आवश्यकता नहीं है।
__STDC_UTF_16__
पूर्णांक स्थिरांक 1, यह इंगित करने के लिए है कि प्रकारchar16_t
का मान UTFchar16_t
16 एन्कोडेड हैं। यदि कुछ अन्य एन्कोडिंग का उपयोग किया जाता है, तो मैक्रो को परिभाषित नहीं किया जाएगा और उपयोग की जाने वाली वास्तविक एन्कोडिंग कार्यान्वयन-परिभाषित है।
__STDC_UTF_32__
पूर्णांक स्थिरांक 1, यह इंगित करने के लिए है कि प्रकारchar32_t
का मान UTFchar32_t
32 एन्कोडेड हैं। यदि कुछ अन्य एन्कोडिंग का उपयोग किया जाता है, तो मैक्रो को परिभाषित नहीं किया जाएगा और उपयोग की जाने वाली वास्तविक एन्कोडिंग कार्यान्वयन-परिभाषित है।
आईएसओ / आईईसी 9899: 2011 §6.10.8.3 सशर्त विशेषता मैक्रोज़
__STDC_ANALYZABLE__
पूर्णांक स्थिरांक 1, जिसका उद्देश्य अनुलग्नक L (एनालाइज़ेबिलिटी) में विशिष्टताओं के अनुरूप होना है।__STDC_IEC_559__
पूर्णांक स्थिरांक 1, जिसका उद्देश्य अनुलग्नक F (IEC 60559 फ़्लोटिंग-पॉइंट अंकगणित) में विशिष्टताओं के अनुरूप होना है।__STDC_IEC_559_COMPLEX__
पूर्णांक स्थिरांक 1, जिसका उद्देश्य अनुलग्नक G (IEC 60559 संगत जटिल अंकगणित) में विशिष्टताओं के पालन से है।__STDC_LIB_EXT1__
पूर्णांक स्थिर201112L
, जिसका उद्देश्य अनुलग्नक K (सीमा-जाँच इंटरफेस) में परिभाषित एक्सटेंशन के समर्थन को इंगित करना है।__STDC_NO_ATOMICS__
पूर्णांक स्थिरांक 1, यह इंगित करने के लिए है कि कार्यान्वयन परमाणु प्रकार (_Atomic
प्रकार क्वालीफायर सहित) और<stdatomic.h>
शीर्ष लेख का समर्थन नहीं करता है।__STDC_NO_COMPLEX__
पूर्णांक स्थिरांक 1, यह दर्शाता है कि कार्यान्वयन जटिल प्रकारों या<complex.h>
शीर्षलेख का समर्थन नहीं करता है।__STDC_NO_THREADS__
पूर्णांक स्थिरांक 1, यह इंगित करने के लिए है कि कार्यान्वयन<threads.h>
शीर्ष लेख का समर्थन नहीं करता है।__STDC_NO_VLA__
पूर्णांक स्थिरांक 1, यह इंगित करने के लिए है कि कार्यान्वयन चर लंबाई सरणियों या चर संशोधित प्रकारों का समर्थन नहीं करता है।
हेडर गार्ड शामिल करें
बहुत ज्यादा हर हेडर फ़ाइल में गार्ड आइडियल शामिल होना चाहिए:
मेरी हेडर-file.h
#ifndef MY_HEADER_FILE_H
#define MY_HEADER_FILE_H
// Code body for header file
#endif
यह सुनिश्चित करता है कि जब आप कई जगहों पर #include "my-header-file.h"
करते हैं, तो आपको फ़ंक्शंस, वेरिएबल्स आदि की डुप्लिकेट घोषणाएं नहीं मिलती हैं। फाइलों की निम्न पदानुक्रम की कल्पना करें:
हैडर-1.h
typedef struct {
…
} MyStruct;
int myFunction(MyStruct *value);
हैडर-2.H
#include "header-1.h"
int myFunction2(MyStruct *value);
main.c
#include "header-1.h"
#include "header-2.h"
int main() {
// do something
}
इस कोड में एक गंभीर समस्या है: MyStruct
की विस्तृत सामग्री को दो बार परिभाषित किया गया है, जिसकी अनुमति नहीं है। इसके परिणामस्वरूप एक संकलन त्रुटि होगी जो कि नीचे ट्रैक करने में मुश्किल हो सकती है, क्योंकि एक हेडर फ़ाइल में दूसरा भी शामिल है। यदि आपने इसके बजाय हेडर गार्ड के साथ किया है:
हैडर-1.h
#ifndef HEADER_1_H
#define HEADER_1_H
typedef struct {
…
} MyStruct;
int myFunction(MyStruct *value);
#endif
हैडर-2.H
#ifndef HEADER_2_H
#define HEADER_2_H
#include "header-1.h"
int myFunction2(MyStruct *value);
#endif
main.c
#include "header-1.h"
#include "header-2.h"
int main() {
// do something
}
इसके बाद इसका विस्तार होगा:
#ifndef HEADER_1_H
#define HEADER_1_H
typedef struct {
…
} MyStruct;
int myFunction(MyStruct *value);
#endif
#ifndef HEADER_2_H
#define HEADER_2_H
#ifndef HEADER_1_H // Safe, since HEADER_1_H was #define'd before.
#define HEADER_1_H
typedef struct {
…
} MyStruct;
int myFunction(MyStruct *value);
#endif
int myFunction2(MyStruct *value);
#endif
int main() {
// do something
}
जब कंपाइलर हेडर-1 . HEADER_1_H
के दूसरे HEADER_1_H
पर HEADER_1_H
, तो HEADER_1_H
को पहले से ही शामिल किया गया था। एर्गो, यह निम्नलिखित को उबालता है:
#define HEADER_1_H
typedef struct {
…
} MyStruct;
int myFunction(MyStruct *value);
#define HEADER_2_H
int myFunction2(MyStruct *value);
int main() {
// do something
}
और इस प्रकार कोई संकलन त्रुटि नहीं है।
नोट: हेडर गार्ड के नामकरण के लिए कई अलग-अलग सम्मेलन हैं। कुछ लोग इसे HEADER_2_H_
नाम देना पसंद करते हैं, कुछ में MY_PROJECT_HEADER_2_H
जैसे प्रोजेक्ट का नाम शामिल है। महत्वपूर्ण बात यह सुनिश्चित करना है कि आपके द्वारा अनुसरण किया जाने वाला सम्मेलन इसे बनाता है ताकि आपकी परियोजना की प्रत्येक फ़ाइल में एक अद्वितीय हेडर गार्ड हो।
यदि संरचना विवरण को हेडर में शामिल नहीं किया गया था, तो घोषित प्रकार अधूरा या अपारदर्शी प्रकार होगा । इस प्रकार के कार्य उपयोगी हो सकते हैं, फ़ंक्शन के उपयोगकर्ताओं से कार्यान्वयन विवरण छिपा सकते हैं। कई उद्देश्यों के लिए, मानक C लाइब्रेरी में FILE
प्रकार को एक अपारदर्शी प्रकार माना जा सकता है (हालांकि यह आमतौर पर अपारदर्शी नहीं होता है ताकि मानक I / O फ़ंक्शन के मैक्रो कार्यान्वयन संरचना के आंतरिक का उपयोग कर सकें)। उस स्थिति में, header-1.h
हो सकता है:
#ifndef HEADER_1_H
#define HEADER_1_H
typedef struct MyStruct MyStruct;
int myFunction(MyStruct *value);
#endif
ध्यान दें कि संरचना में एक टैग नाम होना चाहिए (यहां MyStruct
- यह टैग नेमस्पेस में है, टाइप किए गए नाम MyStruct
के सामान्य पहचानकर्ता नामस्थान से अलग है), और यह कि { … }
छोड़ा गया है। यह कहता है "एक संरचना प्रकार की struct MyStruct
और इसके लिए एक उपनाम है MyStruct
"।
कार्यान्वयन फ़ाइल में, संरचना का विवरण प्रकार को पूरा करने के लिए परिभाषित किया जा सकता है:
struct MyStruct {
…
};
यदि आप C11 का उपयोग कर रहे हैं, तो आप typedef struct MyStruct MyStruct;
को दोहरा सकते हैं typedef struct MyStruct MyStruct;
एक संकलन त्रुटि पैदा किए बिना घोषणा, लेकिन सी के पहले संस्करण शिकायत करेंगे। नतीजतन, इसमें शामिल गार्ड मुहावरा का उपयोग करना अभी भी सबसे अच्छा है, भले ही इस उदाहरण में, यह वैकल्पिक होगा यदि कोड केवल कभी संकलक के साथ संकलित किया गया था जो C11 का समर्थन करता था।
कई कंपाइलर #pragma once
निर्देश का समर्थन करते हैं, जिसके परिणाम समान हैं:
मेरी हेडर-file.h
#pragma once
// Code for header file
हालाँकि, #pragma once
सी मानक का हिस्सा नहीं है, इसलिए यदि आप इसका उपयोग करते हैं तो कोड कम पोर्टेबल है।
कुछ हेडर में गार्ड मुहावरा शामिल नहीं है। एक विशिष्ट उदाहरण मानक <assert.h>
हैडर है। इसे एकल अनुवाद इकाई में कई बार शामिल किया जा सकता है, और ऐसा करने का प्रभाव इस बात पर निर्भर करता है कि क्या हेडर शामिल होने पर मैक्रो NDEBUG
को परिभाषित किया जाता है। आपको कभी-कभी एक अनुरूप आवश्यकता हो सकती है; इस तरह के मामले कम और दूर के होंगे। आमतौर पर, आपके हेडर को शामिल गार्ड मुहावरे द्वारा संरक्षित किया जाना चाहिए।
पूर्व कार्यान्वयन
हम कोड को पढ़ने और लिखने में आसान बनाने के लिए मैक्रोज़ का भी उपयोग कर सकते हैं। उदाहरण के लिए हम लागू करने के लिए मैक्रो को लागू कर सकते foreach
singly- और दोगुना से जुड़े सूचियों, कतार, आदि जैसे कुछ डेटा संरचनाओं के लिए सी में निर्माण
यहाँ एक छोटा सा उदाहरण है।
#include <stdio.h>
#include <stdlib.h>
struct LinkedListNode
{
int data;
struct LinkedListNode *next;
};
#define FOREACH_LIST(node, list) \
for (node=list; node; node=node->next)
/* Usage */
int main(void)
{
struct LinkedListNode *list, **plist = &list, *node;
int i;
for (i=0; i<10; i++)
{
*plist = malloc(sizeof(struct LinkedListNode));
(*plist)->data = i;
(*plist)->next = NULL;
plist = &(*plist)->next;
}
/* printing the elements here */
FOREACH_LIST(node, list)
{
printf("%d\n", node->data);
}
}
आप इस तरह की डेटा-संरचनाओं के लिए एक मानक इंटरफ़ेस बना सकते हैं और FOREACH
का सामान्य कार्यान्वयन लिख सकते हैं:
#include <stdio.h>
#include <stdlib.h>
typedef struct CollectionItem_
{
int data;
struct CollectionItem_ *next;
} CollectionItem;
typedef struct Collection_
{
/* interface functions */
void* (*first)(void *coll);
void* (*last) (void *coll);
void* (*next) (void *coll, CollectionItem *currItem);
CollectionItem *collectionHead;
/* Other fields */
} Collection;
/* must implement */
void *first(void *coll)
{
return ((Collection*)coll)->collectionHead;
}
/* must implement */
void *last(void *coll)
{
return NULL;
}
/* must implement */
void *next(void *coll, CollectionItem *curr)
{
return curr->next;
}
CollectionItem *new_CollectionItem(int data)
{
CollectionItem *item = malloc(sizeof(CollectionItem));
item->data = data;
item->next = NULL;
return item;
}
void Add_Collection(Collection *coll, int data)
{
CollectionItem **item = &coll->collectionHead;
while(*item)
item = &(*item)->next;
(*item) = new_CollectionItem(data);
}
Collection *new_Collection()
{
Collection *nc = malloc(sizeof(Collection));
nc->first = first;
nc->last = last;
nc->next = next;
return nc;
}
/* generic implementation */
#define FOREACH(node, collection) \
for (node = (collection)->first(collection); \
node != (collection)->last(collection); \
node = (collection)->next(collection, node))
int main(void)
{
Collection *coll = new_Collection();
CollectionItem *node;
int i;
for(i=0; i<10; i++)
{
Add_Collection(coll, i);
}
/* printing the elements here */
FOREACH(node, coll)
{
printf("%d\n", node->data);
}
}
इस सामान्य कार्यान्वयन का उपयोग करने के लिए अपने डेटा संरचना के लिए इन कार्यों को लागू करें।
1. void* (*first)(void *coll);
2. void* (*last) (void *coll);
3. void* (*next) (void *coll, CollectionItem *currItem);
सी ++ कोड में सी एक्सटर्नल का उपयोग करने के लिए __cplusplus, सी ++ नाम के साथ संकलित
कई बार ऐसा होता है जब किसी फ़ाइल में भाषा के अंतर के कारण कंपाइलर C कंपाइलर या C ++ कंपाइलर होता है, इस पर निर्भर करते हुए प्रीप्रोसेसर से अलग आउटपुट जेनरेट करना पड़ता है।
उदाहरण के लिए एक फ़ंक्शन या अन्य बाहरी सी स्रोत फ़ाइल में परिभाषित किया गया है, लेकिन C ++ स्रोत फ़ाइल में उपयोग किया जाता है। चूँकि C ++ फ़ंक्शन तर्क प्रकारों के आधार पर अद्वितीय फ़ंक्शन नामों को उत्पन्न करने के लिए नाम मेनिंगिंग (या नाम सजावट) का उपयोग करता है, इसलिए C ++ स्रोत फ़ाइल में उपयोग किए जाने वाले C फ़ंक्शन घोषणा लिंक त्रुटियों का कारण होगा। C ++ कंपाइलर C ++ के लिए मैनिंग नियमों का उपयोग करके कंपाइलर आउटपुट के लिए निर्दिष्ट बाहरी नाम को संशोधित करेगा। जब C ++ कंपाइलर आउटपुट C कंपाइलर आउटपुट के साथ लिंक किया जाता है तो परिणाम बाहरी नहीं होने के कारण लिंक एरर है।
चूँकि C कंपाइलर नामकरण नहीं करते हैं, लेकिन C ++ कंपाइलर द्वारा निर्मित सभी बाहरी लेबल (फ़ंक्शन नाम या वैरिएबल नाम) के लिए C ++ कंपाइलर करते हैं, एक पूर्वनिर्धारित प्रीप्रोसेसर मैक्रो, __cplusplus
को कंपाइलर का पता लगाने की अनुमति देने के लिए पेश किया गया था।
C और C ++ के बीच बाहरी नामों के लिए असंगत संकलक आउटपुट की इस समस्या के आसपास काम करने के लिए, मैक्रो __cplusplus
C ++ प्रीप्रोसेसर में परिभाषित किया गया है और C प्रीप्रोसेसर में परिभाषित नहीं किया गया है। इस मैक्रो नाम का उपयोग सशर्त प्रीप्रोसेसर #ifdef
निर्देश या #if
साथ defined()
ऑपरेटर के साथ यह बताने के लिए किया जा सकता है कि स्रोत कोड या फ़ाइल को C ++ या C के रूप में संकलित किया जा रहा है या नहीं।
#ifdef __cplusplus
printf("C++\n");
#else
printf("C\n");
#endif
या आप उपयोग कर सकते हैं
#if defined(__cplusplus)
printf("C++\n");
#else
printf("C\n");
#endif
C ++ स्रोत C के साथ संकलित C स्रोत फ़ाइल से किसी फ़ंक्शन के सही फ़ंक्शन का नाम निर्दिष्ट करने के लिए, आप __cplusplus
लिए जाँच कर सकते हैं जो कि निरंतर extern "C" { /* ... */ };
कारण परिभाषित होता है extern "C" { /* ... */ };
जब सी फ़ाइल को C ++ स्रोत फ़ाइल में शामिल किया जाता है, तो सी एक्सटर्नल घोषित करने के लिए उपयोग किया जाता है। हालांकि जब एक सी संकलक के साथ संकलित किया जाता है, तो extern "C" { */ ... */ };
उपयोग नहीं होता है। इस सशर्त संकलन की आवश्यकता है क्योंकि extern "C" { /* ... */ };
C ++ में मान्य है लेकिन C में नहीं है।
#ifdef __cplusplus
// if we are being compiled with a C++ compiler then declare the
// following functions as C functions to prevent name mangling.
extern "C" {
#endif
// exported C function list.
int foo (void);
#ifdef __cplusplus
// if this is a C++ compiler, we need to close off the extern declaration.
};
#endif
समारोह की तरह मैक्रोज़
फ़ंक्शन-जैसे मैक्रोज़ inline
फ़ंक्शन के समान हैं, ये कुछ मामलों में उपयोगी होते हैं, जैसे अस्थायी डीबग लॉग:
#ifdef DEBUG
# define LOGFILENAME "/tmp/logfile.log"
# define LOG(str) do { \
FILE *fp = fopen(LOGFILENAME, "a"); \
if (fp) { \
fprintf(fp, "%s:%d %s\n", __FILE__, __LINE__, \
/* don't print null pointer */ \
str ?str :"<null>"); \
fclose(fp); \
} \
else { \
perror("Opening '" LOGFILENAME "' failed"); \
} \
} while (0)
#else
/* Make it a NOOP if DEBUG is not defined. */
# define LOG(LINE) (void)0
#endif
#include <stdio.h>
int main(int argc, char* argv[])
{
if (argc > 1)
LOG("There are command line arguments");
else
LOG("No command line arguments");
return 0;
}
यहां दोनों मामलों में ( DEBUG
साथ या नहीं) कॉल void
रिटर्न प्रकार के साथ एक फ़ंक्शन के समान व्यवहार करता है। यह सुनिश्चित करता है कि if/else
शर्त की अपेक्षा के अनुसार व्याख्या की जाती है।
DEBUG
मामले में यह एक do { ... } while(0)
निर्माण के do { ... } while(0)
माध्यम से लागू किया जाता है। अन्य मामले में, (void)0
बिना किसी साइड इफ़ेक्ट वाला एक स्टेटमेंट है जिसे सिर्फ अनदेखा किया जाता है।
बाद के लिए एक विकल्प होगा
#define LOG(LINE) do { /* empty */ } while (0)
ऐसा है कि यह सभी मामलों में कृत्रिम रूप से पहले के बराबर है।
यदि आप जीसीसी का उपयोग करते हैं, तो आप फ़ंक्शन-जैसे मैक्रो को भी लागू कर सकते हैं जो एक गैर-मानक GNU एक्सटेंशन - स्टेटमेंट एक्सप्रेशन का उपयोग करके परिणाम देता है। उदाहरण के लिए:
#include <stdio.h>
#define POW(X, Y) \
({ \
int i, r = 1; \
for (i = 0; i < Y; ++i) \
r *= X; \
r; \ // returned value is result of last operation
})
int main(void)
{
int result;
result = POW(2, 3);
printf("Result: %d\n", result);
}
वैराडिक तर्क मैक्रो
वैरिकाज़ आर्ग के साथ मैक्रोज़:
मान लें कि आप अपने कोड को डीबग करने के लिए कुछ प्रिंट-मैक्रो बनाना चाहते हैं, आइए इस मैक्रो को एक उदाहरण के रूप में लेते हैं:
#define debug_print(msg) printf("%s:%d %s", __FILE__, __LINE__, msg)
उपयोग के कुछ उदाहरण:
फ़ंक्शन somefunc()
रिटर्न -1 यदि विफल रहा और 0 सफल होने पर, और इसे कोड के भीतर अलग-अलग स्थानों से कॉल किया जाता है:
int retVal = somefunc();
if(retVal == -1)
{
debug_printf("somefunc() has failed");
}
/* some other code */
retVal = somefunc();
if(retVal == -1)
{
debug_printf("somefunc() has failed");
}
यदि somefunc()
बदलाव होता है, तो क्या होता है और यह अब विभिन्न संभावित त्रुटि प्रकारों के मिलान के विभिन्न मान लौटाता है? आप अभी भी डीबग मैक्रो का उपयोग करना चाहते हैं और त्रुटि मान मुद्रित करें।
debug_printf(retVal); /* this would obviously fail */
debug_printf("%d",retVal); /* this would also fail */
इस समस्या को हल करने के लिए __VA_ARGS__
मैक्रो पेश किया गया था। यह मैक्रो कई मापदंडों को एक्स-मैक्रो की अनुमति देता है:
उदाहरण:
#define debug_print(msg, ...) printf(msg, __VA_ARGS__) \
printf("\nError occurred in file:line (%s:%d)\n", __FILE__, __LINE)
उपयोग:
int retVal = somefunc();
debug_print("retVal of somefunc() is-> %d", retVal);
यह मैक्रो आपको कई मापदंडों को पारित करने और उन्हें प्रिंट करने की अनुमति देता है, लेकिन अब यह आपको किसी भी पैरामीटर को भेजने से मना करता है।
debug_print("Hey");
यह कुछ सिंटैक्स त्रुटि को बढ़ाएगा क्योंकि मैक्रो को कम से कम एक और तर्क की उम्मीद है और प्री-प्रोसेसर debug_print()
मैक्रो में अल्पविराम की कमी की अनदेखी नहीं करेगा। इसके अलावा debug_print("Hey",);
आप एक वाक्यविन्यास त्रुटि को बढ़ाएँगे क्योंकि आप तर्क को स्थूल को खाली नहीं रखेंगे।
इसे हल करने के लिए, ##__VA_ARGS__
मैक्रो पेश किया गया था, इस मैक्रो में कहा गया है कि यदि कोई चर तर्क मौजूद नहीं है, तो कॉमा प्री-प्रोसेसर द्वारा कोड से हटा दिया जाता है।
उदाहरण:
#define debug_print(msg, ...) printf(msg, ##__VA_ARGS__) \
printf("\nError occured in file:line (%s:%d)\n", __FILE__, __LINE)
उपयोग:
debug_print("Ret val of somefunc()?");
debug_print("%d",somefunc());