2010-06-03 12 views
9

क्यों अनंत रिकर्सन सीजी गलती का कारण बनता है? स्टैक ओवरफ्लो सीईजी गलती क्यों करता है। मैं विस्तृत स्पष्टीकरण की तलाश में हूं।क्यों अनंत रिकर्सन सीजी गलती

int f() 
{ 
    f(); 
} 

int main() 
{ 
    f(); 
} 
+0

देखें [यह सी ++ कोड-स्निपेट सेगमेंटेशन गलती क्यों है?] (Http://stackoverflow.com/questions/2809014/why-does-this-c-code-snippet-segmentation-fault)। –

उत्तर

16

हर बार जब आप च() कहते हैं, आप ढेर के आकार में वृद्धि - कि जहां वापसी पता संग्रहीत किया जाता है तो कार्यक्रम जानता है, जहां जब च() पूरा करती है पर जाने के लिए है। चूंकि आप कभी भी f() से बाहर नहीं निकलते हैं, तो प्रत्येक कॉल को कम से कम एक रिटर्न पता से ढेर बढ़ने जा रहा है। एक बार स्टैक सेगमेंट भरने के बाद, आपको segfault त्रुटि मिलती है। आपको हर ओएस में समान परिणाम मिलेंगे।

+1

कंपाइलर्स को छोड़कर जो पूंछ कॉल को लूप में अनुकूलित करते हैं। लेकिन आप जवाब सही है, वैसे भी +1 – qrdl

2

AFAIK: स्टैक के सिरों को उन पते से संरक्षित किया जाता है जो प्रक्रिया के लिए उपलब्ध नहीं हैं। यह स्टैक को आवंटित डेटा-स्ट्रक्चर पर बढ़ने से रोकता है, और स्टैक आकार को स्पष्ट रूप से जांचने से अधिक कुशल है, क्योंकि आपको स्मृति सुरक्षा को वैसे भी जांचना है।

+0

यह समझ में आता है – Pqr

14

Segmentation fault एक शर्त है जब आपका प्रोग्राम उस स्मृति स्थान तक पहुंचने का प्रयास करता है जिस पर इसे एक्सेस करने की अनुमति नहीं है। अनंत रिकर्सन आपके ढेर को बढ़ने का कारण बनता है। और बढ़ो। और बढ़ो। आखिरकार यह एक बिंदु पर बढ़ेगा जब यह स्मृति के क्षेत्र में फैल जाएगा कि आपके प्रोग्राम को ऑपरेटिंग सिस्टम तक पहुंचने के लिए मना किया गया है। वह तब होता है जब आप सेगमेंटेशन गलती प्राप्त करते हैं।

+1

मैं कहूंगा कि यह सबसे अच्छा जवाब है .. – Jeriko

+0

मैंने इस शब्द का वर्णन करने के लिए "स्टैक-हीप टकराव" शब्द सुना है। – Maxpm

+0

@ मैक्सपैम, जो सही समझ में आता है। ढेर और ढेर आम तौर पर एक-दूसरे की तरफ बढ़ते हैं। तो अगर ढेर बहुत बड़ा हो जाता है, तो यह ढेर में फैल सकता है। – Dima

3

यह अभी भी एक stackoverflow ;-) है

बात यह है कि सी क्रम "instrumentalisation" प्रदान नहीं करता है अन्य कामयाब भाषाओं (जैसे जावा, अजगर, आदि) करते हैं, इसलिए नामित अंतरिक्ष के बाहर लिख है विस्तृत अपवाद के बजाय स्टैक के लिए केवल निम्न स्तर की त्रुटि उत्पन्न होती है, जिसका "सेगमेंटेशन गलती" का सामान्य नाम होता है।

