सी में गोटो के लिए यह एक सभ्य उपयोग-मामला है?


59

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

मैंने हाल ही में gotoकमांड के लिए कुछ अरुचि के बारे में सुना है , लेकिन मैंने हाल ही में इसके लिए एक अच्छा उपयोग-मामला भी पाया है।

इस तरह कोड:

error = function_that_could_fail_1();
if (!error) {
    error = function_that_could_fail_2();
    if (!error) {
        error = function_that_could_fail_3();
        ...to the n-th tab level!
    } else {
        // deal with error, clean up, and return error code
    }
} else {
    // deal with error, clean up, and return error code
}

अगर साफ-सुथरा हिस्सा सब समान है, तो क्या इस तरह थोड़ा प्रेटियर (मेरी राय?) लिखा जा सकता है:

error = function_that_could_fail_1();
if(error) {
    goto cleanup;
}
error = function_that_could_fail_2();
if(error) {
    goto cleanup;
}
error = function_that_could_fail_3();
if(error) {
    goto cleanup;
}
...
cleanup:
// deal with error if it exists, clean up
// return error code

क्या यह gotoC का एक सामान्य या स्वीकार्य उपयोग-मामला है? क्या ऐसा करने का कोई अलग / बेहतर तरीका है?



2
चीनी कोटिंग से अधिक, संकलक सफाई और त्रुटि की जांच की गारंटी देता है, जबकि सी में आपको इसे मैन्युअल रूप से करना होगा, DRY का उल्लंघन करना और यह गारंटी देना कि किसी स्थान पर आप त्रुटि कोड की जांच करने के लिए बहुत आलसी थे या गलत लेबल या ऐसा कुछ करने के लिए गए थे।
डेडएमजी

3
@Izkata: नहीं, बिंदु कार्य (विनाशकर्ता की तरह) जो कर रहे हैं करने के लिए है परोक्ष संकलक द्वारा कहा जाता है जब एक अपवाद फेंक दिया है।
DeadMG

6
@DeadMG: हमेशा दूसरी भाषा का उपयोग करना एक विकल्प नहीं है।
बेंजामिन क्लॉस्टर

13
मुझे लगता है कि उपयुक्त लेबल के नामकरण के साथ, उदाहरण काफी सभ्य लग सकता है:goto hell;
gnat

जवाबों:


48

gotoबयान (और उसके संगत लेबल) एक प्रवाह नियंत्रण कर रहे हैं आदिम (एक बयान की सशर्त निष्पादन के साथ)। उसके द्वारा, मेरा मतलब है कि वे आपको प्रोग्राम फ्लो कंट्रोल नेटवर्क बनाने की अनुमति देने के लिए हैं। आप उन्हें एक फ़्लोचार्ट के नोड्स के बीच तीर के मॉडलिंग के रूप में सोच सकते हैं।

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

फिर भी C में सभी संभावित संरचित प्रोग्रामिंग निर्माण शामिल नहीं हैं। (यह मेरे लिए स्पष्ट नहीं है कि सभी प्रासंगिक लोगों को अभी तक खोजा जा चुका है; खोज की दर अब धीमी है, लेकिन मैं यह कहते हुए कूदने में संकोच करूंगा कि सभी मिल गए हैं।) हम जिनके बारे में जानते हैं, सी में निश्चित रूप से कमी है। try/ catch/ finallyसंरचना (अपवाद भी)। इसमें मल्टी-लेवल break-फ्रॉम-लूप का भी अभाव है । ये ऐसी चीजें हैं, जिन्हें gotoलागू करने के लिए इस्तेमाल किया जा सकता है। इन्हें करने के लिए अन्य योजनाओं का उपयोग करना भी संभव है - हम जानते हैं कि सी में गैर का पर्याप्त सेट है-gotoआदिम - लेकिन इनमें अक्सर ध्वज चर और बहुत अधिक जटिल लूप या गार्ड की स्थिति पैदा करना शामिल होता है; डेटा विश्लेषण के साथ नियंत्रण विश्लेषण के उलझाव को बढ़ाने से कार्यक्रम को समग्र रूप से समझना कठिन हो जाता है। यह कंपाइलर के लिए अनुकूलन करने और सीपीयू के लिए तेजी से निष्पादित करने के लिए और अधिक कठिन बनाता है (सबसे प्रवाह नियंत्रण निर्माण - और निश्चित रूप से goto - बहुत सस्ते हैं)।

इस प्रकार, जब gotoतक आपको आवश्यकता नहीं होती है, तब तक आपको पता नहीं होना चाहिए कि यह मौजूद है और इसकी आवश्यकता हो सकती है, और यदि आपको इसकी आवश्यकता है, तो आपको बहुत बुरा नहीं लगना चाहिए। एक मामले का एक उदाहरण जहां इसकी आवश्यकता होती है, संसाधन डीलक्लोलेशन होता है जब कोई फ़ंक्शन एक त्रुटि स्थिति देता है। (वह है, try/ finally।) यह लिखना संभव है कि इसके बिना gotoलेकिन ऐसा करने से अपनी खुद की गिरावट हो सकती है, जैसे कि इसे बनाए रखने की समस्याएं। मामले का एक उदाहरण:

