2009-03-31 12 views
6

हाँ, प्रवाह को नियंत्रित करने के लिए मैक्रो दो संयुक्त निर्माणों नफरत करते थे। क्या यह उतना बुरा है जितना लगता है या इसे गोटो के उपयोग को नियंत्रित करने और उचित सफाई रणनीति प्रदान करने के लिए एक अच्छा तरीका माना जा सकता है?साथ 'गोटो'

काम पर हमने हमारे कोडिंग मानक में गोटो को अनुमति देने या नहीं करने के बारे में चर्चा की थी। आम तौर पर कोई भी गोटो के मुफ्त उपयोग की अनुमति नहीं देना चाहता था, लेकिन कुछ क्लीनअप कूद के लिए इसका इस्तेमाल करने के बारे में सकारात्मक थे। इस कोड में के रूप में:

void func() 
{ 
    char* p1 = malloc(16); 
    if(!p1){ 
     return; 
    } 

    char* p2 = malloc(16); 
    if(!p2){ 
     free(p1); 
     return; 
    } 

    char* p3 = malloc(16); 
    if(!p3){ 
     free(p1); 
     free(p2); 
     return; 
    } 
} 
विशेष रूप से निर्माता की तरह काम करता है में

कई आवंटन इस कर सकते हैं साथ:

void func() 
{ 
    char* p1 = malloc(16); 
    if(!p1) 
     goto cleanup; 

    char* p2 = malloc(16); 
    if(!p2) 
     goto cleanup; 

goto norm_cleanup; 

err_cleanup: 

    if(p1) 
     free(p1); 

    if(p2) 
     free(p2); 

norm_cleanup: 
} 

इस तरह के प्रयोग की abovious लाभ आप इस कोड के साथ खत्म करने की जरूरत नहीं है कि है कभी-कभी बहुत बुरा हो जाता है, कम से कम नहीं जब किसी को बीच में कुछ डालना पड़ता है।

तो, क्रम में गोटो उपयोग करने के लिए सक्षम होने के लिए, लेकिन अभी भी स्पष्ट रूप से यह स्वतंत्र रूप से इस्तेमाल किया जा रहा से अलग, प्रवाह को नियंत्रित करने मैक्रो का समूह काम से निपटने के लिए बनाया गया था।

