शीर्ष लेख फ़ाइलों में परिवर्तनीय घोषणाएं - स्थिर या नहीं?


91

जब कुछ दूर करने के बाद #definesमैं सी + + हैडर फ़ाइल में निम्नलिखित के समान घोषणाओं के पार आया:

static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;

सवाल यह है कि क्या अंतर है, अगर कोई है, तो स्थैतिक बना देगा? ध्यान दें कि क्लासिक #ifndef HEADER #define HEADER #endifट्रिक (यदि वह मायने रखता है) के कारण हेडर का एकाधिक समावेश संभव नहीं है ।

क्या स्थैतिक का मतलब केवल एक प्रति VALबनाया गया है, यदि शीर्ष लेख में एक से अधिक स्रोत फ़ाइल शामिल हैं?


जवाबों:


107

इसका staticमतलब यह है कि इसमें VALशामिल प्रत्येक स्रोत फ़ाइल के लिए बनाई गई एक प्रति होगी । लेकिन इसका मतलब यह भी है कि कई समावेशों के परिणामस्वरूप कई परिभाषाएँ नहीं होंगी, VALजो लिंक समय पर टकराएंगी। सी में, बिना staticआपको यह सुनिश्चित करने की आवश्यकता होगी कि केवल एक स्रोत फ़ाइल को परिभाषित किया जाए VALजबकि अन्य स्रोत फ़ाइलों को घोषित किया जाए extern। आमतौर पर एक स्रोत फ़ाइल में इसे (संभवतः एक इनिलाइज़र के साथ) परिभाषित करके ऐसा किया जाएगा और externघोषणा को हेडर फ़ाइल में रखा जाएगा ।

static वैश्विक स्तर पर चर केवल अपनी स्वयं की स्रोत फ़ाइल में दिखाई देते हैं, चाहे वे एक सम्मिलित के माध्यम से वहां पहुंचे या मुख्य फ़ाइल में थे।


संपादक का ध्यान दें: C ++ में, constन तो staticऔर न ही externउनके घोषणापत्र में कीवर्ड्स के साथ ऑब्जेक्ट निहित हैं static


मैं अंतिम वाक्य का प्रशंसक हूं, अविश्वसनीय रूप से सहायक। मैंने जवाब नहीं दिया 'कारण 42 बेहतर है। संपादित करें: व्याकरण
RealDeal_EE'18

"स्थिर का मतलब है कि इसमें शामिल प्रत्येक स्रोत फ़ाइल के लिए बनाई गई वैल की एक प्रति होगी।" इसका मतलब यह लगता है कि अगर दो स्रोत फ़ाइलों में हेडर फ़ाइल शामिल है तो वैल की दो प्रतियां होंगी। मैं उम्मीद कर रहा हूं कि यह सच नहीं है, और यह कि वैल की एक एकल आवृत्ति हमेशा होती है, चाहे कितनी भी फाइलें हेडर शामिल हों।
Brent212

4
@ Brent212 कंपाइलर को पता नहीं होता है कि हेडर फाइल या मेन फाइल से डिक्लेरेशन / डेफिनिशन आया है या नहीं। तो तुम व्यर्थ आशा करते हो। वैल की दो प्रतियाँ होंगी यदि कोई मूर्खतापूर्ण था और एक हेडर फ़ाइल में स्थिर परिभाषा रखता है और इसे दो स्रोतों में शामिल किया गया है।
जस्टाल्ट

1
const मानों का आंतरिक संबंध C ++ में है
adrianN

112

staticऔर externफ़ाइल-दायरे वाले चर पर टैग निर्धारित करते हैं कि वे अन्य अनुवाद इकाइयों (यानी अन्य में उपलब्ध हैं .cया .cppफ़ाइलें)।

  • staticचर को आंतरिक लिंकेज देता है, इसे अन्य अनुवाद इकाइयों से छिपाता है। हालाँकि, आंतरिक लिंकेज वाले चर को कई अनुवाद इकाइयों में परिभाषित किया जा सकता है।

  • externवैरिएबल एक्सटर्नल लिंकेज देता है, जिससे यह अन्य ट्रांसलेशन यूनिट को दिखाई देता है। आमतौर पर इसका मतलब है कि चर को केवल एक अनुवाद इकाई में परिभाषित किया जाना चाहिए।

