ब्रेक की आवश्यकता के लिए स्विच स्टेटमेंट को क्यों बनाया गया था?


139

एक साधारण स्विच स्टेटमेंट दिया

switch (int)
{
    case 1 :
    {
        printf("1\n");
        break;
    }

    case 2 : 
    {
        printf("2\n");
    }

    case 3 : 
    {
        printf("3\n");
    }
}

मामले 2 में एक ब्रेक स्टेटमेंट की अनुपस्थिति का तात्पर्य है कि मामले के लिए कोड के अंदर निष्पादन जारी रहेगा 3. यह एक दुर्घटना नहीं है; यह इस तरह से डिजाइन किया गया था। यह निर्णय क्यों किया गया? ब्लॉक के लिए एक स्वचालित ब्रेक अर्थ होने बनाम क्या लाभ प्रदान करता है? औचित्य क्या था?

जवाबों:


150

कई उत्तर कथन की आवश्यकता के कारण गिरने की क्षमता पर ध्यान केंद्रित करने लगते हैं break

मेरा मानना ​​है कि यह केवल एक गलती थी, बड़े पैमाने पर क्योंकि जब सी डिजाइन किया गया था तो लगभग अनुभव नहीं था कि इन निर्माणों का उपयोग कैसे किया जाएगा।

पीटर वान डेर लिंडेन अपनी पुस्तक "विशेषज्ञ सी प्रोग्रामिंग" में इस मामले को बनाते हैं:

हमने Sun C कंपाइलर स्रोतों का विश्लेषण करके देखा कि कितनी बार डिफ़ॉल्ट गिरावट का उपयोग किया गया था। सन एएनएसआई सी कंपाइलर फ्रंट एंड में 244 स्विच स्टेटमेंट हैं, जिनमें से प्रत्येक में औसतन सात केस हैं। इन सभी मामलों में से केवल 3% में होता है।

दूसरे शब्दों में, 97% समय में सामान्य स्विच व्यवहार गलत है । यह सिर्फ एक संकलक में नहीं है - इसके विपरीत, जहां के माध्यम से गिरना इस विश्लेषण में इस्तेमाल किया गया था यह अक्सर उन परिस्थितियों के लिए होता था जो अन्य सॉफ़्टवेयर की तुलना में एक कंपाइलर में अधिक बार होते हैं, उदाहरण के लिए, जब संचालक संचालक जो एक या दो ऑपरेटर हो सकते हैं। :

switch (operator->num_of_operands) {
    case 2: process_operand( operator->operand_2);
              /* FALLTHRU */

    case 1: process_operand( operator->operand_1);
    break;
}

केस फ़ॉल के माध्यम से इतनी व्यापक रूप से एक दोष के रूप में पहचाना जाता है कि एक विशेष टिप्पणी सम्मेलन भी है, जो ऊपर दिखाया गया है, जो बताता है कि "यह वास्तव में उन 3% मामलों में से एक है जहां से गुजरना वांछित था।"

मुझे लगता है कि प्रत्येक केस ब्लॉक के अंत में एक स्पष्ट कूद स्टेटमेंट की आवश्यकता के लिए C # के लिए यह एक अच्छा विचार था (जबकि अभी भी कई केस लेबल को स्टैक करने की अनुमति है - जब तक कि बयानों का केवल एक ही ब्लॉक है)। C # में आपके पास अभी भी एक मामला दूसरे के माध्यम से गिर सकता है - आपको बस एक का उपयोग करके अगले मामले में कूदकर गिरावट को स्पष्ट करना होगा goto

यह बहुत बुरा है जावा ने सी शब्दार्थ से तोड़ने का अवसर नहीं लिया।


वास्तव में, मुझे लगता है कि वे कार्यान्वयन की सादगी के लिए गए थे। कुछ अन्य भाषाओं ने लागत पर, शायद, दक्षता के लिए अधिक परिष्कृत मामलों (श्रेणियों, कई मूल्यों, तार ...) का समर्थन किया।
PhiLho

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

@PhiLho - मुझे लगता है कि आप "कार्यान्वयन की सादगी" के साथ शायद सच्चाई के सबसे करीब हैं।
माइकल बूर

यदि आप GOTO के माध्यम से स्पष्ट कूद का उपयोग कर रहे हैं, तो क्या यह केवल IF बयानों की एक श्रृंखला का उपयोग करने के लिए उत्पादक नहीं है?
डेविन बीआर 13'09

