मान्य, लेकिन स्विच-केस में बेकार सिंटैक्स?


207

थोड़ा टाइपो के माध्यम से, मैंने गलती से यह निर्माण पाया:

int main(void) {
    char foo = 'c';

    switch(foo)
    {
        printf("Cant Touch This\n");   // This line is Unreachable

        case 'a': printf("A\n"); break;
        case 'b': printf("B\n"); break;
        case 'c': printf("C\n"); break;
        case 'd': printf("D\n"); break;
    }

    return 0;
}

ऐसा लगता है कि बयान printfके शीर्ष पर switchमान्य है, लेकिन यह भी पूरी तरह से पहुंच से बाहर है।

मुझे एक साफ संकलन मिला, जो अनुपलब्ध कोड के बारे में चेतावनी के बिना भी था, लेकिन यह व्यर्थ लगता है।

क्या एक संकलक को इसे अनुपलब्ध कोड के रूप में चिह्नित करना चाहिए?
क्या इससे किसी उद्देश्य की पूर्ति होती है?


51
इसके लिए जीसीसी का एक विशेष झंडा है। इट-Wswitch-unreachable
एली सदॉफ

57
"क्या यह किसी भी उद्देश्य की पूर्ति करता है?" ठीक है, आप gotoअन्यथा अप्राप्य भाग से अंदर और बाहर कर सकते हैं , जो विभिन्न हैक के लिए उपयोगी हो सकता है।
होलीब्लैककैट

12
@HolyBlackCat ऐसा नहीं होगा जो सभी पहुंच से बाहर कोड के लिए हो?
एली सदॉफ

28
@EliSadoff वास्तव में। मुझे लगता है कि यह किसी विशेष उद्देश्य की सेवा नहीं करता है । मैं शर्त लगाता हूं कि इसकी अनुमति सिर्फ इसलिए है क्योंकि इसके लिए कोई मनाही नहीं है। सब के बाद, कई लेबल के साथ switchसिर्फ एक सशर्त है goto। यह शरीर पर कम या ज्यादा समान प्रतिबंध हैं जैसा कि आपको गोटो लेबल से भरे कोड के एक नियमित ब्लॉक पर होगा।
होलीब्लैकैट

16
इस बात की ओर इशारा करते हुए कि @MooingDuck का उदाहरण डफ के उपकरण पर एक प्रकार है ( en.wikipedia.org/wiki/Duff's_device )
माइकल एंडरसन

जवाबों:


226

शायद सबसे उपयोगी नहीं, लेकिन पूरी तरह से बेकार नहीं । आप इसका उपयोग switchस्कोप के भीतर उपलब्ध एक स्थानीय वैरिएबल को घोषित करने के लिए कर सकते हैं ।

switch (foo)
{
    int i;
case 0:
    i = 0;
    //....
case 1:
    i = 1;
    //....
}

मानक ( N1579 6.8.4.2/7) में निम्नलिखित नमूना है:

उदाहरण कृत्रिम कार्यक्रम के टुकड़े में

switch (expr)
{
    int i = 4;
    f(i);
case 0:
    i = 17;
    /* falls through into default code */
default:
    printf("%d\n", i);
}

वह ऑब्जेक्ट जिसका पहचानकर्ता iस्वत: संग्रहण अवधि (ब्लॉक के भीतर) के साथ मौजूद होता है, लेकिन इसे कभी भी प्रारंभ नहीं किया जाता है, और इस प्रकार यदि कंट्रोलिंग एक्सप्रेशन का एक गैर-मान है, तो printfफ़ंक्शन को कॉल एक अनिश्चित मान तक पहुंच जाएगा। इसी तरह, फ़ंक्शन को कॉल fतक नहीं पहुँचा जा सकता है।

PS BTW, नमूना मान्य C ++ कोड नहीं है। उस मामले में ( N4140 6.7/3, मेरा जोर):

एक प्रोग्राम जो 90 से एक ऐसे बिंदु पर कूदता है जहां एक स्वचालित भंडारण अवधि वाला चर उस बिंदु के दायरे में नहीं होता है, जहां वह स्कोप प्रकार का होता है , जब तक कि चर स्केलर प्रकार , वर्ग प्रकार के साथ एक तुच्छ डिफ़ॉल्ट निर्माता और एक तुच्छ डिस्ट्रक्टर नहीं होता है इन प्रकारों में से एक का एक cv- योग्य संस्करण, या पूर्ववर्ती प्रकारों में से एक का एक सरणी और एक इनिशलाइज़र (8.5) के बिना घोषित किया गया है


90) switchएक केस लेबल के लिए एक बयान की स्थिति से स्थानांतरण इस संबंध में एक कूद माना जाता है।

