C में किसी फंक्शन के अंदर स्टेटिक वैरिएबल


119

क्या छपेगा? ६ ६ या ६ 7? और क्यों?

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}

54
कोशिश करने में क्या हर्ज है?
एंड्रयू

12
क्या आपने इसे टाइप करने और अपने लिए देखने की कोशिश की?
विल्हेमटेल

21
मैं समझना चाहता हूं क्यों।
वडिक्लक

7
@Vadiklk तो "क्यों" से शुरू होने वाले प्रश्न पूछें
एंड्री

1
ideone.com/t9Bbe आप क्या उम्मीद करेंगे? क्या परिणाम आपके समाप्ति से मेल नहीं खाता है? आपको अपने परिणाम की उम्मीद क्यों थी?
Eckes

जवाबों:


187

यहां दो मुद्दे हैं, जीवनकाल और गुंजाइश।

चर का दायरा वह है जहां चर नाम देखा जा सकता है। यहां, x केवल फ़ंक्शन फू () के अंदर दिखाई देता है।

एक चर का जीवनकाल वह अवधि है जिस पर यह मौजूद है। यदि x को कीवर्ड स्थिर के बिना परिभाषित किया गया था, तो जीवनकाल फू () से फू में वापसी () में प्रवेश से होगा; इसलिए इसे हर कॉल पर 5 पर फिर से शुरू किया जाएगा।

कार्यक्रम के जीवनकाल के लिए एक चर के जीवनकाल का विस्तार करने के लिए कीवर्ड स्टैटिक कार्य करता है; उदाहरण के लिए, इनिशियलाइज़ेशन एक बार और केवल एक बार होता है और फिर वैरिएबल इसकी वैल्यू को बरकरार रखता है - जो भी यह आया है - भविष्य के सभी कॉल्स को लुभाने के लिए ()।


15
@ देवनल, हां हम हैं।
ओरियन एल्जेनिल

1
सरल और तार्किक :)
दिमितर वुकमन

किन परिदृश्यों में हमें किसी फ़ंक्शन के अंदर एक वैरिएबल को स्थिर घोषित करने की आवश्यकता होती है ?, बस यह जानने के लिए उत्सुक हूं कि मैंने इससे पहले इसका उपयोग नहीं किया है?
अकय

मैं धन्यवाद कहूंगा, लेकिन यह सब पृष्ठ के शीर्ष पर उत्तर दिया गया था। इससे मुझे हंसी आती है कि लोग सिर्फ अपना कोड नहीं चलाते हैं। xD
पोद्दल

यह उत्तर गलत है। जिस क्षण आप पुनरावर्ती कार्यों के बारे में सोचते हैं, जैसा कि यहां वर्णित परिभाषाएं व्यवहार की व्याख्या नहीं करती हैं!
फिलिप युग्म

53

आउटपुट : ६ 6

कारण : स्थिर चर को केवल एक बार (ऑटो चर के विपरीत) आरंभ किया जाता है और स्थैतिक चर की आगे की परिभाषा रनटाइम के दौरान बाईपास हो जाएगी। और यदि इसे मैन्युअल रूप से प्रारंभ नहीं किया गया है, तो यह मूल्य 0 से स्वचालित रूप से प्रारंभ होता है। इसलिए,

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}

10

६ 7

संकलक व्यवस्था करता है कि स्थिर वैरिएबल आरंभीकरण हर बार फ़ंक्शन के दर्ज होने पर नहीं होता है


10

यह निम्नलिखित कार्यक्रम के रूप में ही है:

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

उस कार्यक्रम में स्थैतिक कीवर्ड जो कुछ भी करता है वह यह है कि संकलक (अनिवार्य रूप से) 'अरे, मेरे पास यहां एक चर है जिसे मैं किसी और तक नहीं पहुंचना चाहता, किसी और को यह न बताएं कि यह मौजूद है'।

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

आशा है कि ये आपकी मदद करेगा


