2015-10-28 8 views
14

मैं सी में एक दिलचस्प सुरक्षित कोडिंग नियम ++ जिसमें कहा गया है भर में आया था:सी ++ कोड, संकलक उत्पन्न करता है std :: अपवाद

Do not reenter a function during the initialization of a static variable declaration. If a function is reentered during the constant initialization of a static object inside that function, the behavior of the program is undefined. Infinite recursion is not required to trigger undefined behavior, the function need only recur once as part of the initialization.

उसी के non_compliant उदाहरण है:

#include <stdexcept> 

int fact(int i) noexcept(false) { 
    if (i < 0) { 
    // Negative factorials are undefined. 
    throw std::domain_error("i must be >= 0"); 
    } 

    static const int cache[] = { 
    fact(0), fact(1), fact(2), fact(3), fact(4), fact(5), 
    fact(6), fact(7), fact(8), fact(9), fact(10), fact(11), 
    fact(12), fact(13), fact(14), fact(15), fact(16) 
    }; 

    if (i < (sizeof(cache)/sizeof(int))) { 
    return cache[i]; 
    } 

    return i > 0 ? i * fact(i - 1) : 1; 
} 

जो स्रोत के अनुसार त्रुटि देता है:

terminate called after throwing an instance of '__gnu_cxx::recursive_init_error' 
    what(): std::exception 

Visual Studio 2013 में मार डाला है। मैंने अपने स्वयं के समान कोड की कोशिश की और मुझे एक ही त्रुटि मिली (जी ++ का उपयोग करके संकलित और Ubuntu पर निष्पादित)।

मुझे संदेह है कि मेरी समझ इस अवधारणा के संबंध में सही है क्योंकि मैं सी ++ से अच्छी तरह से परिचित नहीं हूं। मेरे अनुसार, चूंकि कैश सरणी निरंतर है, जिसका अर्थ यह है कि इसे केवल पढ़ने के लिए ही किया जा सकता है और इसे स्थिर के रूप में केवल एक बार शुरू किया जाना चाहिए, इसे बार-बार शुरू किया जा रहा है क्योंकि इस सरणी के मान प्रत्येक में से लौटाए गए मान हैं अल्पविराम से अलग रिकर्सिव फ़ंक्शन कॉल जो घोषित सरणी के व्यवहार के खिलाफ है। इस प्रकार, यह अनिर्धारित व्यवहार देता है जो नियम में भी कहा गया है।

इसके लिए बेहतर स्पष्टीकरण क्या है?

+4

क्या मैं आपको सही तरीके से समझ रहा हूं: आप कार्यान्वयन-निर्भर कारण चाहते हैं कि अनिश्चित व्यवहार का यह निश्चित मामला इस विशेष तरीके से क्यों व्यवहार करता है? – Downvoter

+0

@ कैड: आप सही हैं! .... :) –

उत्तर

17

fact() निष्पादित करने के लिए, आपको पहले सांख्यिकीय रूप से fact::cache[] प्रारंभ करना होगा। प्रारंभ में fact::cache के लिए, आपको fact() निष्पादित करने की आवश्यकता है। वहां एक परिपत्र निर्भरता है, जो आपके द्वारा देखे जाने वाले व्यवहार की ओर ले जाती है। cache केवल एक बार आरंभ किया जाएगा, लेकिन इसे स्वयं शुरू करने के लिए स्वयं को प्रारंभ करने की आवश्यकता है। यहां तक ​​कि यह टाइपिंग मेरे सिर स्पिन बनाता है।

इस तरह एक कैश तालिका पेश करने का सही तरीके से एक अलग समारोह में अलग करने के लिए है:

int fact(int i) noexcept(false) { 
    if (i < 0) { 
    // Negative factorials are undefined. 
    throw std::domain_error("i must be >= 0"); 
    } 

    return i > 0 ? i * fact(i - 1) : 1; 
} 

int memo_fact(int i) noexcept(false) { 
    static const int cache[] = { 
    fact(0), fact(1), fact(2), fact(3), fact(4), fact(5), 
    fact(6), fact(7), fact(8), fact(9), fact(10), fact(11), 
    fact(12), fact(13), fact(14), fact(15), fact(16) 
    }; 

    if (i < (sizeof(cache)/sizeof(int))) { 
    return cache[i]; 
    } 
    else { 
    return fact(i); 
    }  
} 

यहाँ, memo_fact::cache[] केवल एक बार प्रारंभ किया जाएगा - लेकिन इसकी प्रारंभ पर ही निर्भर नहीं रह गया है। तो हमें कोई मुद्दा नहीं है।

+0

व्याख्या करने के लिए धन्यवाद :) .. इस से मेरी समझ यह है कि कैश सरणी जिसे एक बार स्थैतिक रूप से प्रारंभ किया जाना है, बार-बार पुन: प्रारंभिक फ़ंक्शन कॉल तथ्य() पर निर्भरता के कारण पुनः आरंभ किया जा रहा है। इसलिए, यह कार्य, इसकी प्रारंभिकता पुनः दर्ज करें। –

6

सी ++ मानक, §6.7/4, का कहना है कि स्थिर भंडारण अवधि के साथ ब्लॉक गुंजाइश चर के initialisation के बारे में निम्नलिखित:

If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.

निम्नलिखित जानकारीपूर्ण उदाहरण दिया जाता है:

int foo(int i) { 
static int s = foo(2*i); // recursive call - undefined 
return i+1; 
} 

यह आपके उदाहरण पर भी लागू होता है। fact(0) एक रिकर्सिव कॉल है, इसलिए cache की घोषणा फिर से दर्ज की गई है। अपरिभाषित व्यवहार लागू किया जाता है।

यह याद रखना महत्वपूर्ण है कि अपरिभाषित व्यवहार का क्या अर्थ है। अपरिभाषित व्यवहार का अर्थ है कि सब कुछ हो सकता है, और "सब कुछ" काफी स्वाभाविक रूप से अपवादों को फेंक दिया जा सकता है।

अपरिभाषित व्यवहार का भी अर्थ है कि अब आप कोड में किसी और चीज के बारे में तर्क नहीं दे सकते हैं, सिवाय इसके कि जब आप वास्तव में कंपाइलर-कार्यान्वयन विवरणों पर उतरना चाहते हैं। लेकिन फिर आप एक प्रोग्रामिंग भाषा का उपयोग करने के मामले में सी ++ के बारे में बात नहीं कर रहे हैं, लेकिन उस भाषा को कार्यान्वित करने के तरीके के संदर्भ में।

+0

व्याख्या करने के लिए धन्यवाद :) ... –

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