इसलिए इसके int i = 4;साथ प्रतिस्थापित करने int i;से यह वैध C ++ बन जाता है।


3
"... लेकिन कभी भी इनिशियलाइज़ नहीं किया जाता ..." लगता है जैसे i4 से इनिशियलाइज़ किया गया है, मुझे क्या याद आ रहा है?
यानो १

7
ध्यान दें कि यदि चर है static, तो इसे शून्य पर आरंभीकृत किया जाएगा, इसलिए इसके लिए एक सुरक्षित उपयोग भी है।
लेउशेंको

23
@ ओनो हम हमेशा i = 4;इनिशियलाइजेशन पर कूदते हैं , इसलिए यह कभी नहीं होता है।
एलेक्स डीड

12
बेशक! ... सवाल का पूरा बिंदु ... गीज़। इच्छा इस मूर्खता को हटाने के लिए मजबूत है
yano

1
अच्छा! कभी-कभी मुझे एक के अंदर एक अस्थायी चर की आवश्यकता होती है case, और हमेशा caseस्विच में प्रत्येक में अलग-अलग नामों का उपयोग करना पड़ता है या इसे परिभाषित करना पड़ता है।
SJuan76

98

क्या इससे किसी उद्देश्य की पूर्ति होती है?

हाँ। यदि एक बयान के बजाय, आप पहले लेबल से पहले एक घोषणा करते हैं, तो यह सही समझ में आ सकता है:

switch (a) {
  int i;
case 0:
  i = f(); g(); h(i);
  break;
case 1:
  i = g(); f(); h(i);
  break;
}

घोषणाओं और बयानों के नियमों को सामान्य रूप से ब्लॉकों के लिए साझा किया जाता है, इसलिए यह वही नियम है जो अनुमति देता है कि यह भी बयानों की अनुमति देता है।


साथ ही वर्थ का उल्लेख यह भी है कि यदि पहला स्टेटमेंट लूप कंस्ट्रक्शन है, तो केस लेबल लूप बॉडी में दिखाई दे सकते हैं:

switch (i) {
  for (;;) {
    f();
  case 1:
    g();
  case 2:
    if (h()) break;
  }
}

कृपया इस तरह कोड न लिखें यदि इसे लिखने का अधिक पठनीय तरीका है, लेकिन यह पूरी तरह से वैध है, और f()कॉल उपलब्ध नहीं है।


@ मैथ्यूएमएम डफ डिवाइस में लूप के अंदर केस लेबल होते हैं, लेकिन लूप से पहले केस लेबल से शुरू होता है।

2
मुझे यकीन नहीं है कि अगर मुझे एक वास्तविक कार्यक्रम में इसे लिखने की पूरी पागलपन के लिए दिलचस्प उदाहरण या डाउनवोट के लिए upvote करना चाहिए :)। रसातल में गोता लगाने और एक टुकड़े में वापस लौटने के लिए बधाई।
लिवियु टी।

@ केमिकल एंजाइनर: यदि कोड लूप का हिस्सा है, जैसा कि डफ डिवाइस में है, तो { /*code*/ switch(x) { } }क्लीनर लग सकता है लेकिन यह भी गलत है
बेन वोइगट

39

इसका एक प्रसिद्ध उपयोग है जिसे डफ डिवाइस कहा जाता है ।

int n = (count+3)/4;
switch (count % 4) {
  do {
    case 0: *to = *from++;
    case 3: *to = *from++;
    case 2: *to = *from++;
    case 1: *to = *from++;
  } while (--n > 0);
}