डिफ़ॉल्ट (जब आप निर्दिष्ट नहीं करते हैं staticया नहीं extern) उन क्षेत्रों में से एक है जिसमें C और C ++ भिन्न हैं।

  • C में, फ़ाइल-स्कूप किए गए चर externडिफ़ॉल्ट रूप से (बाहरी लिंकेज) हैं। यदि आप C का उपयोग कर रहे हैं, VALहै staticऔर ANOTHER_VALहै extern

  • C ++ में, फ़ाइल-स्कोप किए गए चर staticडिफ़ॉल्ट रूप से (आंतरिक लिंकेज) हैं यदि वे हैं const, और externडिफ़ॉल्ट रूप से यदि वे नहीं हैं। यदि आप C ++ का उपयोग कर रहे हैं, तो दोनों हैं VALऔर ANOTHER_VALहैं static

सी विनिर्देश के मसौदे से :

6.2.2 पहचानकर्ताओं के संबंध ... -5 - यदि किसी फ़ंक्शन के लिए पहचानकर्ता की घोषणा में कोई भंडारण-वर्ग निर्दिष्टकर्ता नहीं है, तो इसका लिंकेज ठीक उसी तरह निर्धारित किया जाता है जैसे कि इसे स्टोरेज-क्लास स्पेसियर एक्सटर्नल के साथ घोषित किया गया था। यदि किसी ऑब्जेक्ट के लिए पहचानकर्ता की घोषणा में फाइल स्कोप है और कोई स्टोरेज-क्लास स्पेसियर नहीं है, तो इसका लिंकेज बाहरी है।

C ++ विनिर्देशन के मसौदे से :

.१.१ - स्टोरेज क्लास स्पेसिफ़र्स [dcl.stc] ... -6- एक नेमस्पेस स्कोप के बिना एक नेमस्पेस स्कोप में घोषित नाम में बाहरी लिंकेज है, जब तक कि इसमें पिछले लिंकेज के कारण इंटरनल लिंकेज न हो और बशर्ते कि यह न हो घोषित घोषित की गई वस्तुओं और स्पष्ट रूप से घोषित बाहरी नहीं होने के कारण आंतरिक संबंध हैं।


47

स्टेटिक का मतलब होगा कि आपको प्रति फ़ाइल एक कॉपी मिलेगी, लेकिन दूसरों के विपरीत यह कहा गया है कि ऐसा करना पूरी तरह से कानूनी है। आप छोटे कोड नमूने के साथ आसानी से इसका परीक्षण कर सकते हैं:

test.h:

static int TEST = 0;
void test();

test1.cpp:

#include <iostream>
#include "test.h"

int main(void) {
    std::cout << &TEST << std::endl;
    test();
}

test2.cpp:

#include <iostream>
#include "test.h"

void test() {
    std::cout << &TEST << std::endl;
}

इसे चलाने से आपको यह आउटपुट मिलता है:

0x446020
0x446040


5
उदाहरण के लिए धन्यवाद!
काइरोल

मुझे आश्चर्य है अगर TESTथे const, अगर LTO एक भी स्मृति स्थान में अनुकूलन करने के लिए सक्षम होगा। लेकिन -O3 -fltoजीसीसी 8.1 नहीं किया।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

ऐसा करना गैरकानूनी होगा - भले ही यह स्थिर हो, स्थैतिक गारंटी देता है कि प्रत्येक उदाहरण संकलन इकाई के लिए स्थानीय है। यह संभवतः निरंतर मान को इनलाइन कर सकता है यदि इसे एक स्थिरांक के रूप में उपयोग किया जाता है, लेकिन जब से हम इसका पता लेते हैं तब इसे एक अद्वितीय पॉइंटर वापस करना होता है।
स्लेडलीम

6

constC ++ में वैरिएबल में इंटरनल लिंकेज होता है। इसलिए, उपयोग करने staticका कोई प्रभाव नहीं है।

आह

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

two.cpp

#include "a.h"

func1()
{
   cout << i;
}