int frobnicateTheThings() {
    char *workingBuffer = malloc(...);
    int i;

    for (i=0 ; i<numberOfThings ; i++) {
        if (giveMeThing(i, workingBuffer) != OK)
            goto error;
        if (processThing(workingBuffer) != OK)
            goto error;
        if (dispatchThing(i, workingBuffer) != OK)
            goto error;
    }

    free(workingBuffer);
    return OK;

  error:
    free(workingBuffer);
    return OOPS;
}

कोड और भी छोटा हो सकता है, लेकिन यह बिंदु प्रदर्शित करने के लिए पर्याप्त है।


4
+1: सी गोटो में तकनीकी रूप से कभी भी "आवश्यक" नहीं होता है - हमेशा ऐसा करने का एक तरीका है, यह गड़बड़ हो जाता है .....
मिसा

1
आप पसंद करेंगे try/catch/finallyकरने के लिए gotoइसी तरह की है, फिर भी और अधिक व्यापक है (क्योंकि यह कई कार्यों / मॉड्यूल भर में फैल सकता है) स्पेगेटी कोड का उपयोग संभव है के रूप के बावजूद try/catch/finally?
ऑटिस्टिक

65

हाँ।

इसका उपयोग, उदाहरण के लिए, लिनक्स कर्नेल में किया जाता है। यहाँ लगभग एक दशक पहले से एक धागे के अंत से एक ईमेल है , मेरा खदान:

से: रॉबर्ट लव
विषय: पुन: 2.6.0-परीक्षण का कोई मौका *?
दिनांक: 12 जनवरी 2003 17:58:06 -0500

सूर्य पर, 2003-01-12 17:22 पर, रोब विल्केन्स ने लिखा:

मैं कहता हूं कि "कृपया गोटो का उपयोग न करें" और इसके बजाय एक "क्लीनअप_लॉक" फ़ंक्शन है और इसे सभी रिटर्न स्टेटमेंट से पहले जोड़ दें .. यह एक बोझ नहीं होना चाहिए। हां, यह डेवलपर को थोड़ा कठिन काम करने के लिए कह रहा है, लेकिन अंतिम परिणाम बेहतर कोड है।

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

न ही पढ़ना आसान है।

एक अंतिम तर्क के रूप में, यह हमें सफाई से हमेशा की तरह ढेर-esque हवा करते हैं नहीं करता है और तनाव कम , यानी

        do A
        if (error)
            goto out_a;
        do B
        if (error)
            goto out_b;
        do C
        if (error)
            goto out_c;
        goto out;
        out_c:
        undo C
        out_b:
        undo B:
        out_a:
        undo A
        out:
        return ret;

अब इसे बंद करो।

रॉबर्ट लव

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


21
ध्यान दें कि कच्ची गति बनाम पठनीयता पर प्राथमिकता के संबंध में एक कर्नेल गैर-कर्नेल कार्यक्रम से अलग है। दूसरे शब्दों में, उन्होंने ALREADY को प्रोफाइल किया है और पाया है कि उन्हें गोटो के साथ गति के लिए अपने कोड को अनुकूलित करने की आवश्यकता है।

11
स्टैक पर धक्का दिए बिना वास्तव में त्रुटि को साफ करने के लिए स्टैक अन-विंड का उपयोग करना! यह गोटो का एक भयानक उपयोग है।
20:30 बजे mike30

1
@ user1249, बकवास, आप हर {पास्ट, मौजूदा, भविष्य} ऐप को प्रोफाइल नहीं कर सकते हैं जो {लाइब्रेरी, काईट} कोड के एक टुकड़े का उपयोग करता है। आपको बस तेज होना चाहिए।
पेसियर

1
असंबंधित: मैं आश्चर्यचकित हूं कि कुछ भी प्राप्त करने के लिए लोग मेलिंग सूचियों का उपयोग कैसे कर सकते हैं, अकेले ऐसे बड़े पैमाने पर परियोजनाएं दें। यह बस इतना है ... आदिम। लोगों को संदेशों की आग के साथ कैसे आना है ?!
अलेक्जेंडर

2
मुझे मॉडरेशन की कोई चिंता नहीं है। यदि कोई व्यक्ति इंटरनेट पर कुछ गधे द्वारा दूर किए जाने के लिए नरम है, तो आपकी परियोजना शायद उनके बिना बेहतर है। मैं आने वाले संदेशों के बैराज के साथ बनाए रखने की अव्यवहारिकता के बारे में अधिक चिंतित हूं, और उदाहरण के लिए, उद्धरणों का ट्रैक रखने के लिए संभवतः आपको बहुत कम टूलींग के साथ स्वाभाविक रूप से पीछे की चर्चा कैसे हो सकती है।
अलेक्जेंडर

14

मेरी राय में, आपके द्वारा पोस्ट किया गया कोड एक मान्य उपयोग का एक उदाहरण है goto, क्योंकि आप केवल नीचे की ओर कूदते हैं और केवल एक आदिम अपवाद हैंडलर की तरह इसका उपयोग करते हैं।

हालाँकि , पुराने गोटो डिबेट के कारण, प्रोग्रामर gotoकुछ 40 वर्षों से बचते रहे हैं और इसलिए उन्हें गोटो के साथ कोड पढ़ने की आदत नहीं है। यह गोटो से बचने का एक वैध कारण है: यह केवल मानक नहीं है।

