जैसा कि दूसरों ने कहा है, समस्या goto
स्वयं के साथ नहीं है; समस्या यह है कि लोग कैसे उपयोग करते हैं goto
, और यह कैसे कोड को समझने और बनाए रखने के लिए कठिन बना सकता है।
निम्नलिखित स्निपेट को कोड मानें:
i = 4;
label: printf( "%d\n", i );
किस मूल्य के लिए मुद्रित किया जाता है i
? यह कब छपता है? जब तक आप अपने समारोह में हर उदाहरण के लिए खाते हैं goto label
, आप नहीं जान सकते। उस लेबल की सरल उपस्थिति सरल निरीक्षण द्वारा कोड डिबग करने की आपकी क्षमता को नष्ट कर देती है । एक या दो शाखाओं के साथ छोटे कार्यों के लिए, बहुत अधिक समस्या नहीं है। छोटे कार्यों के लिए नहीं ...
90 के दशक की शुरुआत में हमें C कोड का ढेर दिया गया, जिसने एक 3 डी ग्राफिकल डिस्प्ले को प्रदर्शित किया और इसे तेजी से चलाने के लिए कहा। यह कोड की केवल 5000 पंक्तियों के बारे में था, लेकिन यह सभी में था main
, और लेखक ने goto
दोनों दिशाओं में लगभग 15 या तो एस शाखा का उपयोग किया था । यह शुरू करने के लिए बुरा कोड था , लेकिन उन लोगों की उपस्थिति goto
ने इसे इतना बदतर बना दिया। नियंत्रण के प्रवाह को समझने में मेरे सहकर्मी को लगभग 2 सप्ताह का समय लगा। इससे भी बेहतर, उन goto
कोडों के परिणामस्वरूप इतनी मजबूती से खुद को जोड़ लिया गया कि हम बिना कुछ तोड़े बदलाव नहीं कर सकते थे ।
हमने स्तर 1 अनुकूलन के साथ संकलन करने की कोशिश की, और संकलक ने सभी उपलब्ध रैम को खा लिया, फिर सभी उपलब्ध स्वैप किया, और फिर सिस्टम को घबरा दिया (जिसका शायद खुद से कोई लेना-देना नहीं था goto
, लेकिन मुझे उस उपाख्यान को वहां फेंकना पसंद है)।
अंत में, हमने ग्राहक को दो विकल्प दिए - आइए हम पूरी बात को स्क्रैच से फिर से लिखें, या तेजी से हार्डवेयर खरीदें।
उन्होंने तेजी से हार्डवेयर खरीदे।
उपयोग करने के लिए बोड के नियम goto
:
- शाखा ही आगे;
- नियंत्रण संरचनाओं को बायपास न करें (यानी,
if
या for
या while
बयान के शरीर में शाखा न करें );
goto
एक नियंत्रण संरचना के स्थान पर उपयोग न करें
वहाँ मामलों में जहां एक कर रहे हैं goto
है सही जवाब है, लेकिन वे दुर्लभ हैं (एक गहरा नेस्टेड लूप से बाहर तोड़ने के बारे में केवल जगह मैं इसका इस्तेमाल होता है)।
संपादित करें
उस अंतिम विवरण पर विस्तार करते हुए, यहाँ के लिए कुछ मान्य उपयोग मामलों में से एक है goto
। मान लें कि हमारे पास निम्नलिखित कार्य हैं:
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
for ( i = 0; i < N; i ++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
for ( j = 0; j < M; j++ )
{
arr[i][j] = malloc( sizeof *arr[i][j] * P );
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
return arr;
}
अब, हमारे पास एक समस्या है - क्या होगा यदि malloc
कॉल में से एक के बीच में विफल रहता है? पूरी तरह से एक घटना है कि हो सकता है, हम एक आंशिक रूप से आवंटित सरणी वापस नहीं करना चाहते हैं, और न ही हम केवल एक त्रुटि के साथ समारोह से बाहर जमानत चाहते हैं; हम अपने आप को साफ करना चाहते हैं और किसी भी आंशिक रूप से आवंटित मेमोरी को हटा देना चाहते हैं। एक भाषा में जो एक खराब आवंटन पर एक अपवाद फेंकता है, वह काफी सीधा है - आप सिर्फ एक अपवाद हैंडलर को खाली करने के लिए लिखते हैं जो पहले से ही आवंटित किया गया है।
C में, आपके पास संरचित अपवाद हैंडलिंग नहीं है; आपको प्रत्येक malloc
कॉल के रिटर्न मूल्य की जांच करनी होगी और उचित कार्रवाई करनी होगी ।
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( i = 0; i < N; i ++ )
{
if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
goto cleanup_1;
for ( j = 0; j < M; j++ )
{
if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
goto cleanup_2;
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
}
goto done;
cleanup_2:
// We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
while ( j-- )
free( arr[i][j] );
free( arr[i] );
// fall through
cleanup_1:
// We failed while allocating arr[i]; free up all previously allocated arr[i][j]
while ( i-- )
{
for ( j = 0; j < M; j++ )
free( arr[i][j] );
free( arr[i] );
}
free( arr );
arr = NULL;
done:
return arr;
}
क्या हम बिना उपयोग के ऐसा कर सकते हैं goto
? बेशक, हम कर सकते हैं - यह सिर्फ एक छोटे से अतिरिक्त बहीखाता की आवश्यकता है (और, व्यवहार में, यही वह रास्ता है जो मुझे लेना होगा)। लेकिन, यदि आप ऐसी जगहों की तलाश कर रहे हैं, जहां तुरंत बुरा अभ्यास या डिजाइन का संकेत goto
नहीं है , तो यह कुछ में से एक है।