3
यहां तक ​​कि पास्कल बिना ब्रेक के अपने स्विच को लागू करते हैं। C कंपाइलर-कोडर इसके बारे में कैसे नहीं सोच सकता @@
चान ले

30

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

तो, सी एक ही बात करता है ...


1
मुझे पता है कि बहुत से लोग ऐसा कहते हैं, लेकिन मुझे लगता है कि यह पूरी तस्वीर नहीं है; सी भी अक्सर एंटीपार्टर्न को असेंबली करने के लिए एक बदसूरत इंटरफ़ेस है। 1. बदसूरत: अभिव्यक्ति के बजाय कथन। "घोषणा उपयोग को दर्शाता है।" प्रतिरूपकता और पोर्टेबिलिटी के लिए तंत्र के रूप में महिमा कापी-पेस्ट । टाइप सिस्टम तरीका बहुत जटिल है; कीड़े को प्रोत्साहित करता हैNULL। (अगली टिप्पणी में जारी।)
हैरिसन

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

@HarrisonKlaperman: तारों को संचय करने का कोई तरीका सही नहीं है। अशक्त-टर्मिनेटेड स्ट्रिंग्स से जुड़ी समस्याओं का अधिकांश हिस्सा घटित नहीं होगा यदि रूटीन जो स्वीकृत स्ट्रिंग्स भी एक अधिकतम-लंबाई पैरामीटर को स्वीकार करता है, और रूटीन के साथ होता है जो एक बफर-आकार पैरामीटर को स्वीकार किए बिना निश्चित-आकार के बफ़र्स में लंबाई-चिह्नित स्ट्रिंग्स को संग्रहीत करता है। । स्विच स्टेटमेंट का डिज़ाइन जहां केस केवल लेबल होते हैं वे आधुनिक आंखों के लिए अजीब लग सकते हैं, लेकिन यह फोर्ट्रान DOलूप के डिज़ाइन से भी बदतर नहीं है ।
सुपरकैट

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

2
जबकि आजकल लोगों को साफ-सफाई और सेल्फ-प्रोटेक्टिंग इडोस और लैंग्वेज फीचर्स का ज्यादा इस्तेमाल किया जाता है, जो अपने आप को पैर में गोली मारने से रोकते हैं, यह एक ऐसे क्षेत्र से एक रीइंबर्सेंस है जहां बाइट महंगी हुई थीं (1970 से पहले सी शुरू हो गई थी)। यदि आपके कोड को 1024 बाइट के भीतर फिट होने की आवश्यकता है, तो आपको कोड टुकड़ों का पुन: उपयोग करने के लिए भारी दबाव का अनुभव होगा। एक ही छोर पर अलग-अलग एंट्रीपॉइंट्स को साझा करके पुन: उपयोग करना इसे प्राप्त करने के लिए एक तंत्र है।
रपी

23

डफ के उपकरण को लागू करने के लिए, स्पष्ट रूप से:

dsend(to, from, count)
char *to, *from;
int count;
{
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

3
मुझे डफ का उपकरण बहुत पसंद है। इतना सुंदर, और दुष्ट तेज।
4

7
हां, लेकिन क्या हर बार एसओ पर स्विच स्टेटमेंट दिखाना पड़ता है?
बिलजमदेव

तुम दो बंद घुंघराले ब्रेसिज़ ;-) याद किया।
तून Krijthe

18

यदि मामलों को स्पष्ट रूप से तोड़ने के लिए डिज़ाइन किया गया था, तो आप परित्याग नहीं कर सकते थे।

case 0:
case 1:
case 2:
    // all do the same thing.
    break;
case 3:
case 4:
    // do something different.
    break;
default:
    // something else entirely.

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


12
आप एक ऐसे स्विच की छवि बना सकते हैं जो अव्यवस्थित रूप से टूटता है, लेकिन इसमें "गिरावट" कीवर्ड है। अजीब, लेकिन व्यावहारिक।
dmckee --- पूर्व-मध्यस्थ बिल्ली का बच्चा

यह कैसे बेहतर होगा? मैं इस तरह से काम करने के लिए एक केस स्टेटमेंट की कल्पना करता हूं, "केस प्रति कोड ऑफ ब्लॉक" की तुलना में अधिक बार ... यह एक / अगर / फिर ब्लॉक है।
बिलजमदेव

