2009-05-22 14 views
19

मैं एक समारोह के भीतर स्थैतिक चर की अंतर्निहित कार्यान्वयन के बारे में उत्सुक हूँ।संकलक द्वारा स्थिर स्थिर चर प्रारंभिकरण कैसे लागू किया जाता है?

यदि मैं एक मौलिक प्रकार (चार, int, double, आदि) का स्थिर चर घोषित करता हूं, और इसे प्रारंभिक मान देता हूं, तो मुझे लगता है कि संकलक बस उस चर के मूल्य को उस शुरुआत में सेट करता है main() से पहले कार्यक्रम कहा जाता है:

void SomeFunction(); 

int main(int argCount, char ** argList) 
{ 
    // at this point, the memory reserved for 'answer' 
    // already contains the value of 42 
    SomeFunction(); 
} 

void SomeFunction() 
{ 
    static int answer = 42; 
} 

हालांकि, अगर स्थिर चर एक वर्ग का एक उदाहरण है:

class MyClass 
{ 
    //... 
}; 

void SomeFunction(); 

int main(int argCount, char ** argList) 
{ 
    SomeFunction(); 
} 

void SomeFunction() 
{ 
    static MyClass myVar; 
} 

मुझे पता है कि यह पहली बार है कि समारोह में कहा जाता है जब तक आरंभ नहीं किया जा जाएगा । चूंकि संकलक को यह जानने का कोई तरीका नहीं है कि फ़ंक्शन को पहली बार कब बुलाया जाएगा, यह इस व्यवहार को कैसे उत्पन्न करता है? क्या यह अनिवार्य रूप से फ़ंक्शन बॉडी में एक if-block पेश करता है?

static bool initialized = 0; 
if (!initialized) 
{ 
    // construct myVar 
    initialized = 1; 
} 

उत्तर

11

मैंने देखा है कि संकलक आउटपुट में, स्थानीय स्थैतिक चरों को ठीक उसी तरह शुरू किया जाता है जैसा आप कल्पना करते हैं।

ध्यान दें कि सामान्यतः यह थ्रेड-सुरक्षित तरीके से किया गया है। इसलिए यदि आपके पास स्थैतिक स्थानीय लोगों के साथ कार्य है, जिन्हें कई धागे से बुलाया जा सकता है, तो आपको इसे ध्यान में रखना चाहिए। किसी अन्य को बुलाए जाने से पहले मुख्य धागे में एक बार फ़ंक्शन को कॉल करना आम तौर पर चाल करेगा।

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

+0

क्या कुछ बदल गया? मैंने सुना है कि सी ++ 11 के बाद स्थैतिक प्रारंभिक प्रत्येक थ्रेड-सुरक्षित है। –

+0

@VictorPolevoy: हाँ - जब यह उत्तर लिखा गया था, सी ++ 11 मौजूद नहीं था। सी ++ 11 में, मानक में धागा समर्थन शामिल था, और इसे ब्लॉक-स्कोप स्थिर चर (6.7/4) के प्रारंभिक विवरण के विवरण में जोड़ा गया था: "यदि नियंत्रण प्रारंभिक होने पर एक साथ घोषणा में प्रवेश करता है, समवर्ती निष्पादन प्रारंभिक समापन के लिए इंतजार करेंगे "। –

+1

मेरा सुझाव है कि आप अपना उत्तर संपादित करें क्योंकि इसे उत्तर के रूप में स्वीकार किया गया है और इसमें 10 वोट हैं। यह प्रश्न स्थैतिक चर के प्रारंभिक विषय के विषय के साथ सबसे अधिक देखने योग्य है। –

2

आप एक आम कार्यान्वयन के रूप में प्रारंभ ध्वज सहित सब कुछ के बारे में सही कर रहे हैं। यह मूल रूप से स्थिर स्थानीय लोगों का प्रारंभिक धागा-सुरक्षित नहीं है, और क्यों pthread_once मौजूद है।

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

जाहिर है आप असेंबली कोड देख सकते हैं, या अपरिभाषित व्यवहार को उत्तेजित कर सकते हैं और वास्तव में क्या होता है से कटौती कर सकते हैं। लेकिन सी ++ मानक यह नहीं मानता कि वैध आधार यह दावा करने के लिए है कि व्यवहार "जैसा नहीं" है, यह स्पेक कहता है।

1

मुझे पता है कि इसे पहली बार जब तक फ़ंक्शन कहा जाता है तब तक इसे प्रारंभ नहीं किया जाएगा। चूंकि संकलक को यह जानने का कोई तरीका नहीं है कि फ़ंक्शन को पहली बार कब बुलाया जाएगा, यह इस व्यवहार को कैसे उत्पन्न करता है? क्या यह अनिवार्य रूप से फ़ंक्शन बॉडी में एक if-block पेश करता है?

हां, यह सही है: और, एफडब्ल्यूआईडब्ल्यू, यह आवश्यक रूप से थ्रेड-सुरक्षित नहीं है (यदि फ़ंक्शन को पहली बार दो थ्रेडों द्वारा "पहली बार" कहा जाता है)।

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

10

This question समान जमीन को कवर किया गया, लेकिन थ्रेड सुरक्षा का उल्लेख नहीं किया गया था। इसके लायक होने के लिए, सी ++ 0 एक्स फ़ंक्शन स्थिर प्रारंभिक थ्रेड सुरक्षित बनाएगा।

(C++0x FCD, 6.7/4 समारोह स्टैटिक्स पर देखते हैं: "। नियंत्रण घोषणा समवर्ती जबकि चर प्रारंभ किया जा रहा है में प्रवेश करती है, तो समवर्ती निष्पादन प्रारंभ की पूरा करने के लिए इंतजार करना होगा")

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

+2

क्या आप सी ++ 0x में थ्रेड सुरक्षित होने के लिए संदर्भ/उद्धरण दे सकते हैं? मुझे एक नहीं मिला है। – ChrisW

1

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

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