int func() 
{ 
    char* p1 = NULL; 
    char* p2 = NULL; 
    char* p3 = NULL; 

    FAIL_SECTION_BEGIN 
    { 
     p1 = malloc(16); 
     FAIL_SECTION_DO_EXIT_IF(!p1, -1); 

     p2 = malloc(16); 
     FAIL_SECTION_DO_EXIT_IF(!p2, -1); 

     p3 = malloc(16); 
     FAIL_SECTION_DO_EXIT_IF(!p3, -1); 
    } 
    FAIL_SECTION_ERROR_EXIT(code) 
    { 
     if(p3) 
     free(p3); 

     if(p2) 
     free(p2); 

     if(p1) 
     free(p1); 

     return code; 
    } 
    FAIL_SECTION_END 

    return 0; 

यह अच्छा लग रहा है, और बहुत से लाभ के साथ आता है, लेकिन, वहाँ किसी भी कमियां हम के बारे में सोच किया जाना चाहिए रहे हैं:

#define FAIL_SECTION_BEGIN int exit_code[GUID] = 0; 
#define FAIL_SECTION_DO_EXIT_IF(cond, exitcode) if(cond){exit_code[GUID] = exitcode; goto exit_label[GUID];} 
#define FAIL_SECTION_ERROR_EXIT(code) exit_label[GUID]: if(exit_code[GUID]) int code = exit_code[GUID];else goto end_label[GUID] 
#define FAIL_SECTION_END end_label[GUID]: 

हम इस प्रकार के रूप में उपयोग कर सकते हैं: कुछ इस तरह (सरलीकृत) लग रहा है इसे विकास में लाने से पहले? यह सभी प्रवाह नियंत्रण के बाद है और goto: ish। दोनों निराश हैं। इस मामले में उन्हें निराश करने के लिए तर्क क्या हैं?

धन्यवाद।

+0

मार्टिन फिडो अपने जवाब में बताते हैं, अंतिम कोड स्निपेट में पॉइंटर्स पी 1, पी 2 और पी 3 त्रुटि-हैंडलिंग सेक्शन में से बाहर हैं, और पहले कोड स्निपेट पी 2 में कचरा डेटा होगा (यदि यह संकलित करता है बिल्कुल - सी नियमों के बारे में निश्चित नहीं है)। –

+0

@j: मुझे यकीन है कि यह कोड संकलक के माध्यम से भी नहीं जाएगा। टिप के लिए धन्यवाद, लेकिन आप सवाल के पूरे बिंदु को याद किया होगा। – sharkin

+0

@ आरएए: आपको क्या लगता है कि मैंने इस बिंदु को याद किया? गैर-compilability एक भ्रमित पक्ष मुद्दा था कि मैंने सोचा था कि एक टिप्पणी में इंगित करने लायक था। –

उत्तर

11

त्रुटि निवारण दुर्लभ स्थितियों में से एक है जब goto है इतना बुरा नहीं।

लेकिन अगर मुझे लगता है कि कोड मैं बहुत परेशान है कि goto मैक्रो द्वारा छिपे हुए हैं होगा बनाए रखने के लिए किया था।

तो इस मामले में goto मेरे लिए नहीं है, लेकिन मैक्रो ठीक है।

+0

त्रुटि प्रबंधन एक दुर्लभ स्थिति नहीं होनी चाहिए: पी – quinmars

+0

दुर्लभ स्थिति "त्रुटि प्रबंधन" नहीं है, यह "गोटो उपयोग" है। – mouviciel

+0

मुझे मैक्रो के भीतर गोटो का उपयोग करने में कोई फर्क नहीं पड़ता है और समय-समय पर ऐसा करता है, लेकिन मैक्रो के अंदर कूदने वाले लेबल को छिपाना एक पूर्ण संख्या नहीं है। अर्थात। ठीक नहीं है: "# परिभाषित करें CHECK_RESULT (res) अगर (res == 0) goto cleanup;", ठीक है: "# CHECK_RESULT (res, लेबल) परिभाषित करें यदि (res == 0) goto लेबल;"। इस तरह आप समझ सकते हैं कि मैक्रो कूदता है (उदा। "Res = something(); CHECK_RESULT (res, cleanup); ... सफाई: ...")। – hlovdal

7

एक आम त्रुटि हैंडलर/सफाई/बाहर निकलें अनुक्रम में जाने के लिए goto का प्रयोग पूरी तरह से ठीक है।

+0

हालांकि मैक्रोज़ छोड़ दूंगा। आपकी प्रोग्रामर आंखों को() एस के लिए प्रशिक्षित किया जाता है। वे मैक्रोज़ तुलनात्मक रूप से शायद ही कभी दिखाई देते हैं, और इसलिए पढ़ने के लिए बहुत कठिन हैं। –

7

इस कोड:

void func() 
{ 
    char* p1 = malloc(16); 
    if(!p1) 
     goto cleanup; 

    char* p2 = malloc(16); 
    if(!p2) 
     goto cleanup; 

cleanup: 

    if(p1) 
     free(p1); 

    if(p2) 
     free(p2); 
} 

कानूनी तौर पर के रूप में लिखा जा सकता है:

void func() 
{ 
    char* p1 = malloc(16); 
    char* p2 = malloc(16); 

    free(p1); 
    free(p2); 
} 

या नहीं, स्मृति आवंटन सफल होते हैं।

यह काम करता है क्योंकि मुफ्त() एक पूर्ण सूचक पारित होने पर कुछ भी नहीं करता है।

इस तरह
// return handle to new Foo resource, or 0 if allocation failed 
FOO_HANDLE AllocFoo(); 

// release Foo indicated by handle, - do nothing if handle is 0 
void ReleaseFoo(FOO_HANDLE h); 

डिजाइनिंग एपीआई काफी संसाधन प्रबंधन को आसान बनाने में कर सकते हैं: जब अपने स्वयं के एपीआई आवंटित करने के लिए और अन्य संसाधनों को डिजाइन आप एक ही मुहावरा उपयोग कर सकते हैं।

+0

हाँ, क्षमा करें, मैं कुछ कोड भूल गया। सवाल अपडेट किया गया। – sharkin

+0

हालांकि यह मॉलोक विशिष्ट है, अन्य संसाधन आवंटन कार्यों को कॉल करने के बारे में क्या? हालांकि मुझे यह नहीं पता था, धन्यवाद – shodanex

+0

गलत, वह कोड सी 99 विशिष्ट है। – sharkin

1

पहला उदाहरण मैक्रोनाइज्ड संस्करण की तुलना में मेरे लिए अधिक पठनीय दिखता है। और mouviciel यह बहुत बेहतर की तुलना में मैं

2

किया कहा शब्द "संरचित प्रोग्रामिंग" जो हम सभी विरोधी गोटो बात मूल रूप से शुरू कर दिया और गोटो (या जेएमपी के) के साथ पैटर्न कोडिंग का एक समूह के रूप में विकसित रूप में जानते हैं। उन पैटर्न को while और if पैटर्न कहा गया था।

इसलिए, यदि आप गोटो के प्रयोग कर रहे हैं, उन्हें एक संरचित तरीके में उपयोग करें। इससे नुकसान सीमित हो जाता है। और उन मैक्रो का एक उचित दृष्टिकोण लगते हैं।

3

goto के साथ सफाई एक सामान्य सी idiom है और used in Linux kernel * है।

** शायद लीनुस की राय एक अच्छा तर्क का सबसे अच्छा उदाहरण नहीं है, लेकिन यह goto एक अपेक्षाकृत बड़े पैमाने परियोजना में इस्तेमाल किया जा रहा दिखाने करता है। *

3

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

अपने पिछले उदाहरण में चर ब्रेसिज़ के अंदर scoped (यानी वे केवल FAIL_SECTION_BEGIN ब्लॉक में मौजूद हैं)।

कोड मान लिया जाये कि ब्रेसिज़ आप अभी भी FAIL_SECTION_BEGIN से पहले शून्य करने के लिए सभी संकेत आरंभ करने के लिए SEG दोषयुक्त से बचने के लिए होगा बिना काम करता है।

मैं गोटो और मैक्रो के खिलाफ कुछ भी नहीं है, लेकिन मैं नील बटरवर्थ का विचार पसंद करते हैं ..

void func(void) 
{ 
    void *p1 = malloc(16); 
    void *p2 = malloc(16); 
    void *p3 = malloc(16); 

    if (!p1 || !p2 || !p3) goto cleanup; 

    /* ... */ 

cleanup: 
    if (p1) free(p1); 
    if (p2) free(p2); 
    if (p3) free(p3); 
} 

या अगर यह अधिक उपयुक्त है ..

void func(void) 
{ 
    void *p1 = NULL; 
    void *p2 = NULL; 
    void *p3 = NULL; 

    p1 = malloc(16); 
    if (!p1) goto cleanup; 

    p2 = malloc(16); 
    if (!p2) goto cleanup; 

    p3 = malloc(16); 
    if (!p3) goto cleanup; 

    /* ... */ 

cleanup: 
    if (p1) free(p1); 
    if (p2) free(p2); 
    if (p3) free(p3); 
} 
+0

+1। क्लीनअप सेक्शन में "अभी तक मौजूद नहीं है" चर के साथ खेलने के बारे में अच्छा बिंदु। –

+0

स्वाभाविक रूप से यह एक टाइपो है, और स्पष्ट रूप से मैंने इसे संकलित करने की कोशिश नहीं की है (पॉइंटर्स निकास खंड में अनुपयोगी होंगे)। जाहिर है कि इस तरह के विवरण वास्तव में वास्तव में सवाल का सार नहीं हैं। – sharkin

+0

@ आरएए: नहीं, वे सार नहीं हैं, लेकिन वे उस बिंदु को खराब कर देते हैं जिसे आप बनाने की कोशिश कर रहे हैं।जब आप कोड स्निपेट पोस्ट करते हैं, तो यह सुनिश्चित करने के लिए सरल सौजन्य है कि वे संकलित और उद्देश्य के अनुसार काम करते हैं। –

2

मूल कोड को कई वापसी बयानों का उपयोग करने से लाभ होगा - एरर रिटर्न साफ़ कोड के आसपास हॉप करने की कोई ज़रूरत नहीं है। इसके अलावा, आपको सामान्य रूप से सामान्य रिटर्न पर जारी आवंटित स्थान की भी आवश्यकता होती है - अन्यथा आप स्मृति लीक कर रहे हैं। और यदि आप सावधान हैं तो आप goto के बिना उदाहरण को फिर से लिख सकते हैं। यह ऐसा मामला है जहाँ आप उपयोगी अन्यथा आवश्यक पहले चर घोषणा कर सकते हैं है: प्रत्येक आवंटन ऑपरेशन के बाद काम की

void func() 
{ 
    char *p1 = 0; 
    char *p2 = 0; 
    char *p3 = 0; 

    if ((p1 = malloc(16)) != 0 && 
     (p2 = malloc(16)) != 0 && 
     (p3 = malloc(16)) != 0) 
    { 
     // Use p1, p2, p3 ... 
    } 
    free(p1); 
    free(p2); 
    free(p3); 
} 

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

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

चित्रण - नाम दोषी की रक्षा के लिए प्रच्छन्न:

#define rerrcheck if (currval != &localval && globvar->currtub &&   \ 
        globvar->currtub->te_flags & TE_ABORT)     \ 
        { if (globvar->currtub->te_state)      \ 
         globvar->currtub->te_state->ts_flags |= TS_FAILED;\ 
         else             \ 
         delete_tub_name(globvar->currtub->te_name);  \ 
         goto failure;          \ 
        } 


