जवाबों:
उपयोग करना extern
केवल प्रासंगिकता का है जब आप जो प्रोग्राम बना रहे हैं, उसमें एक साथ कई स्रोत फाइलें शामिल हैं, जहां कुछ चर परिभाषित किए गए हैं, उदाहरण के लिए, स्रोत फ़ाइल file1.c
में अन्य स्रोत फ़ाइलों में संदर्भित होने की आवश्यकता है, जैसे file2.c
।
एक चर को परिभाषित करने और एक चर घोषित करने के बीच अंतर को समझना महत्वपूर्ण है :
एक चर घोषित किया जाता है जब संकलक को सूचित किया जाता है कि एक चर मौजूद है (और यह इसका प्रकार है); यह उस बिंदु पर चर के लिए भंडारण को आवंटित नहीं करता है।
एक चर को परिभाषित किया जाता है जब संकलक चर के लिए भंडारण आवंटित करता है।
आप कई बार एक चर घोषित कर सकते हैं (हालांकि एक बार पर्याप्त है); आप किसी दिए गए दायरे में इसे केवल एक बार परिभाषित कर सकते हैं। एक चर परिभाषा भी एक घोषणा है, लेकिन सभी चर घोषणाएं परिभाषाएं नहीं हैं।
वैश्विक चर घोषित करने और परिभाषित करने का स्वच्छ, विश्वसनीय तरीका है कि आप चर की extern
घोषणा को रोकने के लिए हेडर फाइल का उपयोग कर सकते हैं ।
शीर्ष लेख में एक स्रोत फ़ाइल शामिल है जो चर को परिभाषित करती है और चर का संदर्भ देने वाली सभी स्रोत फ़ाइलों द्वारा। प्रत्येक प्रोग्राम के लिए, एक स्रोत फ़ाइल (और केवल एक स्रोत फ़ाइल) चर को परिभाषित करती है। इसी तरह, एक हेडर फ़ाइल (और केवल एक हेडर फ़ाइल) को चर घोषित करना चाहिए। हेडर फ़ाइल महत्वपूर्ण है; यह स्वतंत्र टीयू (अनुवाद इकाइयों - स्रोत फ़ाइलों के बारे में) के बीच क्रॉस-चेकिंग को सक्षम बनाता है और स्थिरता सुनिश्चित करता है।
यद्यपि इसे करने के अन्य तरीके हैं, यह विधि सरल और विश्वसनीय है। यह द्वारा प्रदर्शित किया जाता है file3.h
, file1.c
और file2.c
:
extern int global_variable; /* Declaration of the variable */
#include "file3.h" /* Declaration made available here */
#include "prog1.h" /* Function declarations */
/* Variable defined here */
int global_variable = 37; /* Definition checked against declaration */
int increment(void) { return global_variable++; }
#include "file3.h"
#include "prog1.h"
#include <stdio.h>
void use_it(void)
{
printf("Global variable: %d\n", global_variable++);
}
वैश्विक चर घोषित करने और परिभाषित करने का यह सबसे अच्छा तरीका है।
अगली दो फाइलें इसके लिए स्रोत को पूरा करती हैं prog1
:
पूर्ण कार्यक्रमों में उपयोग किए गए कार्य दिखाए गए हैं, इसलिए फ़ंक्शन घोषणाओं में कमी आई है। C99 और C11 दोनों को उपयोग किए जाने से पहले फ़ंक्शन को घोषित या परिभाषित करने की आवश्यकता होती है (जबकि C90 अच्छे कारणों के लिए नहीं था)। मैं extern
स्थिरता के लिए हेडर में फ़ंक्शन घोषणाओं के सामने कीवर्ड का उपयोग करता हूं - extern
हेडर में चर घोषणाओं के सामने मिलान करने के लिए । कई लोग extern
फ़ंक्शन घोषणाओं के सामने उपयोग नहीं करना पसंद करते हैं ; संकलक परवाह नहीं करता है - और अंत में, न तो जब तक आप सुसंगत हैं, कम से कम एक स्रोत फ़ाइल के भीतर।
extern void use_it(void);
extern int increment(void);
#include "file3.h"
#include "prog1.h"
#include <stdio.h>
int main(void)
{
use_it();
global_variable += 19;
use_it();
printf("Increment: %d\n", increment());
return 0;
}
prog1
का उपयोग करता है prog1.c
, file1.c
, file2.c
, file3.h
और prog1.h
।फ़ाइल केवल के prog1.mk
लिए एक makefile है prog1
। यह make
सहस्राब्दी के मोड़ के बाद से उत्पादित के अधिकांश संस्करणों के साथ काम करेगा । यह विशेष रूप से जीएनयू मेक से बंधा नहीं है।
# Minimal makefile for prog1
PROGRAM = prog1
FILES.c = prog1.c file1.c file2.c
FILES.h = prog1.h file3.h
FILES.o = ${FILES.c:.c=.o}
CC = gcc
SFLAGS = -std=c11
GFLAGS = -g
OFLAGS = -O3
WFLAG1 = -Wall
WFLAG2 = -Wextra
WFLAG3 = -Werror
WFLAG4 = -Wstrict-prototypes
WFLAG5 = -Wmissing-prototypes
WFLAGS = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5}
UFLAGS = # Set on command line only
CFLAGS = ${SFLAGS} ${GFLAGS} ${OFLAGS} ${WFLAGS} ${UFLAGS}
LDFLAGS =
LDLIBS =
all: ${PROGRAM}
${PROGRAM}: ${FILES.o}
${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}
prog1.o: ${FILES.h}
file1.o: ${FILES.h}
file2.o: ${FILES.h}
# If it exists, prog1.dSYM is a directory on macOS DEBRIS = a.out core *~ *.dSYM RM_FR = rm -fr
clean:
${RM_FR} ${FILES.o} ${PROGRAM} ${DEBRIS}
विशेषज्ञों द्वारा केवल और केवल अच्छे कारणों से नियम तोड़े जाने हैं:
एक हेडर फ़ाइल में केवल extern
चर की घोषणाएँ शामिल होती हैं - कभी-कभी
static
या अयोग्य श्रेणी की परिभाषाएँ नहीं।
किसी भी चर के लिए, केवल एक हेडर फ़ाइल इसे घोषित करती है (SPOT - सत्य का एकल बिंदु)।
एक स्रोत फ़ाइल extern
में चर की घोषणाएं कभी नहीं होती हैं - स्रोत फ़ाइलों में हमेशा एकमात्र (एकमात्र) हेडर शामिल होता है जो उन्हें घोषित करता है।
किसी भी दिए गए चर के लिए, वास्तव में एक स्रोत फ़ाइल चर को परिभाषित करती है, अधिमानतः इसे भी प्रारंभ करना। (हालांकि शून्य को स्पष्ट रूप से प्रारंभ करने की कोई आवश्यकता नहीं है, यह कोई नुकसान नहीं पहुंचाता है और कुछ अच्छा कर सकता है, क्योंकि एक कार्यक्रम में किसी विशेष वैश्विक चर की केवल एक ही प्रारंभिक परिभाषा हो सकती है)।
चर को परिभाषित करने वाली स्रोत फ़ाइल में यह सुनिश्चित करने के लिए हेडर भी शामिल है कि परिभाषा और घोषणा संगत हैं।
एक फ़ंक्शन का उपयोग करके एक चर घोषित करने की आवश्यकता नहीं होनी चाहिए extern
।
जब भी संभव हो वैश्विक चर से बचें - इसके बजाय कार्यों का उपयोग करें।
इस उत्तर का स्रोत कोड और पाठ मेरे SOQ (Stack Overflow Questions) भंडार में src / so-0143-3204 उप-निर्देशिका में उपलब्ध हैं।
यदि आप एक अनुभवी सी प्रोग्रामर नहीं हैं, तो आप (और शायद) को यहां पढ़ना बंद कर सकते हैं।
कुछ (वास्तव में, कई) सी संकलक के साथ, आप एक चर की 'आम' परिभाषा भी कह सकते हैं। Refers कॉमन ’, यहाँ, स्रोत फ़ाइलों के बीच चर साझा करने के लिए फोरट्रान में प्रयुक्त तकनीक को संदर्भित करता है, (संभवतः नाम) COMMON ब्लॉक का उपयोग करके। यहाँ क्या होता है कि प्रत्येक संख्या में फाइल चर की एक अस्थायी परिभाषा प्रदान करती है। जब तक एक से अधिक फ़ाइल एक आरंभिक परिभाषा प्रदान नहीं करती है, तब तक विभिन्न फाइलें चर की एक सामान्य एकल परिभाषा साझा करती हैं:
#include "prog2.h"
long l; /* Do not do this in portable code */
void inc(void) { l++; }
#include "prog2.h"
long l; /* Do not do this in portable code */
void dec(void) { l--; }
#include "prog2.h"
#include <stdio.h>
long l = 9; /* Do not do this in portable code */
void put(void) { printf("l = %ld\n", l); }
यह तकनीक सी मानक के अक्षर और 'एक परिभाषा नियम' के अनुरूप नहीं है - यह आधिकारिक रूप से अपरिभाषित व्यवहार है:
बाहरी लिंकेज के साथ एक पहचानकर्ता का उपयोग किया जाता है, लेकिन कार्यक्रम में पहचानकर्ता के लिए बिल्कुल एक बाहरी परिभाषा मौजूद नहीं है, या पहचानकर्ता का उपयोग नहीं किया जाता है और पहचानकर्ता (6.9) के लिए कई बाहरी परिभाषाएं मौजूद हैं।
एक बाहरी परिभाषा एक बाहरी घोषणा है जो एक फ़ंक्शन (एक इनलाइन परिभाषा के अलावा) या एक वस्तु की परिभाषा भी है। एक पहचानकर्ता बाहरी लिंकेज के साथ घोषित (एक के संकार्य के हिस्से के रूप के अलावा अन्य एक अभिव्यक्ति में प्रयोग किया जाता है
sizeof
या_Alignof
ऑपरेटर जिसका परिणाम एक पूर्णांक स्थिर है), कहीं पूरे कार्यक्रम में वहाँ ठीक एक पहचानकर्ता के लिए बाहरी परिभाषा होगा; अन्यथा, एक से अधिक नहीं होंगे। 161)161) इस प्रकार, यदि बाहरी लिंकेज के साथ घोषित एक पहचानकर्ता का अभिव्यक्ति में उपयोग नहीं किया जाता है, तो इसके लिए कोई बाहरी परिभाषा नहीं होनी चाहिए।
हालांकि, सी मानक भी यह जानकारीपूर्ण अनुलग्नक जम्मू में से एक के रूप में सूचीबद्ध आम एक्सटेंशन ।
कीवर्ड बाहरी के स्पष्ट उपयोग के साथ या उसके बिना किसी वस्तु की पहचान करने वाले के लिए एक से अधिक बाहरी परिभाषा हो सकती है; यदि परिभाषाएँ असहमत हैं, या एक से अधिक का आरोप लगाया गया है, तो व्यवहार अपरिभाषित है (6.9.2)।
क्योंकि यह तकनीक हमेशा समर्थित नहीं है, इसलिए इसका उपयोग करने से बचने के लिए सबसे अच्छा है, खासकर यदि आपके कोड को पोर्टेबल होने की आवश्यकता है । इस तकनीक का उपयोग करते हुए, आप अनजाने प्रकार की चकाचौंध के साथ भी समाप्त हो सकते हैं।
यदि उपरोक्त फ़ाइलों में से l
एक के double
बजाय एक के रूप में घोषित किया गया है long
, तो C के टाइप-असुरक्षित लिंकर्स शायद बेमेल नहीं दिखेंगे। यदि आप 64-बिट के साथ एक मशीन पर हैं long
और double
, आपको चेतावनी भी नहीं मिलेगी; 32-बिट long
और 64-बिट वाली मशीन पर double
, आपको संभवतः विभिन्न आकारों के बारे में एक चेतावनी मिलेगी - लिंकर सबसे बड़े आकार का उपयोग करेगा, ठीक उसी तरह जैसे कि फोरट्रान प्रोग्राम किसी भी सामान्य ब्लॉक का सबसे बड़ा आकार लेगा।
ध्यान दें कि GCC 10.1.0, जो कि 2020-05-07 को जारी किया गया था, उपयोग करने के लिए डिफ़ॉल्ट संकलन विकल्पों में परिवर्तन करता है -fno-common
, जिसका अर्थ है कि डिफ़ॉल्ट रूप से, जब तक आप डिफ़ॉल्ट के साथ ओवरराइड नहीं करते -fcommon
(या विशेषताओं का उपयोग करते हैं, आदि) से ऊपर कोड लिंक नहीं करता है - लिंक देखें)।
अगली दो फाइलें इसके लिए स्रोत को पूरा करती हैं prog2
:
extern void dec(void);
extern void put(void);
extern void inc(void);
#include "prog2.h"
#include <stdio.h>
int main(void)
{
inc();
put();
dec();
put();
dec();
put();
}
prog2
का उपयोग करता है prog2.c
, file10.c
, file11.c
, file12.c
, prog2.h
।यहाँ टिप्पणी में बताया गया है, और के रूप में एक समान करने के लिए अपने जवाब में कहा गया है सवाल , अपरिभाषित व्यवहार करने के लिए एक वैश्विक चर सुराग के लिए कई परिभाषाएं उपयोग करते हुए;, जो "कुछ भी हो सकता" कहने का मानक तरीका है (J.2 §6.9)। चीजों में से एक यह हो सकता है कि कार्यक्रम आपकी अपेक्षा के अनुरूप व्यवहार करे; और J.5.11 कहते हैं, लगभग, "आप अधिक भाग्यशाली हो सकते हैं जितना आप लायक हैं"। लेकिन एक प्रोग्राम जो एक्सटर्नल वेरिएबल की कई परिभाषाओं पर निर्भर करता है - स्पष्ट 'एक्सटर्नल' कीवर्ड के साथ या उसके बिना - एक सख्ती से अनुरूप प्रोग्राम नहीं है और हर जगह काम करने की गारंटी नहीं है। समान रूप से: इसमें एक बग होता है जो स्वयं को दिखा सकता है या नहीं दिखा सकता है।
बेशक, ऐसे कई तरीके हैं जिनसे इन दिशानिर्देशों को तोड़ा जा सकता है। कभी-कभी, दिशानिर्देशों को तोड़ने का एक अच्छा कारण हो सकता है, लेकिन ऐसे अवसर बेहद असामान्य होते हैं।
c int some_var; /* Do not do this in a header!!! */
नोट 1: यदि हेडर extern
कीवर्ड के बिना वेरिएबल को परिभाषित करता है , तो हेडर में प्रत्येक फ़ाइल में वेरिएबल की एक टेंपरेरी परिभाषा बनाई जाती है। जैसा कि पहले उल्लेख किया गया है, यह अक्सर काम करेगा, लेकिन सी मानक गारंटी नहीं देता है कि यह काम करेगा।
c int some_var = 13; /* Only one source file in a program can use this */
नोट 2: यदि शीर्ष लेख चर को परिभाषित करता है और आरंभ करता है, तो दिए गए प्रोग्राम में केवल एक स्रोत फ़ाइल हेडर का उपयोग कर सकता है। चूंकि हेडर मुख्य रूप से जानकारी साझा करने के लिए होते हैं, इसलिए इसे बनाना थोड़ा मूर्खतापूर्ण है जो केवल एक बार उपयोग किया जा सकता है।
c static int hidden_global = 3; /* Each source file gets its own copy */
नोट 3: यदि शीर्ष लेख एक स्थैतिक चर (आरंभ के साथ या बिना) को परिभाषित करता है, तो प्रत्येक स्रोत फ़ाइल 'वैश्विक' चर के अपने निजी संस्करण के साथ समाप्त होती है।
यदि चर वास्तव में एक जटिल सरणी है, उदाहरण के लिए, इससे कोड का अत्यधिक दोहराव हो सकता है। यह, कभी-कभी, कुछ प्रभाव को प्राप्त करने का एक समझदार तरीका हो सकता है, लेकिन यह बहुत ही असामान्य है।
मैंने पहले दिखाई गई हेडर तकनीक का उपयोग करें। यह मज़बूती से और हर जगह काम करता है। ध्यान दें, विशेष रूप से, यह घोषित करने वाला शीर्षलेख global_variable
प्रत्येक फ़ाइल में शामिल है जो इसका उपयोग करता है - इसमें वह भी शामिल है जो इसे परिभाषित करता है। यह सुनिश्चित करता है कि सब कुछ आत्मनिर्भर है।
समान कार्य घोषित करने और परिभाषित करने के साथ-साथ समान नियम लागू होते हैं। लेकिन सवाल विशेष रूप से चर के बारे में था, इसलिए मैंने केवल चर का जवाब रखा है।
यदि आप एक अनुभवी सी प्रोग्रामर नहीं हैं, तो आपको शायद यहां पढ़ना बंद कर देना चाहिए।
लेट मेजर एडिशन
एक चिंता जो कभी-कभी (और वैध रूप से) हेडर में 'घोषणाओं, स्रोत में परिभाषा' के बारे में उठाई जाती है, यहां वर्णित है कि हेडर और स्रोत को सिंक्रनाइज़ करने के लिए दो फाइलें हैं। यह आमतौर पर एक अवलोकन के साथ पालन किया जाता है कि एक मैक्रो का उपयोग किया जा सकता है ताकि हेडर दोहरे कर्तव्य परोसता है - सामान्य रूप से चर घोषित करता है, लेकिन जब हेडर शामिल करने से पहले एक विशिष्ट मैक्रो सेट किया जाता है, तो यह चर को परिभाषित करता है।
एक और चिंता का विषय यह हो सकता है कि चर को कई 'मुख्य कार्यक्रमों' में से प्रत्येक में परिभाषित किया जाना चाहिए। यह सामान्य रूप से एक गंभीर चिंता है; आप बस चर को परिभाषित करने के लिए एक सी स्रोत फ़ाइल पेश कर सकते हैं और प्रत्येक प्रोग्राम के साथ उत्पादित ऑब्जेक्ट फ़ाइल को लिंक कर सकते हैं।
एक विशिष्ट योजना इस तरह से काम करती है, जिसमें मूल वैश्विक चर का उपयोग किया गया है file3.h
:
#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern
#endif /* DEFINE_VARIABLES */
EXTERN int global_variable;
#define DEFINE_VARIABLES
#include "file3a.h" /* Variable defined - but not initialized */
#include "prog3.h"
int increment(void) { return global_variable++; }
#include "file3a.h"
#include "prog3.h"
#include <stdio.h>
void use_it(void)
{
printf("Global variable: %d\n", global_variable++);
}
अगली दो फाइलें इसके लिए स्रोत को पूरा करती हैं prog3
:
extern void use_it(void);
extern int increment(void);
#include "file3a.h"
#include "prog3.h"
#include <stdio.h>
int main(void)
{
use_it();
global_variable += 19;
use_it();
printf("Increment: %d\n", increment());
return 0;
}
prog3
का उपयोग करता है prog3.c
, file1a.c
, file2a.c
, file3a.h
, prog3.h
।इस योजना के साथ समस्या यह है कि यह वैश्विक चर के आरंभ के लिए प्रदान नहीं करता है। मैक्रोज़ के लिए C99 या C11 और चर तर्क सूचियों के साथ, आप आरंभीकरण का समर्थन करने के लिए एक मैक्रो को भी परिभाषित कर सकते हैं। (मैक्रो में परिवर्तनशील तर्क सूचियों के लिए C89 और कोई समर्थन नहीं होने के कारण, मनमाने ढंग से लंबे आरंभीकरण को संभालने का कोई आसान तरीका नहीं है।)
#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#define INITIALIZER(...) = __VA_ARGS__
#else
#define EXTERN extern
#define INITIALIZER(...) /* nothing */
#endif /* DEFINE_VARIABLES */
EXTERN int global_variable INITIALIZER(37);
EXTERN struct { int a; int b; } oddball_struct INITIALIZER({ 41, 43 });
डेनिस नियाज़ेव द्वारा पहचाने गए बग को ठीक करना, ब्लॉक #if
और #else
ब्लॉक
की सामग्री
#define DEFINE_VARIABLES
#include "file3b.h" /* Variables now defined and initialized */
#include "prog4.h"
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
#include "file3b.h"
#include "prog4.h"
#include <stdio.h>
void use_them(void)
{
printf("Global variable: %d\n", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
}
स्पष्ट रूप से, ऑडबॉल संरचना के लिए कोड वह नहीं है जो आप सामान्य रूप से लिखते हैं, लेकिन यह बिंदु को दिखाता है। के दूसरे आह्वान का पहला तर्क INITIALIZER
है { 41
और शेष तर्क (इस उदाहरण में एकवचन) है 43 }
। मैक्रो के लिए चर तर्क सूचियों के लिए C99 या इसी तरह के समर्थन के बिना, शुरुआती को अल्पविराम शामिल करने की आवश्यकता है जो बहुत समस्याग्रस्त हैं।
सही हेडर file3b.h
शामिल है (इसके बजाय fileba.h
) प्रति
डेनिस नियाज़ेव
अगली दो फाइलें इसके लिए स्रोत को पूरा करती हैं prog4
:
extern int increment(void);
extern int oddball_value(void);
extern void use_them(void);
#include "file3b.h"
#include "prog4.h"
#include <stdio.h>
int main(void)
{
use_them();
global_variable += 19;
use_them();
printf("Increment: %d\n", increment());
printf("Oddball: %d\n", oddball_value());
return 0;
}
prog4
का उपयोग करता है prog4.c
, file1b.c
, file2b.c
, prog4.h
, file3b.h
।किसी भी हेडर को पुनर्जन्म के खिलाफ संरक्षित किया जाना चाहिए, ताकि प्रकार की परिभाषाएं (एनम, संरचना या संघ प्रकार या आमतौर पर टाइप किए गए) समस्या का कारण न बनें। मानक तकनीक हैडर के शरीर को हेडर गार्ड में लपेटना है जैसे:
#ifndef FILE3B_H_INCLUDED
#define FILE3B_H_INCLUDED
...contents of header...
#endif /* FILE3B_H_INCLUDED */
शीर्ष लेख को दो बार अप्रत्यक्ष रूप से शामिल किया जा सकता है। उदाहरण के लिए, यदि एक प्रकार की परिभाषा file4b.h
शामिल है , file3b.h
जिसे दिखाया नहीं गया है, और file1b.c
हेडर file4b.h
और दोनों का उपयोग करने की आवश्यकता है file3b.h
, तो आपके पास हल करने के लिए कुछ और मुश्किल मुद्दे हैं। स्पष्ट रूप से, आप सिर्फ शामिल करने के लिए हेडर सूची को संशोधित कर सकते हैं file4b.h
। हालाँकि, आपको आंतरिक निर्भरता के बारे में पता नहीं हो सकता है - और कोड को, आदर्श रूप से, काम करना जारी रखना चाहिए।
इसके अलावा, यह मुश्किल होने लगता है क्योंकि आप परिभाषाओं को बनाने के लिए file4b.h
शामिल file3b.h
करने से पहले शामिल हो सकते हैं , लेकिन सामान्य हेडर गार्ड पर हेडर को file3b.h
फिर से चालू करने से रोका जाएगा।
तो, आपको file3b.h
घोषणाओं के लिए अधिकतम एक बार शरीर को शामिल करने की आवश्यकता है , और परिभाषाओं के लिए अधिकतम एक बार, लेकिन आपको एक ही अनुवाद इकाई (टीयू - स्रोत फ़ाइल का एक संयोजन और इसका उपयोग करने वाले शीर्ष लेख) दोनों की आवश्यकता हो सकती है।
हालांकि, यह एक बहुत अनुचित अनुचित के अधीन किया जा सकता है। चलिए एक नए फ़ाइल नाम का सेट प्रस्तुत करते हैं:
external.h
EXTERN मैक्रो परिभाषाओं के लिए, आदि।
file1c.h
प्रकारों को परिभाषित करने के लिए (विशेषकर, struct oddball
प्रकार oddball_struct
)।
file2c.h
वैश्विक चरों को परिभाषित या घोषित करना।
file3c.c
जो वैश्विक चर को परिभाषित करता है।
file4c.c
जो बस वैश्विक चर का उपयोग करता है।
file5c.c
जो दिखाता है कि आप घोषित कर सकते हैं और फिर वैश्विक चर को परिभाषित कर सकते हैं।
file6c.c
जो दिखाता है कि आप परिभाषित कर सकते हैं और फिर (प्रयास) वैश्विक चर घोषित कर सकते हैं।
इन उदाहरणों में, file5c.c
और file6c.c
सीधे हेडर को file2c.h
कई बार शामिल करते हैं , लेकिन यह दिखाने का सबसे सरल तरीका है कि तंत्र काम करता है। इसका मतलब है कि अगर हेडर को अप्रत्यक्ष रूप से दो बार शामिल किया गया था, तो यह सुरक्षित भी होगा।
इस काम के लिए प्रतिबंध हैं:
वैश्विक चरों को परिभाषित या घोषित करने वाला शीर्षलेख स्वयं किसी भी प्रकार को परिभाषित नहीं कर सकता है।
इससे पहले कि आप वैरिएबल को परिभाषित करने वाले हेडर को शामिल करें, आप मैक्रो DEFINE_VARIABLES को परिभाषित करते हैं।
वैरिएबल को परिभाषित या घोषित करने वाले हेडर में सामग्री की शैली होती है।
#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#define INITIALIZE(...) = __VA_ARGS__
#else
#define EXTERN extern
#define INITIALIZE(...) /* nothing */
#endif /* DEFINE_VARIABLES */
#ifndef FILE1C_H_INCLUDED
#define FILE1C_H_INCLUDED
struct oddball
{
int a;
int b;
};
extern void use_them(void);
extern int increment(void);
extern int oddball_value(void);
#endif /* FILE1C_H_INCLUDED */
/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2C_H_DEFINITIONS)
#undef FILE2C_H_INCLUDED
#endif
#ifndef FILE2C_H_INCLUDED
#define FILE2C_H_INCLUDED
#include "external.h" /* Support macros EXTERN, INITIALIZE */
#include "file1c.h" /* Type definition for struct oddball */
#if !defined(DEFINE_VARIABLES) || !defined(FILE2C_H_DEFINITIONS)
/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });
#endif /* !DEFINE_VARIABLES || !FILE2C_H_DEFINITIONS */
/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */
#endif /* FILE2C_H_INCLUDED */
#define DEFINE_VARIABLES
#include "file2c.h" /* Variables now defined and initialized */
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
#include "file2c.h"
#include <stdio.h>
void use_them(void)
{
printf("Global variable: %d\n", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
}
#include "file2c.h" /* Declare variables */
#define DEFINE_VARIABLES
#include "file2c.h" /* Variables now defined and initialized */
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
#define DEFINE_VARIABLES
#include "file2c.h" /* Variables now defined and initialized */
#include "file2c.h" /* Declare variables */
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
अगले स्रोत फ़ाइल के लिए स्रोत (एक मुख्य कार्यक्रम प्रदान करता है) को पूरा करता है prog5
, prog6
और prog7
:
#include "file2c.h"
#include <stdio.h>
int main(void)
{
use_them();
global_variable += 19;
use_them();
printf("Increment: %d\n", increment());
printf("Oddball: %d\n", oddball_value());
return 0;
}
prog5
का उपयोग करता है prog5.c
, file3c.c
, file4c.c
, file1c.h
, file2c.h
, external.h
।
prog6
का उपयोग करता है prog5.c
, file5c.c
, file4c.c
, file1c.h
, file2c.h
, external.h
।
prog7
का उपयोग करता है prog5.c
, file6c.c
, file4c.c
, file1c.h
, file2c.h
, external.h
।
यह योजना अधिकांश समस्याओं से बचाती है। आप केवल एक समस्या में भाग लेते हैं यदि कोई शीर्ष लेख जो चर को परिभाषित करता है (जैसे कि file2c.h
) किसी अन्य शीर्षक (कहते हैं file7c.h
) में शामिल है जो चर को परिभाषित करता है। "ऐसा न करें" के अलावा उसके आसपास कोई आसान तरीका नहीं है।
आप आंशिक रूप से संशोधन द्वारा समस्या को हल करने कर सकते हैं file2c.h
में file2d.h
:
/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2D_H_DEFINITIONS)
#undef FILE2D_H_INCLUDED
#endif
#ifndef FILE2D_H_INCLUDED
#define FILE2D_H_INCLUDED
#include "external.h" /* Support macros EXTERN, INITIALIZE */
#include "file1c.h" /* Type definition for struct oddball */
#if !defined(DEFINE_VARIABLES) || !defined(FILE2D_H_DEFINITIONS)
/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });
#endif /* !DEFINE_VARIABLES || !FILE2D_H_DEFINITIONS */
/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2D_H_DEFINITIONS
#undef DEFINE_VARIABLES
#endif /* DEFINE_VARIABLES */
#endif /* FILE2D_H_INCLUDED */
मुद्दा 'हेडर शामिल होना चाहिए #undef DEFINE_VARIABLES
?' यदि आप शीर्ष लेख से बाहर निकलते हैं और किसी भी परिभाषित आमंत्रण के साथ #define
और #undef
:
#define DEFINE_VARIABLES
#include "file2c.h"
#undef DEFINE_VARIABLES
स्रोत कोड में (ताकि हेडर कभी भी मूल्य में परिवर्तन न करें DEFINE_VARIABLES
), तो आपको साफ होना चाहिए। अतिरिक्त लाइन लिखने के लिए याद रखना एक उपद्रव है। एक विकल्प हो सकता है:
#define HEADER_DEFINING_VARIABLES "file2c.h"
#include "externdef.h"
#if defined(HEADER_DEFINING_VARIABLES)
#define DEFINE_VARIABLES
#include HEADER_DEFINING_VARIABLES
#undef DEFINE_VARIABLES
#undef HEADER_DEFINING_VARIABLES
#endif /* HEADER_DEFINING_VARIABLES */
यह एक बालक को दृढ़ होता जा रहा है, लेकिन लगता है कि वह सुरक्षित है (इसका उपयोग कर रहा है file2d.h
, जिसमें कोई नहीं #undef DEFINE_VARIABLES
है file2d.h
)।
/* Declare variables */
#include "file2d.h"
/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"
/* Declare variables - again */
#include "file2d.h"
/* Define variables - again */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE8C_H_DEFINITIONS)
#undef FILE8C_H_INCLUDED
#endif
#ifndef FILE8C_H_INCLUDED
#define FILE8C_H_INCLUDED
#include "external.h" /* Support macros EXTERN, INITIALIZE */
#include "file2d.h" /* struct oddball */
#if !defined(DEFINE_VARIABLES) || !defined(FILE8C_H_DEFINITIONS)
/* Global variable declarations / definitions */
EXTERN struct oddball another INITIALIZE({ 14, 34 });
#endif /* !DEFINE_VARIABLES || !FILE8C_H_DEFINITIONS */
/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE8C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */
#endif /* FILE8C_H_INCLUDED */
/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"
/* Define variables */
#define HEADER_DEFINING_VARIABLES "file8c.h"
#include "externdef.h"
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
अगली दो फाइलें इसके लिए स्रोत को पूरा करती हैं prog8
और prog9
:
#include "file2d.h"
#include <stdio.h>
int main(void)
{
use_them();
global_variable += 19;
use_them();
printf("Increment: %d\n", increment());
printf("Oddball: %d\n", oddball_value());
return 0;
}
#include "file2d.h"
#include <stdio.h>
void use_them(void)
{
printf("Global variable: %d\n", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
}
prog8
का उपयोग करता है prog8.c
, file7c.c
, file9c.c
।
prog9
का उपयोग करता है prog8.c
, file8c.c
, file9c.c
।
हालाँकि, अभ्यास में समस्याएँ अपेक्षाकृत कम होती हैं, खासकर यदि आप मानक सलाह लेते हैं
क्या यह प्रदर्शनी कुछ भी याद आती है?
स्वीकारोक्ति : यहां उल्लिखित 'डुप्लीकेट कोड से बचने की योजना' को विकसित किया गया था क्योंकि समस्या कुछ कोड को प्रभावित करती है जिन पर मैं काम करता हूं (लेकिन खुद का नहीं), और जवाब के पहले भाग में उल्लिखित योजना के साथ एक चिंताजनक चिंता है। हालांकि, मूल योजना आपको चर परिभाषाओं और घोषणाओं को सिंक्रनाइज़ रखने के लिए संशोधित करने के लिए सिर्फ दो स्थानों के साथ छोड़ती है, जो कि कोड बेस में बिखरे हुए बाह्य चर घोषणाएं होने पर एक बड़ा कदम है (जो वास्तव में तब मायने रखता है जब कुल फाइलें हजारों हैं) । हालांकि, नाम fileNc.[ch]
(प्लस external.h
और externdef.h
) के साथ फाइलों में कोड से पता चलता है कि यह काम करने के लिए बनाया जा सकता है। स्पष्ट रूप से, यह एक हेडर जनरेटर स्क्रिप्ट बनाने के लिए मुश्किल नहीं होगा कि आप एक चर को परिभाषित करने और हेडर फ़ाइल की घोषणा के लिए मानकीकृत टेम्पलेट दें।
एनबी ये थोड़े पर्याप्त कोड के साथ खिलौना कार्यक्रम हैं जो उन्हें मामूली दिलचस्प बनाते हैं। ऐसे उदाहरणों के भीतर पुनरावृत्ति है जिन्हें हटाया जा सकता है, लेकिन शैक्षणिक व्याख्या को सरल बनाने के लिए नहीं है। (उदाहरण के लिए: के बीच अंतर prog5.c
और prog8.c
हेडर में से एक का नाम है जो शामिल हैं। यह कोड को पुनर्गठित करना संभव होगा ताकि main()
फ़ंक्शन दोहराया नहीं गया था, लेकिन यह प्रकट की तुलना में अधिक छिपाएगा।)
foo.h
): #define FOO_INITIALIZER { 1, 2, 3, 4, 5 }
एरे के लिए इनिशियलाइज़र को परिभाषित करने, एरे enum { FOO_SIZE = sizeof((int [])FOO_INITIALIZER) / sizeof(((int [])FOO_INITIALIZER)[0]) };
के आकार को प्राप्त करने और एरे extern int foo[];
को घोषित करने के लिए C99 एरे के शाब्दिक का उपयोग कर सकते हैं। । स्पष्ट रूप से, परिभाषा बस होनी चाहिए int foo[FOO_SIZE] = FOO_INITIALIZER;
, हालांकि आकार को वास्तव में परिभाषा में शामिल करने की आवश्यकता नहीं है। यह आपको एक पूर्णांक लगातार हो जाता है FOO_SIZE
।
एक extern
वैरिएबल एक वेरिएबल की घोषणा है (सुधार के लिए sbi के लिए धन्यवाद) जो एक अन्य अनुवाद इकाई में परिभाषित है। इसका मतलब है कि वेरिएबल के लिए स्टोरेज दूसरी फाइल में आवंटित किया गया है।
आप दो है कहो .c
-files test1.c
और test2.c
। आप एक वैश्विक चर को परिभाषित करता है, तो int test1_var;
में test1.c
और आप इस चर का उपयोग करना चाहते हैं test2.c
आप का उपयोग करने के लिए है extern int test1_var;
में test2.c
।
पूरा नमूना:
$ cat test1.c
int test1_var = 5;
$ cat test2.c
#include <stdio.h>
extern int test1_var;
int main(void) {
printf("test1_var = %d\n", test1_var);
return 0;
}
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
extern int test1_var;
करने के लिए int test1_var;
, लिंकर (जीसीसी 5.4.0) अभी भी गुजरता है। तो, क्या extern
वास्तव में इस मामले में जरूरत है?
extern
एक सामान्य विस्तार है जो अक्सर काम करता है - और विशेष रूप से जीसीसी के साथ काम करता है (लेकिन जीसीसी एकमात्र कंपाइलर होने से बहुत दूर है जो इसका समर्थन करता है; यह यूनिक्स सिस्टम पर प्रचलित है)। आप "J.5.11" या अनुभाग "इतना अच्छा नहीं जिस तरह से" मेरा उत्तर में लिए देख सकते हैं (मुझे पता है - यह है लंबे समय तक) और है कि निकट पाठ यह बताते हैं (या कोशिश करता ऐसा करने के लिए)।
एक्सटर्न्स वह कीवर्ड होता है जिसका उपयोग आप यह घोषित करने के लिए करते हैं कि वेरिएबल स्वयं किसी अन्य ट्रांसलेशन यूनिट में रहता है।
तो आप एक अनुवाद इकाई में एक चर का उपयोग करने का फैसला कर सकते हैं और फिर इसे दूसरे से एक्सेस कर सकते हैं, फिर दूसरे में आप इसे बाहरी रूप में घोषित करते हैं और लिंकर द्वारा प्रतीक को हल किया जाएगा।
यदि आप इसे बाहरी घोषित नहीं करते हैं, तो आपको 2 वैरिएबल मिलेंगे, जिनका नाम एक ही है, लेकिन सभी संबंधित नहीं हैं, और वेरिएबल की कई परिभाषाओं की त्रुटि है।
मैं एक बाहरी चर को एक वादे के रूप में सोचना पसंद करता हूं जो आप संकलक से करते हैं।
जब एक बाहरी मुठभेड़, संकलक केवल अपने प्रकार का पता लगा सकता है, न कि जहां यह "रहता है", इसलिए यह संदर्भ को हल नहीं कर सकता है।
आप इसे बता रहे हैं, "मुझ पर भरोसा करें। लिंक समय पर यह संदर्भ फिर से शुरू होगा।"
बाहरी आपको संकलक को यह विश्वास दिलाने के लिए कहता है कि इस चर के लिए मेमोरी कहीं और घोषित की गई है, इसलिए यह मेमोरी को आवंटित / जांचने की कोशिश नहीं करता है।
इसलिए, आप उस फ़ाइल को संकलित कर सकते हैं जिसमें एक बाहरी का संदर्भ है, लेकिन आप लिंक नहीं कर सकते हैं यदि उस मेमोरी को कहीं घोषित नहीं किया गया है।
वैश्विक चर और पुस्तकालयों के लिए उपयोगी है, लेकिन खतरनाक है क्योंकि लिंकर चेक टाइप नहीं करता है।
जोड़ा जा रहा है एक extern
एक चर बदल जाता है परिभाषा एक चर में घोषणा । इस धागे को देखें कि घोषणा और परिभाषा में क्या अंतर है।
declare | define | initialize |
----------------------------------
extern int a; yes no no
-------------
int a = 2019; yes yes yes
-------------
int a; yes yes no
-------------
घोषणा स्मृति आवंटित नहीं करेगी (स्मृति आवंटन के लिए चर को परिभाषित किया जाना चाहिए) लेकिन परिभाषा होगी। यह बाहरी कीवर्ड पर सिर्फ एक और सरल दृश्य है क्योंकि अन्य उत्तर वास्तव में बहुत अच्छे हैं।
बाहरी की सही व्याख्या यह है कि आप संकलक को कुछ बताते हैं। आप संकलक को बताते हैं कि, अभी मौजूद नहीं होने के बावजूद, घोषित चर किसी तरह लिंकर (आमतौर पर किसी अन्य ऑब्जेक्ट (फ़ाइल)) में पाया जाएगा। लिंकर फिर सब कुछ खोजने और इसे एक साथ रखने के लिए भाग्यशाली व्यक्ति होगा, चाहे आपके पास कुछ बाहरी घोषणाएं थीं या नहीं।
C में एक फ़ाइल के अंदर एक चर का उदाहरण example.c को स्थानीय गुंजाइश दी गई है। कंपाइलर को उम्मीद है कि वैरिएबल की फाइल के अंदर वैरिएबल की परिभाषा होगी। जब उसे समान नहीं मिलेगा, तो वह एक एरर फेंक देगा। दूसरी तरफ फंक्शन डिफ़ॉल्ट ग्लोबल स्कोप द्वारा होता है। इस प्रकार आपको संकलक को स्पष्ट रूप से उल्लेख नहीं करना है "यार देखो ... तुम्हें इस समारोह की परिभाषा यहाँ मिल सकती है"। फ़ाइल सहित एक फ़ंक्शन के लिए जिसमें इसकी घोषणा पर्याप्त है। (वह फ़ाइल जिसे आप वास्तव में हेडर फ़ाइल कहते हैं)। उदाहरण के लिए निम्नलिखित 2 फाइलों पर विचार करें:
example.c
#include<stdio.h>
extern int a;
main(){
printf("The value of a is <%d>\n",a);
}
example1.c
int a = 5;
अब जब आप निम्न आदेशों का उपयोग करते हुए दो फाइलों को एक साथ संकलित करते हैं:
चरण 1) cc -o ex example.c example1.c चरण 2) ./ ex
आपको निम्न आउटपुट मिलते हैं: a का मान <5> है
जीसीसी ईएलएफ लिनक्स कार्यान्वयन
अन्य उत्तरों ने भाषा के उपयोग के पक्ष को कवर किया है, तो अब इस पर एक नज़र डालते हैं कि इसे कैसे लागू किया जाता है।
main.c
#include <stdio.h>
int not_extern_int = 1;
extern int extern_int;
void main() {
printf("%d\n", not_extern_int);
printf("%d\n", extern_int);
}
संकलन और विघटित:
gcc -c main.c
readelf -s main.o
आउटपुट में शामिल हैं:
Num: Value Size Type Bind Vis Ndx Name
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 not_extern_int
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND extern_int
सिस्टम वी ABI अद्यतन ELF कल्पना "प्रतीक तालिका" अध्याय बताते हैं:
SHN_UNDEF इस खंड तालिका सूचकांक का अर्थ है कि प्रतीक अपरिभाषित है। जब लिंक संपादक इस ऑब्जेक्ट फ़ाइल को किसी अन्य के साथ जोड़ता है जो संकेतित प्रतीक को परिभाषित करता है, तो प्रतीक के लिए इस फ़ाइल के संदर्भ वास्तविक परिभाषा से जुड़े होंगे।
जो मूल रूप से व्यवहार सी मानक के देता है extern
चर।
अब से, अंतिम प्रोग्राम बनाने के लिए लिंकर का काम है, लेकिन extern
स्रोत कोड से ऑब्जेक्ट फ़ाइल में जानकारी पहले ही निकाली जा चुकी है।
जीसीसी 4.8 पर परीक्षण किया गया।
सी ++ 17 इनलाइन चर
C ++ 17 में, आप बाहरी लोगों के बजाय इनलाइन चर का उपयोग करना चाह सकते हैं, क्योंकि वे उपयोग करने के लिए सरल हैं (हेडर पर सिर्फ एक बार परिभाषित किया जा सकता है) और अधिक शक्तिशाली (समर्थन कॉन्स्ट्रेप)। देखें: C और C ++ में 'स्थैतिक स्थैतिक' का क्या अर्थ है?
readelf
या nm
मददगार हो सकता है, आपने इसका उपयोग नहीं करने के मूल सिद्धांतों के बारे में नहीं बताया है extern
और न ही वास्तविक परिभाषा के साथ पहला कार्यक्रम पूरा किया है। आपका कोड भी उपयोग नहीं करता है notExtern
। एक नामकरण की समस्या है, भी: हालांकि notExtern
यहां घोषित के बजाय परिभाषित किया गया है extern
, यह एक बाहरी चर है जिसे अन्य स्रोत फ़ाइलों द्वारा एक्सेस किया जा सकता है अगर उन अनुवाद इकाइयों में एक उपयुक्त घोषणा (जिसमें आवश्यकता होगी extern int notExtern;
!)।
notExtern
बदसूरत का उपयोग नहीं , यह तय किया गया था। नामकरण के बारे में, मुझे बताएं कि क्या आपका कोई बेहतर नाम है। बेशक, यह एक वास्तविक कार्यक्रम के लिए एक अच्छा नाम नहीं होगा, लेकिन मुझे लगता है कि यह अच्छी तरह से यहां प्रचलित भूमिका को फिट करता है।
global_def
यहाँ परिभाषित चर के लिए क्या है, और extern_ref
कुछ अन्य मॉड्यूल में परिभाषित चर के लिए? क्या उनके पास उपयुक्त समरूपता होगी? आप अभी भी int extern_ref = 57;
उस फ़ाइल के साथ या उस चीज़ के साथ समाप्त होते हैं जहाँ इसे परिभाषित किया गया है, इसलिए नाम काफी आदर्श नहीं है, लेकिन एकल स्रोत फ़ाइल के संदर्भ में, यह एक उचित विकल्प है। extern int global_def;
एक हेडर में होने के कारण एक समस्या नहीं है, यह मुझे लगता है। निश्चित रूप से आप पर निर्भर है।
extern
आपके प्रोग्राम के एक मॉड्यूल को आपके प्रोग्राम के किसी अन्य मॉड्यूल में घोषित वैश्विक चर या फ़ंक्शन तक पहुंचने की अनुमति देता है। आपके पास आमतौर पर हेडर फ़ाइलों में घोषित बाहरी चर होते हैं।
यदि आप अपने चर या कार्यों तक पहुँचने के लिए कोई कार्यक्रम नहीं चाहते हैं, तो आप static
संकलक को यह बताते हैं कि इस मॉड्यूल के बाहर इस चर या फ़ंक्शन का उपयोग नहीं किया जा सकता है।
सबसे पहले, extern
कीवर्ड एक चर को परिभाषित करने के लिए नहीं किया जाता है; बल्कि इसका उपयोग चर घोषित करने के लिए किया जाता है। मैं कह सकता हूं कि extern
भंडारण वर्ग है, डेटा प्रकार नहीं।
extern
अन्य C फ़ाइलों या बाहरी घटकों को यह बताने के लिए उपयोग किया जाता है कि यह चर पहले से ही कहीं न कहीं परिभाषित है। उदाहरण: यदि आप एक पुस्तकालय का निर्माण कर रहे हैं, तो पुस्तकालय में कहीं पर भी वैश्विक परिवर्तनशीलता को परिभाषित करने की आवश्यकता नहीं है। लाइब्रेरी को सीधे संकलित किया जाएगा, लेकिन फ़ाइल को लिंक करते समय, यह परिभाषा की जांच करता है।
extern
उपयोग किया जाता है इसलिए एक first.c
फ़ाइल में किसी अन्य second.c
फ़ाइल में वैश्विक पैरामीटर तक पूर्ण पहुंच हो सकती है ।
extern
में घोषित किया जा सकता first.c
फ़ाइल या हेडर फाइल में से किसी में first.c
भी शामिल है।
extern
घोषणा एक हेडर में होनी चाहिए, न first.c
कि इसलिए कि यदि प्रकार बदल जाता है, तो घोषणा भी बदल जाएगी। इसके अलावा, हैडर जो घोषणा करता है, चर second.c
को यह सुनिश्चित करने के लिए शामिल किया जाना चाहिए कि परिभाषा घोषणा के अनुरूप है। शीर्षलेख में घोषणा वह गोंद है जो इसे एक साथ रखता है; यह फ़ाइलों को अलग-अलग संकलित करने की अनुमति देता है, लेकिन यह सुनिश्चित करता है कि उनके पास वैश्विक चर के प्रकार के अनुरूप दृश्य हो।
Xc8 के साथ आपको प्रत्येक फ़ाइल में एक चर के रूप में घोषित करने के बारे में सावधान रहना होगा, जैसा कि आप, गलती से, int
एक फ़ाइल में कुछ और char
दूसरे में एक कहने की घोषणा कर सकते हैं । इससे चर का भ्रष्टाचार हो सकता है।
यह समस्या सुंदर ढंग से कुछ 15 साल पहले एक माइक्रोचिप मंच में हल किया गया था / * देखें "http: www.htsoft.com" / / "मंच / सब / showflat.php / कैट / 0 / संख्या / 18,766 / एक / 0 / पेज / 0 # 18,766 "
लेकिन यह लिंक अब काम नहीं कर रहा है ...
तो मैं जल्दी से इसे समझाने की कोशिश करूँगा; Global.h नामक फाइल बनाना।
इसमें निम्नलिखित की घोषणा करें
#ifdef MAIN_C
#define GLOBAL
/* #warning COMPILING MAIN.C */
#else
#define GLOBAL extern
#endif
GLOBAL unsigned char testing_mode; // example var used in several C files
अब फाइल में main.c
#define MAIN_C 1
#include "global.h"
#undef MAIN_C
यह मुख्य साधन में है। चर को ए के रूप में घोषित किया जाएगा unsigned char
।
अब ग्लोबल सहित अन्य फाइलों में। यह उस फाइल के लिए एक बाहरी के रूप में घोषित किया जाएगा ।
extern unsigned char testing_mode;
लेकिन इसे सही रूप में घोषित किया जाएगा unsigned char
।
पुराने फ़ोरम पोस्ट ने शायद इसे थोड़ा और स्पष्ट रूप से समझाया। लेकिन gotcha
कंपाइलर का उपयोग करते समय यह एक वास्तविक क्षमता है जो आपको एक फ़ाइल में एक चर घोषित करने की अनुमति देता है और फिर दूसरे में एक अलग प्रकार के रूप में बाहरी घोषित करता है। इससे जुड़ी समस्याएं यह हैं कि यदि आप कहते हैं कि परीक्षण_मोड को एक अन्य फ़ाइल में एक इंट के रूप में घोषित किया गया है तो उसे लगता है कि यह एक 16 बिट संस्करण था और राम के कुछ अन्य भाग को अधिलेखित करता है, संभवतः एक और चर को भ्रष्ट करता है। डिबग करना मुश्किल!
एक बहुत ही कम समाधान मैं एक हेडर फ़ाइल को किसी वस्तु के बाहरी संदर्भ या वास्तविक कार्यान्वयन में शामिल करने की अनुमति देता हूं। फ़ाइल जिसमें वास्तव में ऑब्जेक्ट होता है वह बस करता है #define GLOBAL_FOO_IMPLEMENTATION
। फिर जब मैं इस फाइल में एक नई वस्तु जोड़ता हूं तो यह उस फाइल में दिखाई देता है, वह भी बिना मेरे द्वारा परिभाषा को कॉपी और पेस्ट किए बिना।
मैं कई फाइलों में इस पैटर्न का उपयोग करता हूं। तो आदेश के रूप में स्वयं संभव के रूप में निहित बातें रखने के लिए, मैं सिर्फ प्रत्येक शीर्षक में एक वैश्विक मैक्रो का पुन: उपयोग। मेरा हैडर इस तरह दिखता है:
//file foo_globals.h
#pragma once
#include "foo.h" //contains definition of foo
#ifdef GLOBAL
#undef GLOBAL
#endif
#ifdef GLOBAL_FOO_IMPLEMENTATION
#define GLOBAL
#else
#define GLOBAL extern
#endif
GLOBAL Foo foo1;
GLOBAL Foo foo2;
//file main.cpp
#define GLOBAL_FOO_IMPLEMENTATION
#include "foo_globals.h"
//file uses_extern_foo.cpp
#include "foo_globals.h