2015-06-16 6 views
17

मुझे पता है कि यह एक आम सवाल है लेकिन मैं अभी भी अपने सिर को पूरी तरह से नहीं प्राप्त कर सकता हूं।शीर्षलेख फ़ाइल में केवल एक बार पूरे कार्यक्रम में शामिल है?

सी या कई अलग-अलग स्रोतों और हेडर फ़ाइलों से उत्पन्न सी ++ प्रोग्राम में, प्रत्येक हेडर फ़ाइल को हेडर गार्ड का उपयोग होने पर पूरे कोड में केवल एक बार शामिल किया जाएगा?

किसी ने मुझे पहले बताया था कि एक हेडर फ़ाइल (गार्ड शामिल करने के साथ) केवल एक बार एक अनुवाद इकाई में शामिल हो जाएगी लेकिन पूरे कोड में कई बार शामिल होगा। क्या ये सच है?

यदि यह पूरे कोड में केवल एक बार शामिल हो जाता है, जब एक फ़ाइल इसे शामिल करना चाहती है और प्रीप्रोसेसर का पता लगाता है कि इसे पहले से ही शामिल किया गया है, तो वह फ़ाइल कैसे उपयोग करती है जो इसे पहले कोड में बताती है शामिल

+8

* "एक हेडर फ़ाइल (गार्ड शामिल करने के साथ) केवल एक बार एक अनुवाद इकाई में शामिल हो जाएगी लेकिन पूरे कोड में कई बार। क्या यह सच है ?"* हाँ। प्रति कार्यक्रम एक बार नहीं, यह प्रति अनुवाद इकाई में एक बार (अधिकतर) है। – CoryKramer

+6

गार्ड शामिल करने के साथ, शीर्षलेख को अभी भी एक अनुवाद इकाई में कई बार शामिल किया जा सकता है - लेकिन इसमें से अधिकांश (गार्ड के भीतर हिस्सा) पहली बार छोड़ा जाएगा। प्रीप्रोसेसर इस बात पर परवाह नहीं करता है कि इसे पहले कहाँ शामिल किया गया था ... केवल तभी गार्ड के मैक्रो को परिभाषित किया गया है (यदि गार्ड सही तरीके से स्थापित होते हैं तो पहली बार इसमें शामिल होता है)। – Dmitri

+0

धन्यवाद। मुझे इस साइट से प्यार है :) सुपर फास्ट प्रतिक्रियाएं और महान जवाब। – Engineer999

उत्तर

16

संकलन शुरू होने से पहले "हेडर फ़ाइल" वास्तव में प्री-प्रोसेसर द्वारा डाला जाता है। इसके बारे में सोचें कि इसके #include निर्देश को "प्रतिस्थापित" करें।

गार्ड ...

#ifndef MY_HEADER_H 
#define MY_HEADER_H 

.... 

#endif 

... प्रतिस्थापन के बाद मार डाला जाता है। इसलिए, हेडर को वास्तव में कई बार शामिल किया जा सकता है, लेकिन टेक्स्ट का "संरक्षित" भाग केवल प्रीप्रोसेसर द्वारा संकलक को पास किया जाता है।

इसलिए, यदि शीर्षलेख में कोई कोड-जनरेशन परिभाषाएं हैं, तो निश्चित रूप से - संकलन इकाई (उर्फ "मॉड्यूल") की ऑब्जेक्ट फ़ाइल में शामिल किया जाएगा। यदि एक ही शीर्षलेख #include एकाधिक मॉड्यूल में समर्पित है, तो यह कई बार दिखाई देगा।

static परिभाषाओं के लिए, यह कोई समस्या नहीं है, क्योंकि ये मॉड्यूल से बाहर दिखाई नहीं देंगे (उर्फ फ़ाइल स्कोप)। कार्यक्रम-वैश्विक परिभाषाओं के लिए, यह अलग है और परिणामस्वरूप "एकाधिक परिभाषाएं" त्रुटि होगी।