यह प्रदर्शन कारणों से है, क्योंकि उन मेमोरी एक्सेस वॉचडॉग को हार्डवेयर समर्थन की मदद से सेट किया जा सकता है, जिनमें बहुत कम या कोई ओवरहेड नहीं है; मुझे अब सटीक विवरण याद नहीं आ रहे हैं, लेकिन यह आमतौर पर एमएमयू पेज टेबल को चिह्नित करने या अधिकतर अप्रचलित सेगमेंट ऑफसेट रजिस्टरों के साथ किया जाता है।

0

यह अनिवार्य रूप से एक बफर ओवरफ्लो के समान सिद्धांत है; ओएस स्टैक के लिए एक निश्चित मात्रा में स्मृति आवंटित करता है, और जब आप बाहर निकलते हैं (ढेर ओवरफ्लो) आपको अपरिभाषित व्यवहार मिलता है, जो इस संदर्भ में एक SIGSEGV का अर्थ है।

मूल विचार:

int stack[A_LOT]; 
int rsp=0; 

void call(Func_p fn) 
    { 
    stack[rsp++] = rip; 
    rip = fn; 
    } 

void retn() 
    { 
    rip = stack[--rsp]; 
    } 

/*recurse*/ 
for(;;){call(somefunc);} 

अंततः ढेर के अंत अतीत चाल आरएसपी और आप आवंटित भंडारण और अपने कार्यक्रम barfs में अगले वापसी पता लगाने के लिए प्रयास करें। जाहिर है वास्तविक प्रणाली बहुत उससे अधिक जटिल हैं, लेकिन यह कई बड़ी किताबें ले सकती है (और है)।

0

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

एक अनंत रिकर्सन लूप में, सर्वोत्तम मामले में केवल वापसी मूल्य स्टैक को कम करने का कारण बनता है ... जब तक यह उस स्मृति को इंगित नहीं करता है जिसे प्रोग्राम द्वारा एक्सेस नहीं किया जा सकता है।और आप सेगमेंटेशन गलती समस्या देखते हैं।

आपको दिलचस्प this page मिल सकता है।

+0

कुछ आरआईएससी आर्किटेक्चर एक रजिस्टर में रिटर्न पता स्टोर करते हैं ... लेकिन यह रिकर्सन की अनुमति नहीं देता है, न ही अन्य सबराउटिन को कॉल करने की अनुमति देता है, जब तक कि यह अलग-अलग रजिस्टरों का उपयोग करने की अनुमति न दे; चूंकि रजिस्ट्रार आमतौर पर सीमित होते हैं, अंत में एक पुनरावृत्ति की अनुमति देने के लिए अधिक सुविधाजनक है ... यानी एक स्टैक ... या कुछ अन्य तंत्र (जैसे एमएमआईक्स) जो स्मृति को खपत करते हैं, जिसे किसी भी तरह संबोधित किया जा सकता है, और इसलिए एक ही समस्या जल्द या बाद में उत्पन्न होती है। – ShinTakezou

1

एक प्रोग्राम copunter या निर्देश सूचक एक रजिस्टर है जिसमें निष्पादित करने के लिए अगले निर्देश का मूल्य होता है। फ़ंक्शन कॉल में, प्रोग्राम काउंटर का वर्तमान मान स्टैक में धक्का दिया जाता है और फिर फ़ंक्शन के पहले निर्देश के लिए प्रोग्राम काउंटर पॉइंट्स। पुराना मान उस फ़ंक्शन से लौटने के बाद पॉप किया गया है और प्रोग्राम काउंटर को सौंपा गया है। अनंत रिकर्सन में मूल्य बार-बार धक्का दिया जाता है और स्टैक ओवरफ़्लो की ओर जाता है।

4

आपके सिस्टम संसाधन सीमित हैं। वे सीमित हैं। भले ही आपके सिस्टम में पूरी धरती पर सबसे अधिक स्मृति और भंडारण हो, फिर भी आपके पास जो कुछ है उसके मुकाबले अनंत बिगड़ गया है। अब याद रखें।

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

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

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