13
खैर, यह वास्तव में एक ही नहीं है। अभी भी एक्स पर गुंजाइश का मुद्दा है। इस उदाहरण में, आप xमुख्य के साथ प्रोक और फ्यूज़ कर सकते हैं ; यह वैश्विक है। मूल उदाहरण xमें foo के लिए स्थानीय था, केवल उस ब्लॉक के अंदर दिखाई देता है, जो आमतौर पर बेहतर होता है: यदि foo xपूर्वानुमान और दृश्यमान तरीकों से बनाए रखने के लिए मौजूद है, तो दूसरों को प्रहार करने देना आमतौर पर खतरनाक है। इसे दायरे में रखने के एक अन्य लाभ के रूप में foo() यह foo()पोर्टेबल भी रहता है ।
user2149140

2
@ user2149140 'किसी को यह न बताएं कि यह इस फ़ंक्शन के बाहर मौजूद है, इसे केवल इस फ़ंक्शन के अंदर ही पहुंचना चाहिए'
DCShannon

3
जबकि आपने चर को घोषित करने के लिए गुंजाइश के मुद्दे को संबोधित किया है, जहां जीवनकाल के बजाय गुंजाइश को प्रभावित करने वाले स्थैतिक का वर्णन गलत लगता है।
डीसीशनॉन

1
@Chameleon इस प्रश्न को टैग किया गया है c, इसलिए इस संदर्भ में, आपका उदाहरण वैश्विक दायरे में अवैध होगा। (C को ग्लोबल्स के लिए निरंतर इनिशियलाइज़र की आवश्यकता होती है, C ++ नहीं)।
रिचर्ड जे। रॉस III

5

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


यह कहना एक "वैश्विक" चर की तरह है और फिर यह कहते हुए कि आप इसका उपयोग नहीं कर सकते हैं यह एक ऑक्सीमोरोन है। वैश्विक का अर्थ है कहीं भी सुलभ। एक स्थिर इनसाइड के इस मामले में यह एक फ़ंक्शन है जो हर जगह सुलभ नहीं है। ओपी में मुद्दा जैसा कि अन्य ने नोट किया है वह गुंजाइश और जीवनकाल के बारे में है। कृपया 'ग्लोबल' शब्द का उपयोग करने और चर के दायरे पर उन्हें गुमराह करने के साथ लोगों को भ्रमित न करें।
चकब सिप

@ChuckB: सही है। ठीक कर दिया। खैर 6 साल हो गए। मेरे पिछले उत्तर में 6 साल पहले की धारणा थी!
डोनटालो

5

आउटपुट: 6,7

कारण

की घोषणा xअंदर है, fooलेकिन x=5इनिशियलाइज़ेशन बाहर से होता है foo!

यहां हमें समझने की जरूरत है

static int x = 5;

के रूप में ही नहीं है

static int x;
x = 5;

अन्य उत्तरों ने यहाँ महत्वपूर्ण शब्दों, गुंजाइश और जीवनकाल का उपयोग किया है, और बताया कि कार्यक्षेत्र xमें इसकी घोषणा के बिंदु से लेकर कार्य fooके अंत तक का दायरा है foo। उदाहरण के लिए मैंने फ़ंक्शन के अंत में घोषणा को स्थानांतरित करके जाँच की, और यह xअघोषित बनाता हैx++; कथन में ।

तो static int xकथन का (स्कोप) हिस्सा वास्तव में लागू होता है जहां आप इसे पढ़ते हैं, कहीं INSIDE फ़ंक्शन को करता है और केवल वहाँ से, फ़ंक्शन के अंदर से ऊपर नहीं।

हालांकि x = 5(जीवन) के बयान का हिस्सा है चर का प्रारंभ और हो रहा बाहर कार्यक्रम लोड हो रहा है के भाग के रूप समारोह का। चर xका मान के साथ जन्म होता है5 है जब प्रोग्राम लोड होता है।

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

5 का मूल्य सैद्धांतिक रूप से निर्धारित किया जाता है, भले ही फू को बिल्कुल भी बुलाया जाए या नहीं, हालांकि एक कंपाइलर फंक्शन को ऑप्टिमाइज़ कर सकता है यदि आप इसे कहीं भी कॉल नहीं करते हैं। 5 की वैल्यू को चर में पहले कभी भी बुलाया जाना चाहिए।

fooबयान के अंदरstatic int x = 5; किसी भी कोड को उत्पन्न करने की संभावना नहीं है।