नोट: यह ज्यादातर सी के लिए है। सी ++ के लिए, महत्वपूर्ण अंतर हैं, कक्षाओं के रूप में, आदि। कई वैश्विक वस्तुओं की अनुमति के लिए अतिरिक्त जटिलता जोड़ें।

+3

आप गैर मानक '#pragma एक बार' का भी उल्लेख कर सकते हैं, यह इसके अस्तित्व को जानने के लायक है – dlavila

+3

@dlavila: मैंने किसी उद्देश्य के लिए नहीं किया था। किसी पर भरोसा नहीं करना चाहिए, इसलिए आपको गार्ड को विज्ञापन देना होगा। इसके अलावा, यह बूस्ट जैसे लाइब्रेरी को अस्वीकार कर सकता है जो जानबूझकर एकाधिक उपयोगों का उपयोग करता है। (मैं हमेशा मानक से चिपकने वाला नहीं हूं, लेकिन लिंक-एक बार बहुत कम लाभ होता है और इससे अधिक परेशानी हो सकती है)। – Olaf

+4

वैसे भी इसके अस्तित्व को जानने के लायक है, प्रतिस्थापन के रूप में नहीं, लेकिन कभी-कभी यह बहुत उपयोगी हो सकता है, उदाहरण के लिए: यदि आपको यकीन है कि यह समर्थित है (आपकी कंपनी में एसडब्ल्यूई लोगों ने आपको बताया है) और आप हजारों शीर्षकों के साथ एक परियोजना में काम कर रहे हैं (संकलन समय बहुत तेज हो सकता है)। – dlavila

5

हेडर फ़ाइल प्रति अनुवाद इकाई में एक बार शामिल की जाएगी, हां। इसे प्रति कार्यक्रम कई बार शामिल किया जा सकता है, क्योंकि संकलन प्रक्रिया के लिए प्रत्येक अनुवाद इकाई को अलग से संभाला जाता है। वे एक पूर्ण कार्यक्रम बनाने के लिए लिंकिंग प्रक्रिया के दौरान एक साथ लाए जाते हैं।

+0

क्या होगा यदि हमारे पास हेडर फ़ाइल में फ़ंक्शन परिभाषाएं हों। क्या पूरे कार्यक्रम में एक ही समारोह की कई परिभाषाएं नहीं हैं जिनकी अनुमति नहीं है? – Engineer999

+0

@ Engineer999 फ़ंक्शन परिभाषा आमतौर पर हेडर फ़ाइलों में शामिल नहीं होती है, इनलाइन फ़ंक्शंस को छोड़कर – Random832

+0

फ़ंक्शन को अंतर्निहित रूप से इनलाइन नहीं किया जाता है यदि वे किसी हेडर फ़ाइल (पाठ्यक्रम के C++) में किसी क्लास बॉडी के अंदर परिभाषित होते हैं। तब से कई परिभाषाओं का सामना कैसे किया जाता है? – Engineer999

6

उचित include guards के साथ एक हेडर फ़ाइल केवल प्रति अनुवाद इकाई शामिल होगी। कड़ाई से बोलते हुए, यह कई बार शामिल हो सकता है, लेकिन प्रीप्रोसेसर #ifndef और #endif के बीच के हिस्सों को बाद के समावेशन पर छोड़ दिया जाएगा। यदि सही तरीके से किया जाता है, तो यह फ़ाइल के सभी (या अधिकतर) होना चाहिए।

एक अनुवाद इकाई आमतौर पर "स्रोत फ़ाइल" से मेल खाती है, हालांकि कुछ अस्पष्ट कार्यान्वयन एक अलग परिभाषा का उपयोग कर सकते हैं। यदि एक अलग संकलित स्रोत फ़ाइल में एक ही शीर्षलेख शामिल है, तो प्रीप्रोसेसर में जानने का कोई तरीका नहीं है कि एक और फ़ाइल पहले ही इसमें शामिल हो चुकी है, या कोई अन्य फ़ाइल एक ही प्रोजेक्ट का हिस्सा थी।

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

source   header source header header 
    \   /  \ | //
    \  /  \ | //
    PREPROCESSOR   PREPROCESSOR 
     |      | 
     V      V 