यहाँ हम एक ऐसे बफर की ओर इशारा करते हैं, जिसके द्वारा इंगित किए fromगए एक बफर को इंगित किया गया है to। हम countडेटा के उदाहरणों की नकल करते हैं।

do{}while()बयान से पहले शुरू होता है caseलेबल, और caseलेबल के भीतर एम्बेडेड रहे हैं do{}while()

यह do{}while()लूप के अंत में सशर्त शाखाओं की संख्या को कम कर देता है जो लगभग 4 के कारक के रूप में सामने आता है (इस उदाहरण में, स्थिरांक को आप जो भी मूल्य चाहते हैं, उसे ट्वीक किया जा सकता है)।

अब, ऑप्टिमाइज़र कभी-कभी आपके लिए ऐसा कर सकते हैं (विशेषकर यदि वे स्ट्रीमिंग / वेक्टर किए गए निर्देशों का अनुकूलन कर रहे हैं), लेकिन प्रोफ़ाइल निर्देशित अनुकूलन के बिना वे नहीं जान सकते कि आप लूप के बड़े होने की उम्मीद करते हैं या नहीं।

सामान्य तौर पर, चर घोषणाएं वहां हो सकती हैं और हर मामले में उपयोग की जा सकती हैं, लेकिन स्विच समाप्त होने के बाद दायरे से बाहर रहें। (ध्यान दें कि किसी भी आरंभीकरण को छोड़ दिया जाएगा)

इसके अलावा, नियंत्रण प्रवाह जो स्विच-विशिष्ट नहीं है, आपको स्विच ब्लॉक के उस हिस्से में प्राप्त कर सकता है, जैसा कि ऊपर सचित्र है, या ए के साथ goto