मैं कोड को फिर से लिखूंगा क्योंकि C प्रोग्रामर द्वारा आसानी से पढ़ा जा सकता है:

Error some_func (void)
{
  Error error;
  type_t* resource = malloc(...);

  error = some_other_func (resource);

  free (resource);

  /* error handling can be placed here, or it can be returned to the caller */

  return error;
}


Error some_other_func (type_t* resource)  // inline if needed
{
  error = function_that_could_fail_1();
  if(error)
  {
    return error;
  }

  /* ... */

  error = function_that_could_fail_2();
  if(error)
  {
    return error;
  }

  /* ... */

  return ok;
}

इस डिजाइन के लाभ:

  • वास्तविक कार्य करने वाले फ़ंक्शन को अपने एल्गोरिथ्म के लिए अप्रासंगिक होने वाले कार्यों से खुद को चिंतित होने की आवश्यकता नहीं है, जैसे डेटा आवंटित करना।
  • कोड सी प्रोग्रामर को कम विदेशी लगेगा, क्योंकि वे गोटो और लेबल से डरते हैं।
  • एल्गोरिथ्म कर रहे फ़ंक्शन के बाहर, आप एक ही स्थान पर त्रुटि से निपटने और निपटान को केंद्रीकृत कर सकते हैं। यह अपने स्वयं के परिणामों को संभालने के लिए एक समारोह के लिए कोई मतलब नहीं है।

11

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


9

जावा में आप इसे इस तरह से करेंगे:

makeCalls:  {
    error = function_that_could_fail_1();
    if (error) {
        break makeCalls;
    }
    error = function_that_could_fail_2();
    if (error) {
        break makeCalls;
    }
    error = function_that_could_fail_3();
    if (error) {
        break makeCalls;
    }
    ...
    return 0;  // No error code.
}
// deal with error if it exists, clean up
// return error code

मैं इसका भरपूर उपयोग करता हूं। अधिकांश के रूप में मुझे नापसंद है goto, अधिकांश सी-शैली भाषाओं में मैं आपके कोड का उपयोग करता हूं; इसे करने का कोई और अच्छा तरीका नहीं है। (नेस्टेड लूप्स से बाहर कूदना एक समान मामला है; जावा में मैं एक लेबल का उपयोग करता हूं breakऔर हर जगह मैं एक का उपयोग करता हूं goto!)


3
अजी, यह एक साफ-सुथरी नियंत्रण संरचना है।
१०:१०

4
यह वाकई दिलचस्प है। मैं सामान्य रूप से जावा में इसके लिए ट्राइ / कैच / अंततः संरचना का उपयोग करने के बारे में सोचूंगा (ब्रेकिंग के बजाय अपवादों को फेंकना)।
रॉब

5
यह वास्तव में अपठनीय है (कम से कम मेरे लिए)। यदि वर्तमान में, अपवाद कहीं बेहतर हैं।
m3th0dman

1
@ m3th0dman मैं इस विशेष उदाहरण (त्रुटि से निपटने) में आपसे सहमत हूं। लेकिन अन्य (गैर-असाधारण) मामले हैं जहां यह मुहावरा काम में आ सकता है।
कोनराड रुडोल्फ

1
अपवाद महंगे हैं, उन्हें एक त्रुटि, स्टैकट्रेस और बहुत अधिक कबाड़ उत्पन्न करने की आवश्यकता है। यह लेबल ब्रेक चेकिंग लूप से एक साफ निकास देता है। जब तक कोई स्मृति और गति की परवाह नहीं करता है, तब तक मुझे परवाह है कि मैं एक अपवाद का उपयोग करता हूं।
Tschallacka

8

मुझे लगता है कि यह है एक सभ्य उपयोग के मामले, लेकिन इस मामले में "त्रुटि" एक बूलियन मान से ज्यादा कुछ नहीं है, वहाँ एक अलग तरीके से पूरा करने के लिए आप क्या चाहते है:

error = function_that_could_fail_1();
error = error || function_that_could_fail_2();
error = error || function_that_could_fail_3();
if(error)
{
     // do cleanup
}

यह बूलियन ऑपरेटरों के शॉर्ट-सर्किट मूल्यांकन का उपयोग करता है। यदि यह "बेहतर", आपके व्यक्तिगत स्वाद पर निर्भर है और आप उस मुहावरे के आदी कैसे हैं।


1
इसके साथ समस्या यह है कि errorसभी ओ'रिंग के साथ इसका मूल्य अर्थहीन हो सकता है।
जेम्स

@ जेम्स: आपकी टिप्पणी के कारण मेरे उत्तर को संपादित किया
डॉक्टर ब्राउन

1
यह पर्याप्त नहीं है। यदि पहले फ़ंक्शन के दौरान कोई त्रुटि हुई, तो मैं दूसरे या तीसरे फ़ंक्शन को निष्पादित नहीं करना चाहता।
रॉब

2
यदि शॉर्ट-हैंड मूल्यांकन के साथ आपका मतलब शॉर्ट-सर्किट मूल्यांकन से है, तो यह ठीक वैसा नहीं है जैसा कि बिटवाइज़ के उपयोग के कारण या तार्किक OR के बजाय यहाँ किया गया है।
क्रिस का कहना है कि