मुझे लगता है कि इस सवाल में, प्रत्येक मामले में खंड {} संलग्नक को भ्रम में जोड़ रहा है क्योंकि यह "जबकि" कथन की शैली जैसा दिखता है।
मैथ्यू स्मिथ

1
@ बिल: यह बदतर होगा, मुझे लगता है, लेकिन यह माइक बी द्वारा लाई गई शिकायत को संबोधित करेगा: यह गिरावट (हालांकि, कई मामलों के अलावा) एक दुर्लभ घटना है और डिफ़ॉल्ट व्यवहार नहीं होना चाहिए।
dmckee --- पूर्व-मध्यस्थ बिल्ली का बच्चा

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

15

स्विच स्टेटमेंट में केस स्टेटमेंट बस लेबल होते हैं।

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

इसका मतलब है कि अगले लेबल के तहत कोड से गुजरने से बचने के लिए ब्रेक आवश्यक है।

जिस कारण से इसे इस तरह से लागू किया गया था - एक स्विच स्टेटमेंट की गिरावट-थ्रू प्रकृति कुछ परिदृश्यों में उपयोगी हो सकती है। उदाहरण के लिए:

case optionA:
    // optionA needs to do its own thing, and also B's thing.
    // Fall-through to optionB afterwards.
    // Its behaviour is a superset of B's.
case optionB:
    // optionB needs to do its own thing
    // Its behaviour is a subset of A's.
    break;
case optionC:
    // optionC is quite independent so it does its own thing.
    break;

2
दो बड़ी समस्याएं हैं: 1) breakजहां जरूरत है उसे भूल जाना । 2) यदि केस स्टेटमेंट में उनका क्रम बदल गया है, तो गलत परिणाम के कारण गलत केस चलाया जा सकता है। इस प्रकार, मुझे लगता है कि C # को बेहतर तरीके से हैंडल करना ( goto caseखाली केस लेबल को छोड़कर, गिरावट के लिए स्पष्ट है )।
डैनियल रोज

@DanielRose: 1) breakC # में भी भूलने के तरीके हैं - सबसे सरल एक वह है जब आप कुछ भी नहीं करना चाहते हैं, caseलेकिन एक जोड़ने के लिए भूल जाते हैं break(शायद आप व्याख्यात्मक टिप्पणी में लिपट गए हैं, या कुछ दूर बुलाया गया है अन्य कार्य): निष्पादन नीचे से caseनीचे गिर जाएगा । 2) प्रोत्साहित goto caseकरना असंरचित "स्पेगेटी" कोडिंग को प्रोत्साहित करना है - आप आकस्मिक चक्रों ( case A: ... goto case B; case B: ... ; goto case A;) के साथ समाप्त हो सकते हैं , खासकर जब केस फाइल में अलग हो जाते हैं और संयोजन में कठिन हो जाते हैं। सी ++ में, फॉल-थ्रू स्थानीयकृत है।
टोनी डेलरो

8

चीजों को अनुमति देने के लिए:

switch(foo) {
case 1:
    /* stuff for case 1 only */
    if (0) {
case 2:
    /* stuff for case 2 only */
    }
    /* stuff for cases 1 and 2 */
case 3:
    /* stuff for cases 1, 2, and 3 */
}

caseकीवर्ड को एक gotoलेबल के रूप में सोचें और यह स्वाभाविक रूप से बहुत अधिक आता है।


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

11
यह पूरा जवाब बुराई होने की एक कवायद थी, मुझे लगता है। :-)
आर .. गिटहब स्टॉप हेल्पिंग IC

3

यह कोड दोहराव को समाप्त करता है जब कई मामलों को एक ही कोड (या अनुक्रम में समान कोड) को निष्पादित करने की आवश्यकता होती है।

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


2

मैं वैक्टर से स्ट्रक्चर में मूल्यों को निर्दिष्ट करने के मामले में दौड़ने के लिए हुआ था: यह इस तरह से किया जाना था कि यदि डेटा वेक्टर संरचना में डेटा सदस्यों की संख्या से कम था, तो बाकी सदस्य इसमें बने रहेंगे। उनका डिफ़ॉल्ट मान। उस मामले में चूक breakकाफी उपयोगी थी।

switch (nShorts)
{
case 4: frame.leadV1    = shortArray[3];
case 3: frame.leadIII   = shortArray[2];
case 2: frame.leadII    = shortArray[1];
case 1: frame.leadI     = shortArray[0]; break;
default: TS_ASSERT(false);
}

0

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

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

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