2012-12-31 22 views
18

मुझे रिकर्सन का प्रदर्शन करने के लिए एक नमूना कार्यक्रम दिखाया गया था जो ऐसा लगता है कि यह काम नहीं करना चाहिए बल्कि करता है। तर्क बहुत स्पष्ट है लेकिन यह तब भी क्यों काम करता है जब रिकर्स किए गए फ़ंक्शन कॉल को वापस नहीं किया जाता है? ऐसा लगता है कि return स्टैक से कमांड ब्रेक आउट होने पर भी अनुरोध किया जाता है। क्या यह एक भाषा मानक या एक जीसीसी चीज है? मैंने इसे सी और सी ++ के साथ विंडोज और लिनक्स पर जीसीसी के साथ संकलित किया।एक रिक्त रिटर्न कथन बिना रिक्त रिटर्न कॉल स्टैक से क्यों टूट जाता है?

#include <iostream> 
#include <cstdlib> 

using namespace std; 

int isprime(int num, int i) 
{ 
    if (i == 1) { 
     return 1; 
    } 
    else { 
     if (num % i == 0) 
     return 0; 
     else 
     isprime(num, i-1); // should be returned 
    } 
} 


int main(int argc, char** argv) 
{ 
    int input = atoi(argv[1]); 
    cout << input << "\t" << isprime(input, input/2) << "\n"; 
} 
+12

अपरिभाषित व्यवहार में दुर्घटना से काम करने वाले कोड शामिल हैं। फ़ंक्शन कॉल दाएं सीपीयू रजिस्टर में वापसी मान छोड़ देता है। –

+2

मुझे प्रश्न को कम करने का कारण नहीं दिख रहा है। यह एक बहुत अच्छा संदेह है कि कई शुरुआती लोग हो सकते थे। – varevarao

+3

@varevarao: शुरुआती "संदेह" अनुसंधान प्रयास से संतुष्ट हैं। उस ने कहा, मैं ऊपर उठाया। –

उत्तर

20

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

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

यदि यह रिटर्न वैल्यू (रिकर्सिव) कॉलर्स के ढेर के किसी स्थान पर वापसी मूल्य रखा जाएगा, तो यह सब पैटर्न काम नहीं करेगा।

किसी भी मामले में, यह सब किसी भी आधुनिक कंपाइलर द्वारा कब्जा कर लिया जाना चाहिए और आपको चेतावनी देना चाहिए। यदि आपके पास कोई अच्छा कंपाइलर नहीं है, या आप बहुत रक्षात्मक कमांड लाइन विकल्पों का उपयोग कर रहे हैं।

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

+1

_ वापसी मूल्य उस रजिस्टर में होता है जहां कॉलर इसकी अपेक्षा करता है ._ क्या आप इसे थोड़ा समझा सकते हैं? – varevarao

-3

क्या आप वापसी विवरण भूल नहीं रहे हैं? सामान्य रिकर्सन के लिए आपको isprime(num,i-1); से पहले भी वापसी की आवश्यकता है।

मुझे लगता है कि अगर आप इसे सख्त नियमों का उपयोग करके संकलित करते हैं तो संकलन चेतावनी भी देनी चाहिए, क्योंकि फ़ंक्शन हमेशा एक int वापस करना चाहिए, अब यह नहीं होता है (कम से कम यदि आपका कंपाइलर इसे ठीक नहीं करता है)।

+3

यह बिल्कुल सवाल है। मुझे लगता है कि इसे रिटर्न स्टेटमेंट की आवश्यकता होगी लेकिन ऐसा नहीं है। – mmdanziger

+0

@mmdanziger हाँ, बस एहसास हुआ कि। मुझे लगता है कि अगर यह काम करता है तो यह अनिर्धारित व्यवहार है क्योंकि जेन्स ने समझाया। कुछ सख्त विकल्पों का उपयोग करके संकलित करें और आपको शायद एक चेतावनी मिल जाएगी। – Aloys

+1

g ++ -Wall के साथ संकलन चेतावनी देगा "नियंत्रण गैर-शून्य कार्य के अंत तक पहुंचता है" –

5

यहां बहुत कुछ निर्भर करता है कि "यह काम करता है" से आपका क्या मतलब है?

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

मैं आपको संकलक चेतावनी बताता हूं कि संभावित नियंत्रण पथ किसी भी दर पर सी ++ में मूल्य वापस नहीं कर सकते हैं। अपरिभाषित व्यवहार में परिणित, इस सवाल देखें: not returning a value from a non-void returning function

मैं कहूँगा कि इस उदाहरण के बाद एक प्रमुख पाया जाता है और isPrime वापस आ गया है, तो अगले समारोह ऊपर ढेर भी वापस जाने के लिए नि: शुल्क है "के रूप में काम करता है"। कुछ भी isPrime के रिटर्न वैल्यू पर निर्भर नहीं है, इसलिए प्रोग्राम स्टैक का बैक अप लेगा और कुछ आउटपुट करेगा।

... लेकिन जैसा कि व्यवहार अपरिभाषित है, वास्तव में आउटपुट प्राप्त होने वाला मान जंक होने की संभावना है। यदि आप1 इनपुट के रूप में primes के साथ संगत देख रहे हैं, तो वाह।

यदि आपको लगता है कि यह काम कर रहा है, तो मैं विभिन्न मूल्यों के साथ अधिक व्यापक रूप से परीक्षण करना चाहता हूं।

क्या आप किसी भी "डीबग" सेटिंग्स के साथ भी निर्माण कर रहे हैं? यदि ऐसा है तो डीबग सेटिंग्स के साथ इसे फिर से प्रयास करें, क्योंकि कभी-कभी चीजें अनियमित स्मृति को साफ रखने के लिए अतिरिक्त काम करती हैं।

2

मैं समझा सकता है कि वास्तव में क्या होता है:

समारोह कहा जाता है, और जब तक यह या तो सापेक्ष (वापसी 0) या प्रत्यावर्तन के अंत (वापसी 1) पर वापसी तक पहुँच जाता है यह अपने आप में वापस recurses। इस बिंदु पर फ़ंक्शन कॉलर को पुन: चालू करता है, जो is_prime है। लेकिन फ़ंक्शन में निष्पादित करने के लिए कोई और कोड नहीं है, इसलिए यह बिना किसी आगे की कार्रवाई के तुरंत लौटाता है।

हालांकि, आप इसे आसानी से तोड़ सकते हैं, उदाहरण के लिए, is_prime() के कॉल के पीछे printf("Done for %d, %d\n", num, i); जोड़ें [if-statement में होना आवश्यक नहीं है]। या एक सी ++ ऑब्जेक्ट जोड़ना जो फ़ंक्शन के प्रवेश/निकास पर एक और उदाहरण के रूप में बनाया और नष्ट हो गया है।

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

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