1
@ChristianRau: धन्यवाद, मेरे जवाब के अनुसार संपादित किया
डॉक्टर ब्राउन

6

लिनक्स शैली मार्गदर्शिका विशिष्ट कारणों का उपयोग करती है gotoजो आपके उदाहरण के अनुरूप हैं:

https://www.kernel.org/doc/Documentation/process/coding-style.rst

गोटो का उपयोग करने का औचित्य है:

  • बिना शर्त बयान समझने और अनुसरण करने में आसान होते हैं
  • घोंसला बनाना कम हो जाता है
  • संशोधनों को रोकने पर व्यक्तिगत निकास बिंदुओं को अपडेट नहीं करने से त्रुटियां होती हैं
  • कंपाइलर काम को निरर्थक कोड को दूर करने के लिए बचाता है;)

अस्वीकरण मैं अपना काम साझा करने वाला नहीं हूं। यहाँ के उदाहरण थोड़े वंचित हैं इसलिए कृपया मेरे साथ सहन करें।

यह स्मृति प्रबंधन के लिए अच्छा है। मैंने हाल ही में कोड पर काम किया था जिसे गतिशील रूप से मेमोरी आवंटित की गई थी (उदाहरण के लिए char *फ़ंक्शन द्वारा लौटाया गया)। एक फ़ंक्शन जो पथ को देखता है और यह पता लगाता है कि पथ के टोकन को पार्स करके पथ मान्य है या नहीं:

tmp_string = strdup(string);
token = strtok(tmp_string,delim);
while( token != NULL ){
    ...
    some statements, some involving dynamically allocated memory
    ...
    if ( check_this() ){
        free(var1);
        free(var2);
        ...
        free(varN);
        return 1;
    }
    ...
    some more stuff
    ...
    if(something()){
        if ( check_that() ){
            free(var1);
            free(var2);
            ...
            free(varN);
            return 1;
        } else {
            free(var1);
            free(var2);
            ...
            free(varN);
            return 0;
        }
    }
    token = strtok(NULL,delim);
}

free(var1);
free(var2);
...
free(varN);
return 1;

अब मेरे लिए, निम्न कोड बहुत अच्छा है और अगर आप को जोड़ने की आवश्यकता है, तो इसे बनाए रखना आसान है varNplus1:

int retval = 1;
tmp_string = strdup(string);
token = strtok(tmp_string,delim);
while( token != NULL ){
    ...
    some statements, some involving dynamically allocated memory
    ...
    if ( check_this() ){
        retval = 1;
        goto out_free;
    }
    ...
    some more stuff
    ...
    if(something()){
        if ( check_that() ){
            retval = 1;
            goto out_free;
        } else {
            retval = 0;
            goto out_free;
        }
    }
    token = strtok(NULL,delim);
}

out_free:
free(var1);
free(var2);
...
free(varN);
return retval;

अब कोड में इसके साथ सभी प्रकार की अन्य समस्याएं थीं, अर्थात् एन कहीं 10 से ऊपर था, और फ़ंक्शन 450 लाइनों से अधिक था, जिसमें कुछ स्थानों पर 10 स्तर की नेस्टीनेस थी।

लेकिन मैंने अपने पर्यवेक्षक को इसे रिफैक्ट करने की पेशकश की, जो मैंने किया और अब यह उन कार्यों का एक गुच्छा है जो सभी छोटे हैं, और उन सभी में लिनक्स शैली है

int function(const char * param)
{
    int retval = 1;
    char * var1 = fcn_that_returns_dynamically_allocated_string(param);
    if( var1 == NULL ){
        retval = 0;
        goto out;
    }

    if( isValid(var1) ){
         retval = some_function(var1);
         goto out_free;
    }

    if( isGood(var1) ){
         retval = 0;
         goto out_free;
    }

out_free:
    free(var1);
out:
    return retval;
}

यदि हम gotoएस के बिना समकक्ष मानते हैं :

int function(const char * param)
{
    int retval = 1;
    char * var1 = fcn_that_returns_dynamically_allocated_string(param);
    if( var1 != NULL ){

       if( isValid(var1) ){
            retval = some_function(var1);
       } else {
          if( isGood(var1) ){
               retval = 0;
          }
       }
       free(var1);

    } else {
       retval = 0;
    }

    return retval;
}

मेरे लिए, पहले मामले में, मेरे लिए यह स्पष्ट है कि यदि पहला फ़ंक्शन वापस आता है NULL, तो हम यहाँ से बाहर निकल रहे हैं और हम लौट रहे हैं 0। दूसरे मामले में, मुझे यह देखने के लिए नीचे स्क्रॉल करना होगा कि क्या पूरे फ़ंक्शन में है। पहले वाले ने मुझे शैलीगत रूप से (" out" नाम ) को इंगित किया और दूसरा इसे वाक्यगत रूप से दर्शाता है । पहला वाला अभी भी अधिक स्पष्ट है।