2
बेशक, यह तब भी संभव होगा जब पहले मामले से ऊपर के बयानों की अनुमति के बिना, कोई बात नहीं do {और आदेश के रूप में case 0:, दोनों पहले पर एक कूद लक्ष्य रखने के लिए सेवा करते हैं *to = *from++;
बेन वोइग्ट

1
@BenVoigt मैं तर्क दूंगा कि do {यह अधिक पठनीय है। हां, डफ डिवाइस के लिए पठनीयता के बारे में बहस करना बेवकूफी और व्यर्थ है और संभवतः पागल होने का एक सरल तरीका है।
निधि मोनिका का मुकदमा

1
@QPaysTaxes आपको C में साइमन टाथम के कॉरआउट्स की जांच करनी चाहिए । या शायद नहीं।
जोनास श्फर

@ JonasSchäfer आम तौर पर, मूल रूप से C ++ 20 कोरआउट आपके लिए क्या करने जा रहे हैं।
यक - एडम नेवरामॉन्ट

15

यदि आप लिनक्स पर gcc का उपयोग कर रहे हैं, तो मान लें कि यदि आप 4.4 या पूर्व संस्करण का उपयोग कर रहे हैं, तो यह आपको चेतावनी दे सकता है।

-Wreareachable- कोड विकल्प gcc 4.4 आगे की ओर हटा दिया गया था


पहले हाथ का अनुभव होने से हमेशा मदद मिलती है!
१६

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

@ मैथ्यूएमएम: ऐसा प्रतीत होता है कि इस तरह की चेतावनी शब्दार्थ विश्लेषण के बजाय व्याकरण से जुड़े मामलों में पता लगाने के लिए बेहद आसान होगी [जैसे कोड एक "अगर" जिसका दोनों शाखाओं में रिटर्न है], और इसे "झुंझलाहट" को आसान बनाने के लिए आसान है। चेतावनी। दूसरी ओर, कुछ मामले ऐसे होते हैं जहाँ रिलीज़ में त्रुटियों या चेतावनी का होना उपयोगी होता है, जो डिबग बिल्ड में नहीं होते हैं (यदि और कुछ नहीं, तो उन जगहों पर जहां डिबगिंग के लिए कोई हैक लगाया जाता है) जारी)।
सुपरकैट

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

1
... यह जानने के लिए कि कुछ कॉन्फ़िगरेशन दूसरों की तुलना में बड़ा कोड क्यों उत्पन्न करते हैं [जैसे कि एक कंपाइलर एक कॉन्फ़िगरेशन में कुछ स्थिति को असंभव मान सकता है, लेकिन दूसरा नहीं] लेकिन इसका मतलब यह नहीं है कि कोड के साथ "गलत" कुछ भी है जिसे अनुकूलित किया जा सकता है कुछ विन्यास के साथ कि फैशन।
१५:०५ पर सुपरकैट

11

न केवल चर घोषणा के लिए बल्कि उन्नत कूद के लिए भी। आप इसे अच्छी तरह से उपयोग कर सकते हैं अगर और केवल अगर आप स्पेगेटी कोड के लिए प्रवण नहीं हैं।

int main()
{
    int i = 1;
    switch(i)
    {
        nocase:
        printf("no case\n");

        case 0: printf("0\n"); break;
        case 1: printf("1\n"); goto nocase;
    }
    return 0;
}

प्रिंटों

1
no case
0 /* Notice how "0" prints even though i = 1 */

यह ध्यान दिया जाना चाहिए कि स्विच-केस सबसे तेज नियंत्रण प्रवाह खंडों में से एक है। तो यह प्रोग्रामर के लिए बहुत लचीला होना चाहिए, जिसमें कभी-कभी इस तरह के मामले शामिल होते हैं।


और के बीच अंतर क्या है nocase:और default:?
४'६

@ i486 जब i=4यह ट्रिगर नहीं करता है nocase
सांचके दलोदर

@SanchkeDellowar यही मेरा मतलब है।
njzk2

क्यों बिल्ली एक ऐसा करेगी जो केवल केस 0 से पहले केस 1 डालने की जगह और फालतू का इस्तेमाल कर रही हो?
जोनास श्फर

@JonasWielicki इस उद्देश्य में, आप ऐसा कर सकते हैं। लेकिन यह कोड सिर्फ एक प्रदर्शन का मामला है जो किया जा सकता है।
सांचे देलारर २३'१ellow

11

यह ध्यान दिया जाना चाहिए, कि switchकथन के भीतर या जहां case *:कोड इस कोड के भीतर लेबल रखे गए हैं , वस्तुतः कोई संरचनात्मक प्रतिबंध नहीं है । यह डफ के उपकरण की तरह प्रोग्रामिंग चालें संभव बनाता है, जिनमें से एक संभव कार्यान्वयन इस तरह दिखता है:

int n = ...;
int iterations = n/8;
switch(n%8) {
    while(iterations--) {
        sum += *ptr++;
        case 7: sum += *ptr++;
        case 6: sum += *ptr++;
        case 5: sum += *ptr++;
        case 4: sum += *ptr++;
        case 3: sum += *ptr++;
        case 2: sum += *ptr++;
        case 1: sum += *ptr++;
        case 0: ;
    }
}

तुम देखो, switch(n%8) {और case 7:लेबल के बीच कोड निश्चित रूप से उपलब्ध है ...


* जैसा कि सुपरकैट ने एक टिप्पणी में धन्यवाद दिया है : C99 के बाद से, न तो एक gotoलेबल ( जैसा कि यह एक case *:लेबल है या नहीं) एक घोषणा के दायरे में दिखाई दे सकता है जिसमें वीएलए घोषणा शामिल है। इसलिए यह कहना सही नहीं है कि लेबल की नियुक्ति पर कोई संरचनात्मक प्रतिबंध नहींcase *: हैं। हालांकि, डफ का उपकरण C99 मानक से पहले का है, और यह वैसे भी VLA पर निर्भर नहीं करता है। फिर भी, मैंने इसके कारण अपने पहले वाक्य में "वस्तुतः" सम्मिलित करने के लिए मजबूर महसूस किया।


चर-लंबाई सरणियों के अलावा ने उनसे संबंधित संरचनात्मक प्रतिबंधों को लागू किया।
सुपरकैट

@supercat किस तरह के प्रतिबंध?
सेंटास्टर -

1
न तो एक gotoऔर न ही switch/case/defaultलेबल किसी भी variably-घोषित वस्तु या प्रकार के दायरे के भीतर दिखाई दे सकते हैं। इसका प्रभावी रूप से मतलब है कि यदि किसी ब्लॉक में चर-लंबाई-सरणी ऑब्जेक्ट या प्रकार की कोई भी घोषणाएँ हैं, तो किसी भी लेबल को उन घोषणाओं से पहले होना चाहिए। मानक में वर्बेज का एक भ्रामक बिट है जो यह सुझाव देगा कि कुछ मामलों में एक वीएलए घोषणा की गुंजाइश एक स्विच स्टेटमेंट की संपूर्णता तक फैली हुई है; उस पर मेरे प्रश्न के लिए stackoverflow.com/questions/41752072/… देखें ।
सुपरकैट

@supercat: आपने सिर्फ उस क्रिया को गलत समझा (जो मैं मानता हूं कि आपने अपना प्रश्न हटा दिया है)। यह उस दायरे पर एक आवश्यकता को लागू करता है जिसमें एक वीएलए को परिभाषित किया जा सकता है। यह उस दायरे का विस्तार नहीं करता है, यह सिर्फ कुछ वीएलए परिभाषाओं को अमान्य बनाता है।
कीथ थॉम्पसन

@KeithThompson: हाँ, मैंने इसे गलत समझा था। फुटनोट में वर्तमान काल के अजीब प्रयोग ने चीजों को भ्रमित कर दिया, और मुझे लगता है कि अवधारणा को निषेध के रूप में बेहतर रूप से व्यक्त किया जा सकता था: "एक स्विच स्टेटमेंट जिसके शरीर में वीएलए घोषणा होती है, उसमें कोई स्विच या केस लेबल शामिल नहीं होगा। उस वीएलए घोषणा की गुंजाइश "।
सुपरकैट

10

आपको चेतावनी उत्पन्न करने के लिए आवश्यक gccविकल्प -Wswitch-unreachable से संबंधित अपना उत्तर मिल गया है , यह उत्तर प्रयोज्यता / योग्यता वाले हिस्से पर विस्तार से बताना है

C11अध्याय .26.8.4.2, ( जोर मेरा ) से सीधे उद्धृत करते हुए

switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17;
/* falls through into default code */
default:
printf("%d\n", i);
}

वह ऑब्जेक्ट जिसका पहचानकर्ता iस्वत: संग्रहण अवधि (ब्लॉक के भीतर) के साथ मौजूद होता है , लेकिन इसे कभी भी प्रारंभ नहीं किया जाता है , और इस प्रकार यदि कंट्रोलिंग एक्सप्रेशन में एक नॉनज़ेरो वैल्यू है, तो printf फ़ंक्शन को कॉल एक अनिश्चित मूल्य तक पहुंच जाएगा। इसी तरह, फ़ंक्शन को कॉल fतक नहीं पहुँचा जा सकता है।

जो बहुत ही आत्म-व्याख्यात्मक है। आप इसका उपयोग स्थानीय रूप से स्कोप्ड वैरिएबल को परिभाषित करने के लिए कर सकते हैं जो केवल switchस्टेटमेंट स्कोप के भीतर उपलब्ध है।


9

इसके साथ एक "लूप और एक आधा" को लागू करना संभव है, हालांकि यह इसे करने का सबसे अच्छा तरीका नहीं हो सकता है:

char password[100];
switch(0) do
{
  printf("Invalid password, try again.\n");
default:
  read_password(password, sizeof(password));
} while (!is_valid_password(password));

@ रीचार्डी यह एक दंड है या क्या? कृपया समझाएँ।
डांसिया

1
@ डैंसिया वह कह रही है कि यह स्पष्ट रूप से इस तरह से कुछ करने का सबसे अच्छा तरीका नहीं है, और "नहीं हो सकता है" एक समझ का कुछ है।
निधि मोनिका का मुकदमा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.