गोटो क्यों खतरनाक है?
goto
अपने आप अस्थिरता पैदा नहीं करता है। लगभग 100,000 goto
एस के बावजूद , लिनक्स कर्नेल अभी भी स्थिरता का एक मॉडल है।
goto
अपने आप में सुरक्षा कमजोरियों का कारण नहीं होना चाहिए। हालाँकि कुछ भाषाओं में, इसे try
/ catch
अपवाद प्रबंधन ब्लॉकों के साथ मिलाने से इस CERT अनुशंसा में बताई गई कमजोरियाँ हो सकती हैं । मुख्यधारा सी ++ संकलक ध्वज और ऐसी त्रुटियों को रोकती है, लेकिन दुर्भाग्य से, पुराने या अधिक विदेशी संकलक नहीं हैं।
goto
अपठनीय और अचूक कोड का कारण बनता है। इसे स्पेगेटी कोड भी कहा जाता है , क्योंकि स्पैगेटी प्लेट की तरह, नियंत्रण के प्रवाह का पालन करना बहुत मुश्किल होता है जब बहुत अधिक गोटो हो।
यहां तक कि अगर आप स्पेगेटी कोड से बचने का प्रबंधन करते हैं और यदि आप केवल कुछ गोटो का उपयोग करते हैं, तो वे अभी भी बग और रिसोर्स लीकिंग की सुविधा देते हैं:
- स्पष्ट नेस्टेड ब्लॉक और लूप या स्विच के साथ संरचना प्रोग्रामिंग का उपयोग करते हुए कोड का पालन करना आसान है; इसके नियंत्रण का प्रवाह बहुत ही अनुमानित है। इसलिए यह सुनिश्चित करना आसान है कि आक्रमणकारियों का सम्मान किया जाता है।
- एक
goto
बयान के साथ , आप उस सीधे प्रवाह को तोड़ते हैं, और उम्मीदों को तोड़ते हैं। उदाहरण के लिए, आप यह नहीं देख सकते हैं कि आपके पास अभी भी संसाधन खाली करने के लिए है।
- कई
goto
अलग-अलग जगहों पर आप एक एकल गोटो लक्ष्य पर भेज सकते हैं इसलिए यह सुनिश्चित करने के लिए स्पष्ट नहीं है कि इस स्थान पर पहुंचने के दौरान आप किस राज्य में हैं। इसलिए गलत / निराधार धारणा बनाने का जोखिम काफी बड़ा है।
अतिरिक्त जानकारी और उद्धरण:
C, goto
शाखा को असीम रूप से अपमानजनक विवरण और लेबल प्रदान करता है। औपचारिक रूप से goto
यह आवश्यक नहीं है, और व्यवहार में इसके बिना कोड लिखना लगभग हमेशा आसान होता है। (...)
फिर भी हम कुछ स्थितियों का सुझाव देंगे जहां गोटो को जगह मिल सकती है। सबसे आम उपयोग कुछ गहरी घोंसले वाली संरचनाओं में प्रसंस्करण को छोड़ना है, जैसे कि एक ही बार में दो छोरों को तोड़ना। (...)
हालांकि हम इस मामले के बारे में हठधर्मिता नहीं कर रहे हैं, लेकिन ऐसा लगता है कि गोटो बयानों का इस्तेमाल संयम से किया जाना चाहिए, यदि बिल्कुल भी ।
गोटो का उपयोग कब किया जा सकता है?
K & R की तरह मैं गोटो को लेकर हठधर्मिता नहीं कर रहा हूं। मैं स्वीकार करता हूं कि ऐसी परिस्थितियां हैं जहां गोटो से किसी का जीवन आसान हो सकता है।
आमतौर पर, सी में, गोटो बहुस्तरीय लूप से बाहर निकलने की अनुमति देता है, या एक उचित निकास बिंदु तक पहुंचने के लिए त्रुटि से निपटने की आवश्यकता होती है जो अब तक आवंटित किए गए सभी संसाधनों को मुक्त / अनलॉक करता है (अनुक्रम में iemultiple आवंटन का अर्थ है कई लेबल)। यह लेख लिनक्स कर्नेल में गोटो के विभिन्न उपयोगों को बताता है।
व्यक्तिगत रूप से मैं इससे बचना पसंद करता हूं और 10 साल के सी में मैंने अधिकतम 10 गोटो का इस्तेमाल किया। मैं नेस्टेड if
एस का उपयोग करना पसंद करता हूं, जो मुझे लगता है कि अधिक पठनीय हैं। जब यह बहुत गहरे घोंसले का शिकार होगा, तो मैं या तो अपने कार्य को छोटे भागों में विघटित करना चाहूंगा, या कैस्केड में बूलियन संकेतक का उपयोग करूंगा। आज के अनुकूलन करने वाले कंपाइलर समान कोड के साथ लगभग समान कोड उत्पन्न करने के लिए पर्याप्त चतुर हैं goto
।
गोटो का उपयोग भाषा पर बहुत अधिक निर्भर करता है:
C ++ में, RAII के उचित उपयोग से कंपाइलर उन वस्तुओं को स्वचालित रूप से नष्ट कर देता है जो दायरे से बाहर जाती हैं, ताकि संसाधनों / लॉक को वैसे भी साफ किया जा सके, और गोटो की कोई वास्तविक आवश्यकता नहीं होगी।
जावा में (ऊपर जावा के लेखक उद्धरण और यह देखने के लिए गोटो कोई आवश्यकता नहीं है उत्कृष्ट स्टैक ओवरफ़्लो जवाब ): कचरा कलेक्टर साफ है कि गंदगी, break
, continue
, और try
/ catch
अपवाद हैंडलिंग सभी मामले में जहां कवर goto
सहायक हो सकता है, लेकिन में एक सुरक्षित और बेहतर तौर तरीका। जावा की लोकप्रियता यह साबित करती है कि आधुनिक भाषा में गोटो के बयान से बचा जा सकता है।
प्रसिद्ध SSL गोटो पर ज़ूम करना भेद्यता को विफल करता है
महत्वपूर्ण अस्वीकरण: टिप्पणियों में उग्र चर्चा के मद्देनजर, मैं स्पष्ट करना चाहता हूं कि मैं इस बात का ढोंग नहीं करता हूं कि गोटो का बयान ही इस बग का कारण है। मैं इस बात का ढोंग नहीं करता कि बिना गोटे के बग नहीं होगा। मैं केवल यह दिखाना चाहता हूं कि एक गोटो एक गंभीर बग में शामिल हो सकता है।
मुझे नहीं पता कि goto
प्रोग्रामिंग के इतिहास में कितने गंभीर कीड़े हैं : विवरणों का अक्सर संचार नहीं किया जाता है। हालाँकि एक प्रसिद्ध Apple SSL बग था जिसने iOS की सुरक्षा को कमजोर कर दिया था। इस बग के कारण goto
बयान गलत कथन था।
कुछ लोगों का तर्क है कि बग का मूल कारण अपने आप में गोटो स्टेटमेंट नहीं था, बल्कि एक गलत कॉपी / पेस्ट, एक भ्रामक इंडेंटेशन, सशर्त ब्लॉक के आसपास घुंघराले ब्रेसिज़ या शायद डेवलपर की कामकाजी आदतें गायब थीं। मैं उनमें से किसी की भी पुष्टि नहीं कर सकता: ये सभी तर्क संभावित परिकल्पना और व्याख्या हैं। वास्तव में कोई नहीं जानता। ( इस बीच, एक मर्ज की परिकल्पना गलत हो गई, जैसा कि किसी ने टिप्पणी में सुझाया था, एक ही कार्य में कुछ अन्य इंडेंटेशन विसंगतियों को देखते हुए एक बहुत अच्छा उम्मीदवार लगता है )।
एकमात्र उद्देश्य तथ्य यह है कि एक डुप्लिकेट goto
को समय से पहले फ़ंक्शन से बाहर निकलने के लिए प्रेरित किया गया था। कोड को देखते हुए, केवल अन्य एकल कथन जो एक ही प्रभाव का कारण हो सकता था, एक वापसी होगी।
इस फ़ाइलSSLEncodeSignedServerKeyExchange()
में त्रुटि है :
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)
goto fail;
if ((err =...) !=0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail; // <====OUCH: INDENTATION MISLEADS: THIS IS UNCONDITIONDAL!!
if (...)
goto fail;
... // Do some cryptographic operations here
fail:
... // Free resources to process error
सशर्त ब्लॉक के चारों ओर घुंघराले ब्रेसिज़ बग को रोक सकते थे:
यह या तो संकलन (और इसलिए सुधार) या एक अनावश्यक हानिरहित गोटो में एक वाक्यविन्यास त्रुटि के लिए नेतृत्व करेगा। वैसे, जीसीसी 6 असंगत इंडेंटेशन का पता लगाने के लिए अपनी वैकल्पिक चेतावनी के कारण इन त्रुटियों को हाजिर करने में सक्षम होगा।
लेकिन पहले स्थान पर, इन सभी गोटो को अधिक संरचित कोड के साथ टाला जा सकता था। तो गोटो कम से कम अप्रत्यक्ष रूप से इस बग का एक कारण है। कम से कम दो अलग-अलग तरीके हैं जो इसे टाल सकते थे:
दृष्टिकोण 1: यदि खंड या नेस्टेड if
एस
क्रमिक रूप से त्रुटि के लिए बहुत सारी शर्तों का परीक्षण करने के बजाय, और हर बार fail
समस्या के मामले में एक लेबल पर भेजने के बाद , कोई व्यक्ति क्रिप्टोग्राफिक संचालन को अंजाम देने के लिए चुन सकता है, जो if
केवल तभी होगा जब कोई गलत पूर्व शर्त नहीं थी:
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) == 0 &&
(err = ...) == 0 ) &&
(err = ReadyHash(&SSLHashSHA1, &hashCtx)) == 0) &&
...
(err = ...) == 0 ) )
{
... // Do some cryptographic operations here
}
... // Free resources
दृष्टिकोण 2: एक त्रुटि संचायक का उपयोग करें
यह दृष्टिकोण इस तथ्य पर आधारित है कि यहां लगभग सभी कथन err
त्रुटि कोड सेट करने के लिए कुछ फ़ंक्शन को कॉल करते हैं , और शेष कोड को केवल err
0 (यानी, त्रुटि के बिना निष्पादित फ़ंक्शन) निष्पादित करते हैं। एक अच्छा सुरक्षित और पठनीय विकल्प है:
bool ok = true;
ok = ok && (err = ReadyHash(&SSLHashSHA1, &hashCtx))) == 0;
ok = ok && (err = NextFunction(...)) == 0;
...
ok = ok && (err = ...) == 0;
... // Free resources
यहां, एक भी गोटो नहीं है: विफलता से बाहर निकलने के लिए जल्दी से कूदने का कोई जोखिम नहीं है। और नेत्रहीन रूप से एक गलत लाइन या भूल जाना आसान होगा ok &&
।
यह निर्माण अधिक कॉम्पैक्ट है। यह इस तथ्य पर आधारित है कि C में, तार्किक और ( &&
) के दूसरे भाग का मूल्यांकन केवल तभी किया जाता है जब पहला भाग सत्य होता है। वास्तव में, एक अनुकूलन करने वाले कंपाइलर द्वारा उत्पन्न कोडांतरक गोटो के साथ मूल कोड के लगभग बराबर होता है: ऑप्टिमाइज़र परिस्थितियों की श्रृंखला को बहुत अच्छी तरह से पता लगाता है और कोड उत्पन्न करता है, जो पहले गैर-शून्य रिटर्न मान पर अंत ( ऑनलाइन प्रूफ ) के लिए कूदता है ।
आप फ़ंक्शन के अंत में एक निरंतरता जांच की परिकल्पना भी कर सकते हैं जो परीक्षण चरण के दौरान ठीक ध्वज और त्रुटि कोड के बीच बेमेल पहचान कर सकता है।
assert( (ok==false && err!=0) || (ok==true && err==0) );
इस तरह की गलतियों को ==0
अनजाने में !=0
या तार्किक संबंधक त्रुटियों के साथ बदल दिया जाता है जिसे डिबगिंग चरण के दौरान आसानी से देखा जा सकता है।
जैसा कि कहा गया: मैं इस बात का ढोंग नहीं करता कि वैकल्पिक निर्माण किसी भी बग से बचते थे। मैं केवल यह कहना चाहता हूं कि वे बग को और अधिक कठिन बना सकते थे।