इसके अलावा, मैं free()एक समारोह के अंत में बयान देना पसंद करता हूं । यह आंशिक रूप से है, क्योंकि मेरे अनुभव में, free()कार्यों के बीच में बयानों से बदबू आती है और मुझे संकेत मिलता है कि मुझे एक सबरूटीन बनाना चाहिए। इस मामले में, मैंने var1अपने फ़ंक्शन में बनाया और free()इसे एक सबरूटीन में नहीं कर सकता था , लेकिन यही कारण है कि goto out_free, गोटो आउट शैली इतनी व्यावहारिक है।

मुझे लगता है कि प्रोग्रामर को यह विश्वास दिलाने की जरूरत है कि gotoबुराई है। फिर, जब वे पर्याप्त परिपक्व हो जाते हैं, तो उन्हें लिनक्स स्रोत कोड ब्राउज़ करना चाहिए और लिनक्स शैली गाइड पढ़ना चाहिए।

मुझे यह जोड़ना चाहिए कि मैं इस शैली का लगातार उपयोग करता हूं, प्रत्येक फ़ंक्शन में एक इंट retval, एक out_freeलेबल और एक आउट लेबल होता है। शैलीगत स्थिरता के कारण, पठनीयता में सुधार हुआ है।

बोनस: टूटता है और जारी रहता है

कहते हैं कि आपके पास कुछ समय है