#define rgetunsigned(b) {if (_iincnt>=2) \ 
          {_iinptr+=2;_iincnt-=2;b = ldunsigned(_iinptr-2);} \ 
         else {b = _igetunsigned(); rerrcheck}} 

कि कुछ इसी तरह कर रहे हैं rgetunsigned() पर कई दर्जन वेरिएंट हैं - विभिन्न आकारों और विभिन्न लोडर काम करता है।

 for (i = 0 ; i < no_of_rows; i++) 
      { 
      row_t *tmprow = &val->v_coll.cl_typeinfo->clt_rows[i]; 

      rgetint(tmprow->seqno); 
      rgetint(tmprow->level_no); 
      rgetint(tmprow->parent_no); 
      rgetint(tmprow->fieldnmlen); 
      rgetpbuf(tmprow->fieldname, IDENTSIZE); 
      rgetint(tmprow->field_no); 
      rgetint(tmprow->type); 
      rgetint(tmprow->length); 
      rgetlong(tmprow->xid); 
      rgetint(tmprow->flags); 
      rgetint(tmprow->xtype_nm_len); 
      rgetpbuf(tmprow->xtype_name, IDENTSIZE); 
      rgetint(tmprow->xtype_owner_len); 
      rgetpbuf(tmprow->xtype_owner_name, IDENTSIZE); 
      rgetpbuf(tmprow->xtype_owner_name, 
        tmprow->xtype_owner_len); 
      rgetint(tmprow->alignment); 
      rgetlong(tmprow->sourcetype); 
      } 