यदि यह एक C प्रोग्राम होता, तो आपको i(बाहरी लिंकेज के कारण) 'एकाधिक परिभाषा' त्रुटि मिलती ।


2
खैर, उपयोग staticका यह प्रभाव है कि यह बड़े करीने से किसी को कोडिंग करने के इरादे और जागरूकता का संकेत देता है, जो कभी भी बुरी चीज नहीं है। मेरे लिए यह ऐसा है virtualजब ओवरराइड करना शामिल है: हमारे पास नहीं है, लेकिन चीजें बहुत अधिक सहज दिखती हैं - और अन्य घोषणाओं के अनुरूप - जब हम करते हैं।
अंडरस्कोर_ड

आपको सी में कई परिभाषा त्रुटि मिल सकती है। यह अपरिभाषित व्यवहार है जिसमें कोई निदान आवश्यक नहीं है
एमएम

5

कोड के इस स्तर पर स्थिर घोषणा का मतलब है कि वैरिएबल केवल वर्तमान संकलन इकाई में दिखाई दे रहा है। इसका मतलब है कि उस मॉड्यूल के भीतर केवल कोड उस चर को देखेंगे।

यदि आपके पास एक हेडर फाइल है जो एक वैरिएबल स्टेटिक की घोषणा करती है और वह हेडर कई C / CPP फाइलों में शामिल है, तो वह वेरिएबल उन मॉड्यूल्स के लिए "लोकल" होगा। उस शीर्ष स्थान के लिए N वैरिएबल की N प्रतियां होंगी, जिसमें शीर्षलेख शामिल है। वे एक-दूसरे से संबंधित नहीं हैं। उन स्रोत फ़ाइलों में से कोई भी कोड केवल उस मॉड्यूल के भीतर घोषित चर का संदर्भ देगा।

इस विशेष स्थिति में, 'स्थिर' कीवर्ड कोई लाभ नहीं देता है। मुझे कुछ याद आ रहा है, लेकिन यह कोई फर्क नहीं पड़ता है - मैंने ऐसा कुछ पहले कभी नहीं देखा है।

इनलाइनिंग के रूप में, इस मामले में वेरिएबल इनबिल्ड है, लेकिन ऐसा केवल इसलिए है क्योंकि इसे कॉन्स्टेंट घोषित किया गया है। कंपाइलर इनलाइन मॉड्यूल स्टैटिक वैरिएबल की अधिक संभावना हो सकती है, लेकिन यह स्थिति और कोड संकलित होने पर निर्भर है। इस बात की कोई गारंटी नहीं है कि कंपाइलर 'स्टेटिक्स' को इनलाइन करेगा।


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

इस मामले में, के कारण में const, staticनिहित है और इसलिए वैकल्पिक है। कोरोलरी यह है कि माइक एफ का दावा है कि कई परिभाषा त्रुटियों के लिए कोई संवेदनशीलता नहीं है।
अंडरस्कोर_ड

2

सी बुक (मुफ्त ऑनलाइन) में लिंकेज के बारे में एक अध्याय है, जो 'स्टेटिक' के अर्थ को और अधिक विस्तार से बताता है (हालाँकि अन्य टिप्पणियों में सही उत्तर पहले से ही दिया गया है): http://publications.gbdirect.co.uk/c_book /chapter4/linkage.html


2

प्रश्न का उत्तर देने के लिए, "क्या स्थैतिक का अर्थ केवल वैल की एक प्रति है, यदि हेडर को एक से अधिक स्रोतों द्वारा शामिल किया जाता है? ... ...

सं । वैल को हमेशा हर फ़ाइल में अलग से परिभाषित किया जाएगा जिसमें हेडर शामिल है।

C और C ++ के मानक इस मामले में अंतर पैदा करते हैं।

C में, फ़ाइल-स्कूप किए गए चर डिफ़ॉल्ट रूप से बाहरी हैं। यदि आप C का उपयोग कर रहे हैं, तो VAL स्थिर है और ANOTHER_VAL बाहरी है।

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