char *var1, *var2;
char line[MAX_LINE_LENGTH];
while( sscanf(line,... ){
    var1 = functionA(line,count);
    var2 = functionB(line,count);

    if( functionC(var1, var2){
         count++
         continue;
    }

    ...
    a bunch of statements
    ...

    count++;
    free(var1);
    free(var2);
}

इस कोड के साथ अन्य चीजें भी गलत हैं, लेकिन एक बात जारी बयान है। मैं पूरी बात को फिर से लिखना चाहूंगा, लेकिन मुझे इसे छोटे तरीके से संशोधित करने का काम सौंपा गया था। मुझे इसे फिर से शुरू करने में कुछ दिन लगेंगे, जो मुझे संतुष्ट करेगा, लेकिन वास्तविक बदलाव आधे दिन के काम के बारे में था। समस्या भले ही हम यह है कि continue'हम अभी भी मुक्त करने की जरूरत है var1और var2। मुझे एक जोड़ना था var3, और इसने मुझे मुक्त () कथनों को आईने में ढालने के लिए प्रेरित किया।

मैं उस समय एक अपेक्षाकृत नया प्रशिक्षु था, लेकिन मैं कुछ समय पहले मस्ती के लिए लिनक्स स्रोत कोड देख रहा था, इसलिए मैंने अपने पर्यवेक्षक से पूछा कि क्या मैं एक गोटो बयान का उपयोग कर सकता हूं। उसने कहा हाँ, और मैंने ऐसा किया:

char *var1, *var2;
char line[MAX_LINE_LENGTH];
while( sscanf(line,... ){
    var1 = functionA(line,count);
    var2 = functionB(line,count);
    var3 = newFunction(line,count);

    if( functionC(var1, var2){
         goto next;
    }

    ...
    a bunch of statements
    ...
next:
    count++;
    free(var1);
    free(var2);
}

मुझे लगता है कि जारी रखना ठीक है, लेकिन मेरे लिए वे एक अदृश्य लेबल के साथ एक गोटो की तरह हैं। वही टूट जाता है। मैं तब भी जारी रखना या तोड़ना पसंद करूंगा जब तक कि यहाँ मामला नहीं था, यह आपको कई स्थानों पर संशोधन करने के लिए मजबूर करता है।

और मुझे यह भी जोड़ना चाहिए कि यह goto next;और next:लेबल का उपयोग मेरे लिए असंतोषजनक है। वे केवल बयान free()और count++बयानों को प्रतिबिंबित करने से बेहतर हैं ।

gotoलगभग हमेशा गलत होते हैं, लेकिन किसी को पता होना चाहिए कि वे कब उपयोग करना अच्छा है।

एक चीज जिस पर मैंने चर्चा नहीं की, वह है त्रुटि हैंडलिंग जो अन्य उत्तरों द्वारा कवर की गई है।

प्रदर्शन

स्ट्रेटोक के कार्यान्वयन को देख सकते हैं () http://opensource.apple.com//source/Libc/Libc-167/string.subproj/strtok.c

#include <stddef.h>
#include <string.h>

char *
strtok(s, delim)
    register char *s;
    register const char *delim;
{
    register char *spanp;
    register int c, sc;
    char *tok;
    static char *last;


    if (s == NULL && (s = last) == NULL)
        return (NULL);

    /*
     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
     */
cont:
    c = *s++;
    for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
        if (c == sc)
            goto cont;
    }

    if (c == 0) {       /* no non-delimiter characters */
        last = NULL;
        return (NULL);
    }
    tok = s - 1;

    /*
     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
     * Note that delim must have one NUL; we stop if we see that, too.
     */
    for (;;) {
        c = *s++;
        spanp = (char *)delim;
        do {
            if ((sc = *spanp++) == c) {
                if (c == 0)
                    s = NULL;
                else
                    s[-1] = 0;
                last = s;
                return (tok);
            }
        } while (sc != 0);
    }
    /* NOTREACHED */
}

कृपया मुझे सही करें अगर मैं गलत हूं, लेकिन मेरा मानना ​​है कि cont:लेबल और goto cont;स्टेटमेंट प्रदर्शन के लिए हैं (वे निश्चित रूप से कोड को अधिक पठनीय नहीं बनाते हैं)। उन्हें ऐसा करने से पठनीय कोड से बदला जा सकता है

while( isDelim(*s++,delim));

सीमांकक छोड़ने के लिए। लेकिन जितनी जल्दी हो सके और अनावश्यक फ़ंक्शन कॉल से बचने के लिए, वे इस तरह से करते हैं।

मैंने पेपर को दिज्क्स्ट्रा द्वारा पढ़ा और मुझे यह काफी गूढ़ लगा।

Google "dijkstra गोटो स्टेटमेंट को हानिकारक माना जाता है" क्योंकि मेरे पास 2 से अधिक लिंक पोस्ट करने के लिए पर्याप्त प्रतिष्ठा नहीं है।

मैंने इसे गोटो के इस्तेमाल न करने के एक कारण के रूप में उद्धृत किया है और इसे पढ़ने से कुछ भी नहीं बदला है क्योंकि गोटो के मेरे उपयोगों को छुपाया गया है।

परिशिष्ट :

मैं एक साफ नियम के साथ आया हूं जबकि यह सब जारी है और टूटता है।

  • यदि थोड़ी देर के लूप में, आपके पास जारी रहता है, तो जबकि लूप का शरीर एक फ़ंक्शन होना चाहिए और जारी एक रिटर्न स्टेटमेंट होना चाहिए।
  • यदि थोड़ी देर के लूप में, आपके पास एक ब्रेक स्टेटमेंट है, तो जबकि लूप स्वयं एक फ़ंक्शन होना चाहिए और ब्रेक एक रिटर्न स्टेटमेंट बनना चाहिए।
  • अगर आपके पास दोनों हैं, तो कुछ गलत हो सकता है।

स्कोप के मुद्दों के कारण यह हमेशा संभव नहीं है, लेकिन मैंने पाया है कि ऐसा करने से मेरे कोड के बारे में तर्क करना बहुत आसान हो जाता है। मैंने देखा था कि जब भी कुछ समय के लिए लूप में ब्रेक होता था या जारी रहता था, तो इससे मुझे बुरा एहसास होता था।


2
+1 लेकिन क्या मैं एक बिंदु पर असहमत हो सकता हूं? "मुझे लगता है कि प्रोग्रामर को यह विश्वास दिलाने की आवश्यकता है कि गोटो दुष्ट हैं।" शायद ऐसा हो, लेकिन मैंने पहली बार 1975 में बिना किसी टेक्स्ट एडिटर के लाइन नंबरों और GOTO के साथ BASIC में प्रोग्राम करना सीखा था। मैं दस साल बाद संरचित प्रोग्रामिंग से मिला, जिसके बाद मुझे अपने आप पर, बिना GOTO का उपयोग बंद करने में एक महीना लग गया, बिना किसी भी दबाव को रोकने के लिए। आज, मैं विभिन्न कारणों से साल में कई बार गोटो का उपयोग करता हूं, लेकिन यह बहुत ऊपर नहीं आता है। यह मानने के लिए नहीं लाया गया है कि GOTO बुराई है इससे मुझे कोई नुकसान नहीं हुआ है जो मुझे पता है, और यह शायद कुछ अच्छा भी कर सकता है। वो मैं ही हुं।
thb

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

1
@thb इसके अलावा, मजेदार कहानी, मैंने उस समय अपने पर्यवेक्षक से पूछा, GOTO का उपयोग करने की अनुमति के लिए एक प्रशिक्षु के रूप में और मैंने यह सुनिश्चित किया कि मैंने उसे समझाया कि मैं उन्हें एक विशिष्ट तरीके से उपयोग करने जा रहा हूं, जिस तरह से इसका उपयोग किया जाता है लिनक्स कर्नेल और उन्होंने कहा "ठीक है, यह समझ में आता है, और यह भी, मुझे नहीं पता था कि आप गोटो का सी में उपयोग कर सकते हैं"।
फिलिप कारफिन

1
@ मुझे पता नहीं है कि क्या लूप में गोटो के लिए अच्छा है (लूप को तोड़ने के बजाय) इस तरह ? खैर, यह एक बुरा उदाहरण है, लेकिन मुझे पता है कि नथ के स्ट्रक्चर्ड प्रोग्रामिंग पर गोटो स्टेटमेंट (उदाहरण 7 ए) के साथ क्विकॉर्ट स्टेट्स में जाने के लिए बहुत ही स्पष्ट नहीं है।
याईपहा

@ Yai0Phah मैं अपनी बात समझाऊंगा लेकिन मेरा स्पष्टीकरण आपके ठीक उदाहरण 7a को कम नहीं करता है! मैं उदाहरण का अनुमोदन करता हूं। फिर भी, बॉसी लोगों को गोटो के बारे में व्याख्यान देना पसंद करते हैं। 1985 के बाद से गोटो का व्यावहारिक उपयोग करना मुश्किल है, जो महत्वपूर्ण परेशानी का कारण बनता है, जबकि एक हानिरहित गोटो मिल सकता है जो प्रोग्रामर के काम को आसान करता है। गोटो तो आधुनिक प्रोग्रामिंग में शायद ही कभी उठता है, वैसे भी, जब यह उठता है, तो मेरी सलाह है, यदि आप इसका उपयोग करना चाहते हैं, तो आपको शायद इसका उपयोग करना चाहिए। गोटो ठीक है। गोटो के साथ मुख्य समस्या यह है कुछ का मानना है कि वह यह है कि बहिष्कार कर गोटो उन्हें स्मार्ट लग रहे हो।
THB

5

व्यक्तिगत रूप से मैं इसे इस तरह अधिक परिष्कृत करूंगा:

int DoLotsOfStuffThatCouldFail (paramstruct *params)
{
    int errcode = EC_NOERROR;

    if ((errcode = FunctionThatCouldFail1 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail2 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail3 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail4 (params)) != EC_NOERROR) return errcode;

    return EC_NOERROR;
}

void DoStuff (paramstruct *params)
{
    int errcode = EC_NOERROR;

    InitStuffThatMayNeedToBeCleaned (params);

    if ((errcode = DoLotsOfStuffThatCouldFail (params)) != EC_NOERROR)
    {
         CleanupAfterError (params, errcode);
    }
}

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

इस दृष्टिकोण के साथ एक बड़ा फायदा मुझे यह देखने में आता है कि FTCF2 और FTCF3 (या किसी मौजूदा करंट स्टेप को हटा दें) के बीच काल्पनिक भविष्य के अतिरिक्त कदम उठाने के लिए यह आसान और साफ-सुथरा दोनों है, इसलिए यह खुद को बेहतर बनाये रखता है मेरे कोड को मुझे लिंच नहीं करना चाहते हैं!) - गोटो एक तरफ, नेस्टेड संस्करण में कमी है।


1
मैंने अपने प्रश्न में यह नहीं बताया, लेकिन यह संभव है कि FTCF में समान पैरामीटर न हों, जिससे यह पैटर्न थोड़ा और जटिल हो जाए। हालांकि धन्यवाद।
रॉब जूल

3

MISRA (मोटर उद्योग सॉफ्टवेयर विश्वसनीयता एसोसिएशन) C कोडिंग दिशानिर्देशों पर एक नज़र डालें जो गोटो को सख्त मानदंडों के तहत अनुमति देते हैं (जो आपके उदाहरण मिलते हैं)

जहां मैं काम करता हूं वही कोड लिखा जाएगा - किसी भी गोटो की जरूरत नहीं है - उनके बारे में अनावश्यक धार्मिक बहस से बचना किसी भी सॉफ़्टवेयर हाउस में एक बड़ा प्लस है।

error = function_that_could_fail_1();
if(!error) {
  error = function_that_could_fail_2();
}
if(!error) {
  error = function_that_could_fail_3();
} 
if(!error) {
...
if (error) {
  cleanup:
} 

या "गोटो इन ड्रैग" के लिए - गोटो की तुलना में कुछ ज्यादा ही डरावना है, लेकिन "नो गोटो एवर!" शिविर) "निश्चित रूप से यह ठीक होना चाहिए, गोटो का उपयोग नहीं करता है" ...।

do {
  if (error = function_that_could_fail_1() ){
    break 
  }
  if (error = function_that_could_fail_2() ){
    break 
  }
  ....... 
} while (0) 
cleanup();
.... 

यदि फ़ंक्शन में एक ही पैरामीटर प्रकार है, तो उन्हें एक तालिका में डालें और एक लूप का उपयोग करें -


2
वर्तमान MISRA-C: 2004 दिशानिर्देश किसी भी रूप में गोटो की अनुमति नहीं देते हैं (नियम 14.4 देखें)। ध्यान दें कि MISRA समिति हमेशा इस बारे में भ्रमित रही है, उन्हें नहीं पता कि किस पैर पर खड़ा होना है। पहले, उन्होंने बिना शर्त के गोटो, जारी रखने आदि पर प्रतिबंध लगा दिया, लेकिन आगामी MISRA 2011 के मसौदे में वे फिर से अनुमति देना चाहते हैं। एक सनद के रूप में, कृपया ध्यान दें कि MISRA अगर-बयानों के अंदर असाइनमेंट पर प्रतिबंध लगाता है, तो बहुत अच्छे कारणों से क्योंकि यह गोटो के किसी भी उपयोग से कहीं अधिक खतरनाक है।

1
एक विश्लेषणात्मक दृष्टिकोण से, एक कार्यक्रम में एक ध्वज जोड़ना सभी कोड कोड को डुप्लिकेट करने के बराबर है जहां झंडा गुंजाइश है, हर if(flag)एक कॉपी में "यदि" शाखा है, और दूसरी प्रतिलिपि में प्रत्येक संगत बयान होने पर "ले" अन्य"। ध्वज के सेट और साफ़ करने वाले कार्य वास्तव में "गोटो" हैं जो कोड के उन दो संस्करणों के बीच कूदते हैं। ऐसे समय होते हैं जब झंडे का उपयोग किसी भी विकल्प की तुलना में क्लीनर होता है, लेकिन एक gotoलक्ष्य को बचाने के लिए एक ध्वज जोड़ना एक अच्छा व्यापार नहीं है।
सुपरकैट

1

मैं यह भी उपयोग करता हूं gotoकि यदि वैकल्पिक do/while/continue/breakहैकरी कम पठनीय होगी।

gotos का यह फायदा है कि उनके लक्ष्यों का एक नाम है और वे पढ़ते हैं goto something;। इससे अधिक पठनीय हो सकता है breakया continueयदि आप वास्तव में कुछ रोक या जारी नहीं कर रहे हैं।


4
कहीं भी एक do ... while(0)या किसी अन्य निर्माण के अंदर जो एक वास्तविक लूप नहीं है, लेकिन एक के उपयोग को रोकने के लिए एक हरे रंग की कोशिश goto
ऐब

1
आह, धन्यवाद, मुझे यह नहीं पता था कि "आखिर कोई ऐसा क्यों करेगा?" अभी तक निर्माण करता है।
बेंजामिन क्लॉस्टर

2
आमतौर पर, ऐसा करते समय / जारी / तोड़ हैकरी केवल तब अपठनीय हो जाती है जब इसे रखने वाला मॉड्यूल जिस तरह से पहले स्थान पर लंबे समय तक घर्षण करता है।
जॉन आर। स्ट्रॉहम

2
मुझे गोटो के उपयोग के औचित्य के रूप में इसमें कुछ भी नहीं मिला। तोड़ना और जारी रखना एक स्पष्ट परिणाम है। कहाँ जाये? लेबल कहां है? ब्रेक और गोटो आपको बताता है कि अगला कदम कहां है और इसके आस-पास है।
रिग

1
बेशक लूप के भीतर से लेबल दिखाई दे। मैं @ जॉन आर। स्ट्रॉहम की टिप्पणी के भाग की लंबाई से सहमत हूं। और आपकी बात, लूप हैकरी के लिए अनुवादित, "ब्रेक आउट ऑफ़ द क्या? यह एक लूप नहीं है!"। किसी भी मामले में, यह वही हो रहा है जो ओपी को डर था, इसलिए मैं चर्चा को छोड़ रहा हूं।
आइब जूल

-1
for (int y=0; y<height; ++y) {
    for (int x=0; x<width; ++x) {
        if (find(x, y)) goto found;
    }
}
found:

यदि केवल एक लूप है, तो breakठीक वैसे gotoही काम करता है , हालांकि कोई कलंक नहीं।
9000

6
-1: सबसे पहले, x और y पाए गए SCOPE के बाहर हैं :, तो इससे आपको कोई मदद नहीं मिलती है। दूसरा, जैसा कि कोड के साथ लिखा गया है, आप जिस तथ्य पर पहुंचे हैं: इसका मतलब यह नहीं है कि आपने वह पाया जो आप खोज रहे थे।
जॉन आर। स्ट्रॉहम

ऐसा इसलिए है क्योंकि यह सबसे छोटा उदाहरण है जिसके बारे में मैं कई संख्या में छोरों को तोड़ने के मामले में सोच सकता था। कृपया बेहतर लेबल या किए गए चेक के लिए इसे संपादित करने के लिए स्वतंत्र महसूस करें।
ऐब जूल

1
लेकिन यह भी ध्यान रखें कि सी फंक्शन जरूरी साइड-इफेक्ट-फ्री नहीं हैं।
ऐब जूल

1
@ JohnR.Strohm यह बहुत मायने नहीं रखता है ... 'पाया' लेबल का उपयोग लूप को तोड़ने के लिए किया जाता है, न कि चर को जांचने के लिए। अगर मैं चर की जाँच करना चाहता था तो मैं कुछ ऐसा कर सकता था: (int y = 0; y <height; ++ y) {for (int x = 0; x <चौड़ाई; ++ x) {if (ढूँढें) x, y)) {doSomeThingWith (x, y); गोटो मिला; }}} पाया:
YoYoYonnY