: कोड का एक बड़ा ब्लॉक में कुछ छोटे और कोड के कुछ बड़े ब्लॉकों (विशेष रूप से अच्छी तरह से संरचित नहीं) के साथ एक बड़े परिवर्तन की एक भी मामले में -

एक जगह है जहाँ इन उपयोग किया जाता है इस पाश शामिल

यह स्पष्ट नहीं है कि कोड गेटो स्टेटमेंट के साथ लगी हुई है! और स्पष्ट रूप से, कोड के पापों का पूर्ण निष्कर्ष पूरे दिन ले जाएगा - वे कई और विविध हैं।

0
#define malloc_or_die(size) if(malloc(size) == NULL) exit(1) 

यह क्या तुम सच में, विफल रहा है malloc से ठीक हो सकता है, जब तक आप, के लिए एक सौदे लेखन प्रणाली यदि आप ऐसा लायक सॉफ्टवेयर malloc_or_die को रोल बैक कोड जोड़ने की तरह नहीं है।

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

+0

मेरी इच्छा है कि आप "गणना गोटो" के बारे में कुछ और जानकारी के लिए एक लिंक पोस्ट कर सकते हैं। दिलचस्प लगता है। -1 स्मृति आवंटन विफलता के बारे में आपकी राय के लिए। – sharkin

+0

pulseaudio रखरखाव से: "आधुनिक प्रणालियों पर यह बहुत स्पष्ट हो गया है कि सामान्य प्रयोक्ता स्पेस सॉफ्टवेयर के लिए केवल एक निरस्त मॉलोक() समझ में आता है [...]" http://article.gmane.org/gmane.comp.audio। jackit/19998 – CesarB