मुझे पता चलता है कि xजब मैं fooकिसी कार्यक्रम को अपने कार्यक्रम में डालता हूं , तो पता का उपयोग होता है , और फिर (सही ढंग से) यह अनुमान लगाया कि यदि मैं फिर से कार्यक्रम चलाता हूं तो उसी स्थान का उपयोग किया जाएगा। नीचे दी गई आंशिक स्क्रीन कैप्चर से पता चलता है कि पहली कॉल करने से पहले भी xइसका मूल्य 5है foo

ब्रेक से पहले कॉल करने से पहले ब्रेक प्वाइंट


2

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


1
क्या आप सुनिश्चित हैं कि फ़ंक्शन को कॉल करने से पहले स्टेटिक को प्रारंभ किया जाता है, और फ़ंक्शन के पहले कॉल पर नहीं?
जेसी काली मिर्च

@JessePepper: कम से कम यदि स्मृति कार्य करती है, तो यह इस बात पर निर्भर करता है कि आप C ++ 98/03 या C ++ 11 के बारे में बात कर रहे हैं। C ++ 98/03 में, मेरा मानना ​​है कि यह ऊपर वर्णित है। C ++ 11 में, थ्रेडिंग करना अनिवार्य रूप से असंभव है, इसलिए फ़ंक्शन के लिए पहली प्रविष्टि पर प्रारंभ किया जाता है।
जेरी कॉफिन

2
मुझे लगता है कि आप वास्तव में गलत हैं। मुझे लगता है कि C ++ 11 से पहले भी इसे केवल इनिशियलाइज़ किया गया था जब फंक्शन कहा जाता है। स्थैतिक आरंभीकरण निर्भरता समस्या के एक सामान्य समाधान के लिए यह महत्वपूर्ण है।
जेसी पेप्पर

2

Vadiklk,

क्यों ...? कारण यह है कि स्थिर चर को केवल एक बार आरंभीकृत किया जाता है, और पूरे कार्यक्रम में इसके मूल्य को बनाए रखता है। का अर्थ है, आप फ़ंक्शन कॉल के बीच स्थिर चर का उपयोग कर सकते हैं। यह भी कहा जाता है कि "किसी फ़ंक्शन को कितनी बार कहा जाता है" गिनने के लिए इस्तेमाल किया जा सकता है

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

और उत्तर 5 4 3 2 1 है न कि 5 5 5 5 5 5 .... (अनंत लूप) जैसा कि आप उम्मीद कर रहे हैं। फिर से, कारण स्थिर वैरिएबल को एक बार आरंभीकृत किया जाता है, जब अगली बार मुख्य () कहा जाता है, तो इसे 5 के लिए इनिशियलाइज़ नहीं किया जाएगा क्योंकि यह प्रोग्राम में पहले से ही इनिशियलाइज़ हो गया है। इसलिए हम वैल्यू बदल सकते हैं लेकिन रीइंस्ट्रिक्ट नहीं कर सकते हैं। स्थैतिक चर कैसे काम करता है।

या आप भंडारण के अनुसार विचार कर सकते हैं: स्थैतिक चर किसी प्रोग्राम के डेटा अनुभाग पर संग्रहीत होते हैं और वे चर जो डेटा अनुभाग में संग्रहीत किए जाते हैं, उन्हें एक बार प्रारंभ किया जाता है। और आरंभीकरण से पहले उन्हें बीएसएस अनुभाग में रखा गया है।

बदले में ऑटो (स्थानीय) वैरिएबल स्टैक पर संग्रहीत किए जाते हैं और स्टैक पर सभी वेरिएबल्स को हर समय पुनर्निर्मित किया जाता है जब फ़ंक्शन को नया FAR कहा जाता है (फ़ंक्शन सक्रियण रिकॉर्ड) इसके लिए बनाया गया है।

अधिक समझ के लिए ठीक है, उपरोक्त उदाहरण "स्टैटिक" के बिना करें और आपको बताएं कि आउटपुट क्या होगा। इससे आपको इन दोनों के बीच के अंतर को समझने में मदद मिलेगी।

धन्यवाद जावेद


1

आइए अभी स्टैटिक वेरिएबल्स पर विकिपीडिया लेख पढ़ें ...