C ++ में, फ़ाइल-स्कोप किए गए चर डिफ़ॉल्ट रूप से स्थिर होते हैं यदि वे स्थिर होते हैं, और डिफ़ॉल्ट रूप से बाहरी होते हैं यदि वे नहीं हैं। यदि आप C ++ का उपयोग कर रहे हैं, तो VAL और ANOTHER_VAL दोनों स्थिर हैं।

आपको इस तथ्य पर भी ध्यान देने की आवश्यकता है कि दोनों चर निर्धारित नक्षत्र हैं। आदर्श रूप से संकलक हमेशा इन चरों को इनलाइन करना चुनता है और उनके लिए कोई भंडारण शामिल नहीं करता है। भंडारण का आवंटन क्यों किया जा सकता है इसके कारणों की एक पूरी मेजबानी है। मैं सोच सकता हूँ ...

  • डिबग विकल्प
  • फ़ाइल में लिया गया पता
  • संकलक हमेशा भंडारण आवंटित करता है (जटिल कास्ट प्रकार आसानी से इनलेट नहीं किया जा सकता है, इसलिए बुनियादी प्रकारों के लिए एक विशेष मामला बन जाता है)

नोट: अमूर्त मशीन में प्रत्येक अलग अनुवाद इकाई में वैल की एक प्रति होती है जिसमें हेडर शामिल होता है। व्यवहार में लिंकर उन्हें वैसे भी संयोजित करने का निर्णय ले सकता है, और कंपाइलर कुछ या सभी को पहले निकाल सकता है।
एमएम

1

यह मानते हुए कि ये घोषणाएँ वैश्विक दायरे में हैं (अर्थात सदस्य चर नहीं हैं), तब:

स्थैतिक का अर्थ है 'आंतरिक जुड़ाव'। इस मामले में, के बाद से यह घोषित किया जाता है स्थिरांक इस अनुकूलित किया जा सकता है / संकलक द्वारा inlined। यदि आप const को छोड़ देते हैं तो संकलक को प्रत्येक संकलन इकाई में भंडारण आवंटित करना चाहिए।

स्थैतिक को छोड़कर लिंकेज डिफ़ॉल्ट रूप से बाहरी है। फिर, आपको कॉन्स्टेंस नेस द्वारा बचाया गया है - कंपाइलर इनलाइन उपयोग को ऑप्टिमाइज़ कर सकता है। यदि आप कास्ट ड्रॉप करते हैं, तो आपको लिंक समय पर एक बहु परिभाषित प्रतीकों त्रुटि मिलेगी ।


मेरा मानना ​​है कि कंपाइलर को सभी मामलों में एक कास्ट इंट के लिए जगह आवंटित करनी चाहिए, क्योंकि एक अन्य मॉड्यूल हमेशा कह सकता है "बाहरी कॉन्स्टेंट इंट जो भी हो; कुछ (और जो भी हो);"

1

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


"... लेकिन यह प्रत्येक स्रोत फ़ाइल का कारण होगा जिसमें हेडर फ़ाइल में वैरिएबल की अपनी निजी प्रति शामिल होगी, जो कि शायद ऐसा नहीं था।" - स्टेटिक इनिशियलाइज़ेशन ऑर्डर फ़ाइस्को के कारण , प्रत्येक ट्रांसलेशन यूनिट में एक कॉपी होना आवश्यक है।
jww

1

const ++ डिफ़ॉल्ट रूप से C ++ में स्थिर होते हैं, लेकिन C को एक्सटर्नल करते हैं। इसलिए यदि आप C ++ का उपयोग करते हैं तो इसका कोई मतलब नहीं है कि किस निर्माण का उपयोग करना है।

(7.11.6 C ++ 2003, और Apexndix C के नमूने हैं)

उदाहरण संकलन और लिंक स्रोतों की तुलना C और C ++ प्रोग्राम के रूप में करें:

bruziuz:~/test$ cat a.c
const int b = 22;
int main(){return 0;}
bruziuz:~/test$ cat b.c
const int b=2;
bruziuz:~/test$ gcc -x c -std=c89 a.c b.c
/tmp/ccSKKIRZ.o:(.rodata+0x0): multiple definition of `b'
/tmp/ccDSd0V3.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
bruziuz:~/test$ gcc -x c++ -std=c++03 a.c b.c 
bruziuz:~/test$ 
bruziuz:~/test$ gcc --version | head -n1
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609

वहाँ है अभी भी सहित भावना static। यह इरादे / जागरूकता को दर्शाता है कि प्रोग्रामर क्या कर रहा है और अन्य प्रकार की घोषणा के साथ समता बनाए रखता है (और, fwiw, C) जिसमें निहित का अभाव है static। यह ओवरराइडिंग फ़ंक्शंस की घोषणाओं में शामिल है virtualऔर हाल ही overrideमें - आवश्यक नहीं है, लेकिन बहुत अधिक स्व-दस्तावेजीकरण और, बाद के मामले में, स्थैतिक विश्लेषण के लिए अनुकूल है।
अंडरस्कोर_ड

मैं बेवजह सहमत हूं। उदाहरण के लिए मेरे लिए वास्तविक जीवन में मैं हमेशा इसे स्पष्ट रूप से लिखता हूं।
ब्रजज़ीज़

"तो अगर आप C ++ का उपयोग करते हैं, तो इसका कोई मतलब नहीं है कि किस निर्माण का उपयोग करना है ..." - हम्म ... मैंने अभी एक प्रोजेक्ट संकलित किया है जिसका उपयोग constकेवल हेडर में एक चर पर किया जाता है g++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)। इसका परिणाम लगभग 150 गुणा प्रतीकों में से एक था (प्रत्येक अनुवाद इकाई के लिए हेडर शामिल था)। मुझे लगता है कि हम या तो जरूरत static, inlineएक गुमनाम / अज्ञात नाम स्थान बाह्य संबंध से बचने के लिए या।
jww

मैंने const intनाम - स्थान के दायरे और वैश्विक नाम स्थान में घोषित के साथ gcc-5.4 के साथ बेबी-उदाहरण की कोशिश की । और यह नियम का पालन करता है और "ऑब्जेक्ट्स को कॉन्स्टेंट घोषित किया गया है और स्पष्ट रूप से घोषित नहीं किया गया है, जिसमें आंतरिक लिंकेज है।" ".... हो सकता है कि किसी कारण से यह हेडर सी
हेडलेट

@jww मैंने C के लिए लिंकेज के मुद्दे और C ++ के लिए कोई समस्या नहीं के साथ उदाहरण अपलोड किया
bruziuz

0

स्टेटिक एक अन्य संकलन इकाई को उस वेरिएबल को बाहर करने से रोकता है ताकि कंपाइलर उस वेरिएबल के वैल्यू को "इनलाइन" कर सके जहां इसका उपयोग किया जाता है और इसके लिए मेमोरी स्टोरेज नहीं बनाया जाता है।

आपके दूसरे उदाहरण में, कंपाइलर यह नहीं मान सकता है कि कुछ अन्य स्रोत फ़ाइल इसे एक्सटर्नल नहीं करेगी, इसलिए इसे वास्तव में मेमोरी में उस मूल्य को स्टोर करना होगा।


-2

स्टेटिक कंपाइलर को कई उदाहरण जोड़ने से रोकता है। यह #ifndef सुरक्षा के साथ कम महत्वपूर्ण हो जाता है, लेकिन हेडर को दो अलग पुस्तकालयों में शामिल किया गया है, और आवेदन जुड़ा हुआ है, दो उदाहरण शामिल होंगे।


यह मानते हुए कि "पुस्तकालयों" से आपका मतलब अनुवाद इकाइयों से है , तो नहीं, शामिल-गार्ड कई परिभाषाओं को रोकने के लिए बिल्कुल कुछ नहीं करते हैं, यह देखते हुए कि वे केवल एक ही अनुवाद इकाई के भीतर दोहराए गए निष्कर्षों के खिलाफ रक्षा करते हैं । इसलिए, वे static"कम महत्वपूर्ण" बनाने के लिए कुछ भी नहीं करते हैं । और यहां तक ​​कि दोनों के साथ, आप कई आंतरिक रूप से जुड़ी परिभाषाओं के साथ समाप्त हो सकते हैं, जो शायद इरादा नहीं है।
अंडरस्कोर_ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.