preprocessed code  preprocessed code 
     |      | 
    COMPILER    COMPILER 
     |      | 
     V      V 
    object code    object code 
      \   /
       \  /
       \  /
       LINKER 
        | 
        V 
       executable 

Preprocessing

#include इस दिशा में पहला कदम के लिए है:

20

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

तो AB और C शामिल है, और BC, शामिल A के लिए पूर्वप्रक्रमक के उत्पादन C की संसाधित पाठ दो बार शामिल होंगे।

यह एक समस्या है, क्योंकि इसके परिणामस्वरूप डुप्लिकेट घोषणाएं होंगी। प्रीप्रोसेसर वैरिएबल का उपयोग करने का एक उपाय यह है कि क्या स्रोत कोड शामिल किया गया है (उर्फ हेडर गार्ड)।

#ifndef EXAMPLE_H 
#define EXAMPLE_H 

// header contents 

#endif 

पहली बार, EXAMPLE_H अपरिभाषित है, और पूर्वप्रक्रमक ifndef/endif ब्लॉक के भीतर सामग्री का मूल्यांकन करेंगे। दूसरी बार, यह उस ब्लॉक को छोड़ देगा। तो संसाधित आउटपुट बदलता है, और परिभाषा केवल एक बार शामिल होती है।

#pragma once 

// header contents 

आप पता लगा सकते हैं कि कैसे पोर्टेबल आप अपने सी हैं:

यह इतना आम एक गैर मानक निर्देश कुछ compilers कि कम है और एक अद्वितीय पूर्वप्रक्रमक चर चुनने की आवश्यकता नहीं है द्वारा कार्यान्वित है कि वहाँ है/सी ++ कोड, और कौन सा हेडर गार्ड उपयोग करने के लिए।

शीर्षलेख गार्ड यह सुनिश्चित करेंगे कि प्रत्येक शीर्षलेख फ़ाइल की सामग्री एक बार अनुवाद इकाई के लिए प्रीप्रोसेस्ड कोड में मौजूद हो।

संकलन

संकलक अपने preprocessed C/C++ से मशीन कोड उत्पन्न करता है।

आम तौर पर, हेडर फ़ाइलों में केवल घोषणाएं शामिल होती हैं, न कि वास्तविक परिभाषाएं (उर्फ कार्यान्वयन)। कंपाइलर में किसी भी चीज के लिए एक प्रतीक तालिका शामिल है जो वर्तमान में परिभाषा खो रही है।

लिंकर को लिंक वस्तु फ़ाइलों को जोड़ती है। यह प्रतीक तालिका के संदर्भों के साथ परिभाषाओं (उर्फ कार्यान्वयन) से मेल खाता है।

यह हो सकता है कि दो ऑब्जेक्ट फ़ाइलें परिभाषा प्रदान करें, और लिंकर एक ले जाएगा।ऐसा होता है यदि आपने अपने शीर्षकों में निष्पादन योग्य कोड डाला है। यह आम तौर पर सी में नहीं होता है, लेकिन यह टेम्पलेट्स के कारण सी ++ में अक्सर होता है।

हैडर "कोड", घोषणाओं या परिभाषाओं कि क्या, सब वस्तु फ़ाइलों में कई बार शामिल किया गया है लेकिन लिंकर कि सभी को एक साथ विलीन हो जाती है, इतना है कि यह निष्पादन में एक बार ही मौजूद है। (मैं इनलाइन कार्यों को छोड़ रहा हूं, जो कई बार मौजूद हैं।)

+0

उत्कृष्ट उत्तर, लेकिन आपको यह नहीं कहना चाहिए "आम तौर पर, हेडर फ़ाइलों में केवल ** घोषणाएं ** शामिल हैं, वास्तविक कार्यान्वयन नहीं।" चूंकि परिभाषा आमतौर पर सी/सी ++ में कार्यान्वयन के पर्याय का पर्याय बनती है? –

+1

@ ब्रायनशॉ, हाँ, मदद के लिए धन्यवाद। –

संबंधित मुद्दे