-1

हमेशा ऐसे शिविर होंगे जो कहते हैं कि एक रास्ता स्वीकार्य है और दूसरा ऐसा नहीं है। जिन कंपनियों के लिए मैंने काम किया है, उन्होंने गोटो के इस्तेमाल को बढ़ावा दिया है। व्यक्तिगत रूप से, मैं किसी भी समय ऐसा नहीं सोच सकता कि मैंने एक का उपयोग किया है, लेकिन इसका मतलब यह नहीं है कि वे खराब हैं , बस चीजों को करने का एक और तरीका है।

C में, मैं आमतौर पर निम्नलिखित कार्य करता हूं:

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

प्रसंस्करण के लिए, अपने गोटो उदाहरण का उपयोग करके, मैं यह करूँगा:

error = function_that_could_fail_1 (); if (त्रुटि) {error = function_that_could_fail_2 (); } अगर (त्रुटि) {त्रुटि = function_that_could_fail_3 (); }

कोई नेस्टिंग नहीं है, और यदि क्लॉस के अंदर, आप किसी भी त्रुटि रिपोर्टिंग कर सकते हैं, यदि चरण में कोई त्रुटि उत्पन्न हुई। तो, यह गोटो का उपयोग कर एक विधि की तुलना में "बदतर" होना नहीं है।

मुझे अभी तक एक ऐसे मामले में भागना है जहां किसी के पास कोई ऐसा तरीका है जो किसी अन्य विधि से नहीं किया जा सकता है और यह केवल पठनीय / समझने योग्य है और यह कुंजी, IMHO है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.