स्टैटिक लोकल वैरिएबल: एक फंक्शन के अंदर स्टैटिक घोषित किए गए वैरिएबल को ऑटोमैटिक लोकल वैरिएबल के समान स्कोप होते हुए स्टेटिक रूप से आवंटित किया जाता है। इसलिए जो भी फ़ंक्शन एक कॉल के दौरान फ़ंक्शन को अपने स्थिर स्थानीय चर में रखता है वह तब भी मौजूद रहेगा जब फ़ंक्शन को फिर से कॉल किया जाता है।


5
वह भयानक है! "एक फ़ंक्शन के अंदर स्थिर के रूप में घोषित किए गए चर को सांख्यिकीय रूप से आवंटित किया जाता है" - यह कुछ भी नहीं समझाता है, जब तक कि आप पहले से ही नहीं जानते कि इसका क्या मतलब है!

@ ब्लैंक: ठीक है, यही मैंने सोचा था कि दूसरा वाक्य था। हालांकि मुझे लगता है कि आप सही हैं, यह बेहतर शब्द होना चाहिए।
एंड्रयू व्हाइट

इसके अलावा, यह वास्तव में भ्रमित करने वाले भाग को संबोधित नहीं करता है, जो यह तथ्य है कि इनिशियलाइज़र बाद की कॉल पर छोड़ दिया गया है।
टॉम ऑस्टर

सांख्यिकीय रूप से आबंटित का अर्थ है कोई ढेर, और न ही ढेर।
गिरगिट

1

आपको 6 7 प्रिंट मिलेंगे, जैसा कि आसानी से जांचा जाता है, और यहाँ कारण है: कब foo पहली बार बुलाया जाता है, तो स्थिर चर x को 5 से शुरू किया जाता है। फिर इसे 6 तक बढ़ाया जाता है और मुद्रित किया जाता है।

अब अगली कॉल के लिए foo। कार्यक्रम स्थैतिक चर आरंभीकरण को छोड़ देता है, और इसके बजाय मान 6 का उपयोग करता है जो पिछली बार के आसपास x को सौंपा गया था। निष्पादन सामान्य रूप से आगे बढ़ता है, जिससे आपको 7 मूल्य मिलते हैं।


1
6 7

x एक वैश्विक चर है जो केवल foo () से दिखाई देता है। 5 इसका प्रारंभिक मूल्य है, जैसा कि कोड के .data अनुभाग में संग्रहीत है। कोई भी बाद का संशोधन पिछले मूल्य को अधिलेखित कर देता है। फ़ंक्शन बॉडी में कोई असाइनमेंट कोड जेनरेट नहीं किया गया है।


1

6 और 7 क्योंकि स्थिर चर केवल एक ही बार होता है, इसलिए 5 ++ 1 कॉल पर 6 हो जाता है 6 ++ 2 कॉल पर 7 हो जाता है नोट -2 जब यह होता है तो x का मूल्य 5 के बजाय 6 होता है क्योंकि x स्थिर चर है।


0

C ++ 11 में कम से कम, जब स्थानीय स्थिर वैरिएबल को इनिशियलाइज़ करने के लिए प्रयोग किया जाने वाला एक्सप्रेशन 'कॉन्स्टेक्सप्र' (कंपाइलर द्वारा मूल्यांकन नहीं किया जा सकता) नहीं है, तो फंक्शन के पहले कॉल के दौरान इनिशियलाइज़ेशन होना चाहिए। सबसे सरल उदाहरण सीधे स्थानीय स्थैतिक चर को पहचानने के लिए एक पैरामीटर का उपयोग करना है। इस प्रकार कंपाइलर को यह पता लगाने के लिए कोड का उत्सर्जन करना होगा कि क्या कॉल पहले एक है या नहीं, जिसके बदले में स्थानीय बूलियन चर की आवश्यकता होती है। मैंने ऐसे उदाहरण संकलित किए हैं और जाँच की है कि यह विधानसभा कोड देखकर सच है। उदाहरण इस प्रकार हो सकता है:

void f( int p )
{
  static const int first_p = p ;
  cout << "first p == " << p << endl ;
}

void main()
{
   f(1); f(2); f(3);
}

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

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