चर को स्विच स्टेटमेंट में घोषित क्यों नहीं किया जा सकता है?


944

मैंने हमेशा यह सोचा है - स्विच स्टेटमेंट में केस लेबल के बाद आप वेरिएबल की घोषणा क्यों नहीं कर सकते? C ++ में आप चर को बहुत अधिक घोषित कर सकते हैं (और उन्हें पहले उपयोग के करीब घोषित करना स्पष्ट रूप से एक अच्छी बात है), लेकिन निम्नलिखित काम नहीं करेगा:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

उपरोक्त मुझे निम्न त्रुटि (MSC) देता है:

'newVal' के आरंभीकरण को 'केस' लेबल द्वारा छोड़ दिया गया है

यह अन्य भाषाओं में भी एक सीमा लगती है। ऐसी समस्या क्यों है?


10
C BNF व्याकरण पर आधारित स्पष्टीकरण के लिए, stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/…
johne

यहाँ सामान्य रूप से स्विच स्टेटमेंट्स और लेबल्स (ABC :) के बारे में बहुत अच्छा पढ़ा गया है।
इथरेलोन

4
मैं कहूंगा कि 'वेरिएबल को घोषित करने के बजाय स्विच स्टेटमेंट में इनिशियलाइज़ क्यों नहीं किया जा सकता है। केवल वेरिएबल की घोषणा करने से मुझे MSVC में केवल एक चेतावनी मिलती है।
ज़ूम इन

जवाबों:


1141

Caseबयान केवल लेबल हैं । इसका मतलब यह है कि संकलक इसे सीधे लेबल पर एक छलांग के रूप में व्याख्या करेगा। C ++ में, यहाँ समस्या स्कोप में से एक है। आपके घुंघराले कोष्ठक switchकथन के अंदर सब कुछ के रूप में गुंजाइश को परिभाषित करते हैं । इसका मतलब यह है कि आपको ऐसे स्कोप के साथ छोड़ दिया जाता है, जहां इनिशियलाइजेशन को छोड़ते हुए कोड में आगे छलांग लगाई जाएगी।

इसे संभालने का सही तरीका यह है कि उस caseस्टेटमेंट के लिए एक स्कोप स्पेसिफिक को परिभाषित किया जाए और इसके भीतर अपने वैरिएबल को डिफाइन किया जाए:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}

94
एक नया दायरा खोलने के सापेक्ष - कोड में पठनीयता और स्थिरता का पक्ष। पुराने दिनों में, आपको स्वचालित रूप से एक "अतिरिक्त" स्टैक फ्रेम मिल सकता था, लेकिन अब किसी भी सभ्य अनुकूलन कंपाइलर के लिए ऐसा नहीं होना चाहिए।
टॉल जेफ

10
मैं जेफ के साथ सहमत हूं - इंडेंटिंग शैली के कारण स्विच स्टेटमेंट को पढ़ते समय यह "मान लेना" गुंजाइश के लिए बहुत आसान है जो कि ज्यादातर लोग उपयोग करते हैं। मेरी अपनी शैली हमेशा प्रत्येक मामले / डिफ़ॉल्ट के लिए एक नया दायरा खोलने की है यदि यह एक पंक्ति से अधिक लंबा हो।
बोल्स

39
अगर आप किसी भी नए चर की घोषणा नहीं करते हैं, तो क्या आप मुझे कोई सी + + कंपाइलर ढूंढ सकते हैं? आपने मुझे संक्षेप में चिंतित किया है, लेकिन जी ++ 3.1, विजुअल C ++ 7 या इंटेल C ++ 8 में से कोई भी नए स्कोप के लिए कोई कोड उत्पन्न नहीं करेगा जहां आप किसी भी चर को घोषित नहीं करते हैं।
क्रिस जेफरसन

10
@ workmad3 एक नए घुंघराले ब्रेस ब्लॉक में प्रवेश करके एक नए स्टैक फ्रेम का कारण नहीं बनता है stackoverflow.com/questions/2759371/…
MTVS

3
@TallJef मुझे नहीं पता कि आप किस 'पुराने दिन' का ज़िक्र कर रहे हैं। मुझे कभी भी एक कंपाइलर का सामना नहीं करना पड़ा है जहां एक विधि के लिए सभी स्टैक स्पेस को आवंटित नहीं किया जाता है, जब विधि 40 साल में दर्ज की जाती है।
user207421

331

यह सवाल है इसे [सी] और [C ++] एक ही समय में के रूप में चिह्नित किया गया था। मूल कोड वास्तव में C और C ++ दोनों में अमान्य है, लेकिन पूरी तरह से अलग असंबंधित कारणों के लिए।

  • C ++ में यह कोड अमान्य है क्योंकि case ANOTHER_VAL:लेबल newValइसके आरंभ को दरकिनार कर चर के दायरे में कूदता है। स्वचालित ऑब्जेक्ट्स के प्रारंभिक बायपास करने वाले कूदता सी ++ में अवैध हैं। समस्या के इस पक्ष को अधिकांश उत्तरों द्वारा सही ढंग से संबोधित किया गया है।

  • हालांकि, C भाषा में वैरिएबल इनिशियलाइज़ेशन को दरकिनार कर एक त्रुटि नहीं है। चर के दायरे में कूदना, इसके आरंभिक चरण में सी। में कानूनी है। इसका सीधा सा मतलब है कि चर को असिंचित छोड़ दिया गया है। मूल कोड पूरी तरह से अलग कारण से सी में संकलित नहीं करता है। case VAL:मूल कोड में लेबल चर की घोषणा से जुड़ा हुआ है newVal। सी भाषा में घोषणाएँ कथन नहीं हैं। उन्हें लेबल नहीं किया जा सकता है। और इस कोड के C कोड के रूप में व्याख्या किए जाने पर त्रुटि का कारण बनता है।

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }
    

अतिरिक्त {}ब्लॉक जोड़ना C ++ और C दोनों समस्याओं को ठीक करता है, भले ही ये समस्याएं बहुत भिन्न हों। C ++ की तरफ यह स्कोप को सीमित कर देता है newVal, जिससे यह सुनिश्चित हो जाता है कि case ANOTHER_VAL:अब उस स्कोप में कूदता नहीं है, जो C ++ इश्यू को खत्म कर देता है। सी तरफ जो अतिरिक्त {}एक यौगिक बयान पेश करता है, इस प्रकार case VAL:एक बयान पर लागू करने के लिए लेबल बनाता है , जो सी मुद्दे को समाप्त करता है।

  • C मामले में समस्या को आसानी से हल किया जा सकता है {}case VAL:लेबल के बाद बस एक खाली विवरण जोड़ें और कोड मान्य हो जाएगा

    switch (val)  
    {  
    case VAL:;            /* Now it works in C! */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:  
      ...
      break;
    }
    

    ध्यान दें कि भले ही यह अब C के दृष्टिकोण से मान्य है, यह C ++ की दृष्टि से अमान्य है।

  • सममित रूप से, C ++ मामले में समस्या को आसानी से बिना हल किया जा सकता है {}। वेरिएबल डिक्लेरेशन से बस इनिशियलाइज़र निकालें और कोड मान्य हो जाएगा

    switch (val)  
    {  
    case VAL: 
      int newVal;
      newVal = 42;  
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }
    

    ध्यान दें कि भले ही यह अब C ++ के दृष्टिकोण से मान्य है, लेकिन यह C के दृष्टिकोण से अमान्य है।


4
@AnT: मैं समझता हूं कि C ++ को ठीक करने वाला C के लिए लागू क्यों नहीं है; हालाँकि, मैं यह समझने में असमर्थ हूँ कि यह पहली जगह में आरंभीकरण को रोकने के C ++ मुद्दे को कैसे ठीक करता है? क्या यह अभी भी घोषणा और असाइनमेंट को newValनहीं छोड़ेगा जब यह कूदता है ANOTHER_VAL?
किंवदंतियां 2

13
@ किंवदंतियां 2k: हां, यह अभी भी इसे छोड़ देता है। हालांकि, जब मैं कहता हूं "यह समस्या को ठीक करता है", मेरा मतलब है कि यह सी ++ संकलक त्रुटि को ठीक करता है । C ++ में initializer के साथ स्केलर घोषणा को छोड़ना अवैध है , लेकिन initializer के बिना स्केलर घोषणा को छोड़ना पूरी तरह से ठीक है । पर case ANOTHER_VAL:बिंदु चर newValदिखाई दे रहा है, लेकिन अनिश्चित मूल्य के साथ।
चींटी

3
चित्त आकर्षण करनेवाला। मुझे यह प्रश्न §A9.3: Compound StatementK & R C (द्वितीय संस्करण) से पढ़ने के बाद मिला । प्रविष्टि में एक यौगिक-बयान की तकनीकी परिभाषा का उल्लेख किया गया है {declaration-list[opt] statement-list[opt]}। उलझन में, क्योंकि मैंने एक घोषणा डब्ल्यूएएस के एक बयान के बारे में सोचा था, मैंने इसे देखा और तुरंत यह सवाल पाया, एक उदाहरण जहां कहा गया कि असमानता स्पष्ट हो जाती है और वास्तव में एक कार्यक्रम टूट जाता है। मेरा मानना ​​है कि घोषणा के पहले एक और समाधान (सी के लिए) एक और बयान (संभवत: एक शून्य बयान?) डालना होगा ताकि लेबल-बयान संतुष्ट हो जाए।
ब्रैडेन बेस्ट

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

3
यह ध्यान देने योग्य है कि खाली विवरण जोड़ने का कार्य केवल C99 के लिए काम करता है। C89 में, वैरिएबल को उनके संलग्न ब्लॉक की शुरुआत में घोषित किया जाना चाहिए।
आर्थर टाका

136

ठीक है। बस इसे स्पष्ट रूप से स्पष्ट करने के लिए घोषणा से कोई लेना देना नहीं है। यह केवल "आरंभीकरण पर कूदने" से संबंधित है (ISO C ++ '03 6.7 / 3)

यहां बहुत सारे पदों का उल्लेख किया गया है कि घोषणा पर कूदने के परिणामस्वरूप चर "घोषित नहीं किया जा रहा है" हो सकता है। यह सच नहीं है। एक POD ऑब्जेक्ट को एक इनिशलाइज़र के बिना घोषित किया जा सकता है लेकिन इसका एक अनिश्चित मान होगा। उदाहरण के लिए:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

जहां ऑब्जेक्ट एक गैर-पीओडी है या संकलक को संक्षेप में एक इनिशलाइज़र जोड़ता है, और इसलिए ऐसी घोषणा पर कूदना संभव नहीं है:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

यह सीमा स्विच स्टेटमेंट तक सीमित नहीं है। आरंभिक तौर पर कूदने के लिए 'गोटो' का उपयोग करना भी एक त्रुटि है:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

थोड़ा बहुत सामान्य ज्ञान यह है कि यह सी ++ और सी। सी के बीच का अंतर है, यह आरंभीकरण पर कूदने के लिए एक त्रुटि नहीं है।

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


2
"आरंभीकरण पर कूदने में त्रुटि" ??? मेरे जीसीसी के साथ नहीं। यह नीचे लेबल का उपयोग करते समय "j का उपयोग किया जा सकता है, जो इकाई के रूप में इस्तेमाल किया जा सकता है" चेतावनी दे सकता है, लेकिन कोई त्रुटि नहीं है। हालांकि, स्विच के मामले में, एक त्रुटि है (एक कठिन त्रुटि, कमजोर चेतावनी नहीं)।
मेकी

9
@ मीकी: यह सी ++ में अवैध है। ISO C ++ '03 - 6.7 / 3: "... एक प्रोग्राम जो एक बिंदु से कूदता है, जहां स्वचालित भंडारण अवधि के साथ एक स्थानीय चर एक बिंदु के दायरे में नहीं है जहां यह गुंजाइश में बीमार है, जब तक कि चर में POD प्रकार नहीं है (3.9) और बिना इनिशियलाइज़र (8.5) के घोषित किया गया है। "
रिचर्ड कॉर्डन

1
हां, लेकिन यह सी में अवैध नहीं है (कम से कम जीसीसी का कहना है कि यह नहीं है)। j अनइंस्टाल्यूट किया जाएगा (कुछ यादृच्छिक संख्या है), लेकिन संकलक इसे संकलित करता है। हालांकि, स्विच स्टेटमेंट के मामले में, कंपाइलर भी इसे संकलित नहीं करेगा और मैं एक गोटो / लेबल केस और एक स्विच केस के बीच अंतर को देखने में विफल रहता हूं।
मेक्की

8
@ मीकी: सामान्य रूप से एक भी संकलक व्यवहार को प्रतिबिंबित नहीं करता है कि वास्तव में भाषा द्वारा अनुमति दी गई है। मैंने C'90 और C'99 दोनों की जाँच की है और दोनों मानकों में एक स्विच स्टेटमेंट में इनिशियलाइज़ेशन जंप के साथ एक उदाहरण शामिल है।
रिचर्ड कॉर्डन

38

पूरा स्विच स्टेटमेंट एक ही दायरे में है। इसके आसपास जाने के लिए, यह करें:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

कोष्ठक पर ध्यान दें


30

सभी उत्तरों और कुछ और शोधों को पढ़ने के बाद मुझे कुछ चीजें मिलती हैं।

Case statements are only 'labels'

सी में, विनिर्देश के अनुसार,

Ments6.8.1 लेबल किए गए विवरण:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

C में ऐसा कोई क्लॉज नहीं है जो "लेबल डिक्लेरेशन" की अनुमति देता है। यह सिर्फ भाषा का हिस्सा नहीं है।

इसलिए

case 1: int x=10;
        printf(" x is %d",x);
break;

यह संकलित नहीं करेगा , http://codepad.org/YiyLQTYw देखें । जीसीसी एक त्रुटि दे रहा है:

label can only be a part of statement and declaration is not a statement

यहाँ तक की

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

यह भी संकलन नहीं है , http://codepad.org/BXnRD3bu देखें । यहाँ मुझे भी वही त्रुटि मिल रही है।


C ++ में, विनिर्देश के अनुसार,

लेबल-घोषणा की अनुमति है, लेकिन लेबल-युक्तिकरण की अनुमति नहीं है।

Http://codepad.org/ZmQ0IyDG देखें ।


ऐसी स्थिति का समाधान दो है

  1. या तो {} का उपयोग करके नए दायरे का उपयोग करें

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
  2. या लेबल के साथ डमी स्टेटमेंट का उपयोग करें

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. स्विच से पहले वेरिएबल को डिक्लेयर करें () और केस स्टेटमेंट में अलग-अलग वैल्यू के साथ इनिशियलाइज़ करें अगर यह आपकी आवश्यकता को पूरा करता है

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }

स्विच स्टेटमेंट के साथ कुछ और बातें

स्विच में कभी कोई स्टेटमेंट न लिखें जो किसी भी लेबल का हिस्सा नहीं है, क्योंकि वे कभी निष्पादित नहीं होंगे:

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

Http://codepad.org/PA1quYX3 देखें ।


2
आपने C समस्या का सही वर्णन किया है। लेकिन यह दावा कि C ++ में आरंभीकरण की अनुमति नहीं है पूरी तरह से सच नहीं है। C ++ में लेबल किए गए प्रारंभ के साथ कुछ भी गलत नहीं है। C ++ जो अनुमति नहीं देता है वह परिवर्तनशील के दायरे में चर के प्रारंभ पर कूद रहा है । इसलिए, सी के दृष्टिकोण से, समस्याएं लेबल के साथ हैं और आपने इसे सही ढंग से वर्णित किया है। लेकिन C ++ के दृष्टिकोण से, समस्या लेबल के साथ है । aacase VAL:case ANOTHER_VAL:
एनटी

C ++ में, C के विपरीत, घोषणाएँ कथनों का सबसेट होती हैं।
कीथ थॉम्पसन

20

आप ऐसा नहीं कर सकते, क्योंकि caseलेबल वास्तव में केवल ब्लॉक में प्रवेश बिंदु हैं।

यह डफ के उपकरण द्वारा सबसे स्पष्ट रूप से चित्रित किया गया है । यहाँ विकिपीडिया से कुछ कोड है:

strcpy(char *to, char *from, size_t 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);
    }
}

ध्यान दें कि caseलेबल पूरी तरह से ब्लॉक सीमाओं को कैसे अनदेखा करते हैं। हां, यह बुराई है। लेकिन यही कारण है कि आपका कोड उदाहरण काम नहीं करता है। एक करने के लिए कूदते caseलेबल का उपयोग कर के रूप में ही है goto, तो आप एक निर्माता के साथ एक स्थानीय चर के ऊपर कूद करने की अनुमति नहीं है।

जैसा कि कई अन्य पोस्टरों ने संकेत दिया है, आपको अपने स्वयं के ब्लॉक में रखने की आवश्यकता है:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }

1
इस डफ के उपकरण के कार्यान्वयन में एक बग है जो इसे बेहद धीमा बनाता है: गिनती प्रकार है इसलिए% को एक वास्तविक विभाजन / मोड्यूलो ऑपरेशन करना चाहिए। गणना को अहस्ताक्षरित (या बेहतर अभी तक, हमेशा काउंट / इंडेक्स के लिए size_t का उपयोग करें) और समस्या दूर हो जाती है।
आर .. गिटहब स्टॉप हेल्पिंग ICE

1
@ आर ..: क्या ?! एक दो के पूरक प्रणाली में, हस्ताक्षर 2 की शक्तियों द्वारा modulos को प्रभावित नहीं करता है (यह सिर्फ एक है और नीचे की बिट्स पर है), और 2 की शक्तियों द्वारा विभाजनों को तब तक प्रभावित नहीं करता है जब तक कि आपके प्रोसेसर आर्किटेक्चर में अंकगणितीय राइट-शिफ्ट ऑपरेशन हो ( SARx86 में, बनाम SHRजो अहस्ताक्षरित पारियों के लिए है)।
क्रिस जेस्टर-यंग

@ क्रिस: मेरा मानना ​​है कि उसका मतलब है जब संकलक को नकारात्मक मूल्यों के लिए अनुमति देना चाहिए, जहां "बस और नीचे के बिट पर" पकड़ नहीं है; उदाहरण के लिए, जी + + का उपयोग कर इस दो के पूरक प्रणाली पर -1% -1 देता है (इस मामले में संकेत 5.6 / 4 के अनुसार कार्यान्वयन है)।

3
@ क्रिस: मैं आपसे सहमत हूं कि आर प्रभाव को बढ़ा रहा है; मैंने केवल आपकी टिप्पणी देखी और एक सरल और पर्याप्त जानकारी नहीं थी।

1
यह भी ध्यान देने योग्य है कि मूल विकिपीडिया कोड मेमोरी मैप्ड आउटपुट में डेटा भेजने के लिए है, जो यहां अजीब लगता है क्योंकि इसका उल्लेख नहीं किया गया है और हर बाइट को उसी "स्थान" पर कॉपी किया जाता है। के आसपास हो सकता है कि या तो पोस्टफिक्स ++ को जोड़कर या उपयोग किए गए मामले का उल्लेख मेमोरी मैप्ड IO के लिए है। मूल प्रश्न :-) के लिए पूरी तरह से परिधीय।
पीटर

16

अब तक के अधिकांश उत्तर एक मामले में गलत हैं: आप केस स्टेटमेंट के बाद वैरिएबल घोषित कर सकते हैं , लेकिन आप उन्हें शुरू नहीं कर सकते :

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

जैसा कि पहले उल्लेख किया गया है, इसके आस-पास का एक अच्छा तरीका अपने मामले के लिए एक गुंजाइश बनाने के लिए ब्रेसिज़ का उपयोग करना है।


1
श्री 32 आपको गलतफहमी हुई है कि आपकी त्रुटि क्या है: हाँ यह संकलन करने के लिए नहीं है, बल्कि इसलिए कि आप एक स्विच के अंदर एक चर घोषित कर रहे हैं। त्रुटि इसलिए है क्योंकि आप एक बयान के बाद एक वैरिएबल घोषित करने की कोशिश कर रहे हैं, जो सी में गैरकानूनी है
MrZebra

1
अब एक दिन जो c90 में कानूनी है और c
Jeegar Patel

12

मेरी पसंदीदा बुराई स्विच चाल एक अवांछित केस लेबल को छोड़ने के लिए (0) का उपयोग करने के लिए है।

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

लेकिन बहुत दुष्ट।


बहुत अच्छा। उदाहरण के लिए: केस 0 और केस 1 उदाहरण के लिए एक चर को अलग-अलग तरीके से शुरू करने के लिए हो सकता है जो तब मामले में उपयोग किया जाता है 2.
hlovdal

1
यदि आप चाहते हैं कि केस ० और केस १ दोनों केस २ के माध्यम से गिरते हैं (केस ० बिना केस १ से गिरते हुए)। पता नहीं अगर यह वास्तव में उपयोगी है, लेकिन यकीन है कि काम करता है।
पेट्रूजा

1
आप gotoबिना किसी कोड के
आक्षेप के


7

यदि आप एक नया ब्लॉक शुरू करते हैं, तो आप एक स्विच स्टेटमेंट के भीतर चर घोषित कर सकते हैं :

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

इसका कारण स्थानीय चर (नों) के भंडारण के लिए स्टैक पर स्थान आवंटित करना (और पुनः प्राप्त करना) है।


1
चर घोषित किया जा सकता है, लेकिन इसे आरंभीकृत नहीं किया जा सकता है। इसके अलावा, मुझे पूरा यकीन है कि यह मुद्दा वैसे भी स्टैक और स्थानीय चर से संबंधित नहीं है।
रिचर्ड कॉर्डन

6

विचार करें:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

ब्रेक स्टेटमेंट के अभाव में, कभी-कभी न्यूवैल को दो बार घोषित किया जाता है, और आपको नहीं पता कि यह रनटाइम तक होता है या नहीं। मेरा अनुमान है कि सीमा इस तरह के भ्रम के कारण है। न्यूवैल का दायरा क्या होगा? कन्वेंशन यह हुक्म देगा कि यह पूरे स्विच ब्लॉक (ब्रेसिज़ के बीच) होगा।

मैं कोई C ++ प्रोग्रामर नहीं हूं, लेकिन C में:

switch(val) {
    int x;
    case VAL:
        x=1;
}

ठीक काम करता है। एक स्विच ब्लॉक के अंदर एक चर की घोषणा ठीक है। केस गार्ड नहीं होने के बाद घोषणा करना।


3
@ Mr.32: वास्तव में आपका उदाहरण दिखाता है कि एक प्रिंटफ़ को निष्पादित नहीं किया गया है, लेकिन इस मामले में, int x एक बयान नहीं है, बल्कि एक घोषणा है, x घोषित किया गया है, इसके लिए स्थान हर बार आरक्षित होता है जब फ़ंक्शन वातावरण स्टैक हो जाता है, देखें: codepad.org/4E9Zuz1e
पेट्रूजा

मैं प्रश्न के शीर्षक को पढ़ते समय इसे खोजने की उम्मीद कर रहा था, क्योंकि सवाल "केस:" लेबल के भीतर चर घोषित करने के बारे में नहीं है, लेकिन स्विच स्टेटमेंट में है। और केवल आप (और विक्टरएच, आपके उत्तर पर जोर देते हुए) वास्तव में स्विच स्टेटमेंट में चर के बारे में बात करते थे।
सेन्सस

4

स्विच का पूरा खंड एकल घोषणा प्रसंग है। आप उस तरह के केस स्टेटमेंट में वैरिएबल घोषित नहीं कर सकते। इसके बजाय यह प्रयास करें:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}

चर घोषित किया जा सकता है, लेकिन इसे आरंभीकृत नहीं किया जा सकता है।
रिचर्ड कॉर्डन सेप

@ रिचर्ड कॉर्डेन मुझे विश्वास है कि आरंभीकरण काम करेगा। क्या आप अभी भी यह दावा करते हैं कि इसे आरंभीकृत नहीं किया जा सकता है?
chux -

3

यदि आपका कोड "int newVal = 42" कहता है, तो आप यथोचित अपेक्षा करेंगे कि newVal कभी भी अनैतिक नहीं है। लेकिन अगर आप इस कथन (जो आप कर रहे हैं) पर गोटो है तो ठीक यही होता है - newVal- स्कोप है लेकिन असाइन नहीं किया गया है।

यदि वास्तव में ऐसा होने का मतलब है तो भाषा को "int newVal; newVal = 42;" कहकर स्पष्ट करने की आवश्यकता है। अन्यथा आप newVal के दायरे को एकल मामले तक सीमित कर सकते हैं, जो कि आप जो चाहते थे वह अधिक संभावना है।

यदि आप एक ही उदाहरण पर विचार करते हैं, तो यह स्पष्ट कर सकता है, लेकिन "कॉन्स्टीट्यूशन newVal = 42;"


3

मैं सिर्फ स्लिम की बात पर जोर देना चाहता था । एक स्विच निर्माण एक पूर्ण, प्रथम श्रेणी के नागरिक गुंजाइश बनाता है। इसलिए अतिरिक्त ब्रैकेट के बिना , पहले केस लेबल से पहले स्विच स्टेटमेंट में एक वैरिएबल घोषित करना (और शुरू करना) संभव है :

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}

-1 यहाँ int newVal = 42; wil को कभी क्रियान्वित नहीं किया जाता है। इस कोडपैड को
जीगर पटेल

4
घोषणा निष्पादित की int newVal जाएगी , लेकिन = 42असाइनमेंट नहीं ।
पेत्रुजा

3

अब तक C ++ के उत्तर आए हैं।

C ++ के लिए, आप एक प्रारंभ से अधिक नहीं कूद सकते। आप सी में कर सकते हैं। हालांकि, सी में, एक घोषणा एक बयान नहीं है, और मामले के लेबल को बयानों का पालन करना होगा।

तो, मान्य (लेकिन बदसूरत) सी, अमान्य सी ++

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

इसके विपरीत, C ++ में, एक घोषणापत्र एक कथन है, इसलिए निम्नलिखित मान्य C ++, अमान्य C है

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}

1
दूसरा उदाहरण वैध नहीं है C ++ (vc2010 और gcc 4.6.1 C ++ के साथ परीक्षण आरंभिक भाग को छोड़ना नहीं चाहिए। gcc त्रुटि संदेश है: 'int i' का क्रॉस आरंभीकरण
9

3

दिलचस्प है कि यह ठीक है:

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

... लेकिन यह नहीं है:

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

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


1
पहले gcc पर ठीक नहीं है 4.2: "त्रुटि: 'int' से पहले अपेक्षित अभिव्यक्ति"। जैसा कि पीटर और Mr.32 कहते हैं, "केस 0:? इंट जे; ..." और "केस 0:; इंट जे = 7; ..." दोनों काम करते हैं। C में समस्या सिर्फ इतनी है कि "केस <लेबल>: डिक्लेरेशन" मान्य C सिंटैक्स नहीं है।
डब्यूजिम

3

मैंने इस प्रश्न के लिए यह उत्तर मौखिक रूप से लिखा है । हालाँकि जब मैंने इसे पूरा किया तो मैंने पाया कि उत्तर को बंद कर दिया गया है। इसलिए मैंने इसे यहां पोस्ट किया है, हो सकता है कि मानक के संदर्भों को पसंद करने वाला कोई व्यक्ति इसे मददगार लगे।

प्रश्न में मूल कोड:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

वास्तव में 2 प्रश्न हैं:

1. मैं caseलेबल के बाद एक चर की घोषणा क्यों कर सकता हूं ?

ऐसा इसलिए है क्योंकि C ++ लेबल को फॉर्म में होना चाहिए:

N3337 6.1 / 1

लेबल बयान:

...

  • विशेषता-विशेष-seqopt case constant-expression :statement

...

और C++ घोषणा के बयान में भी बयान के रूप में माना जाता है (के विपरीत C):

N3337 6/1:

कथन :

...

घोषणापत्र का बयान

...

2. मैं चर घोषणा पर क्यों कूद सकता हूं और फिर इसका उपयोग कर सकता हूं?

क्योंकि: N3337 6.7 / 3

एक ब्लॉक में स्थानांतरित करना संभव है, लेकिन इस तरह से नहीं कि प्रारंभिककरण के साथ घोषणाओं को बायपास करें । एक प्रोग्राम है जो कूदता है ( से हस्तांतरण एक की हालत एक मामले लेबल करने के लिए स्विच बयान एक कूद माना जाता है इस संबंध में।)

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

के बाद से kकी है अदिश प्रकार , और घोषणा कूद के बिंदु पर आरंभ नहीं किया है पर यह की घोषणा संभव है। यह शब्दार्थ समतुल्य है:

goto label;

int x;

label:
cout << x << endl;

हालांकि यह संभव नहीं होगा, अगर xघोषणा के बिंदु पर आरंभ किया गया था:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;

1

नए वैरिएबल को केवल ब्लॉक स्कोप में ही डिक्लेयर किया जा सकता है। आपको कुछ इस तरह लिखना होगा:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

बेशक, newVal में केवल ब्रेसेस की गुंजाइश है ...

चीयर्स, राल्फ


1

एक switchब्लॉक का एक उत्तराधिकार के समान नहीं है if/else ifब्लॉक। मुझे आश्चर्य है कि कोई अन्य उत्तर स्पष्ट रूप से नहीं बताता है।

इस switchकथन पर विचार करें :

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

यह आश्चर्य की बात हो सकती है, लेकिन संकलक इसे एक साधारण के रूप में नहीं देखेंगे if/else if। यह निम्नलिखित कोड का उत्पादन करेगा:

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

caseबयान लेबल में बदला जाता है और उसके बाद के साथ कहा जाता है goto। कोष्ठक एक नया दायरा बनाते हैं और अब यह देखना आसान है कि आप एक switchखंड में एक ही नाम के साथ दो चर घोषित क्यों नहीं कर सकते ।

यह अजीब लग सकता है, लेकिन यह गिरावट का समर्थन करने के लिए आवश्यक है (यानी, breakनिष्पादन को अगले तक जारी रखने के लिए उपयोग नहीं करना case)।


0

मेरा मानना ​​है कि हाथ में समस्या यह है कि बयान को छोड़ दिया गया था, और आपने कहीं और संस्करण का उपयोग करने की कोशिश की, यह घोषित नहीं किया जाएगा।


0

newVal स्विच के पूरे दायरे में मौजूद है, लेकिन केवल आरंभिक है यदि वैल लिम्ब हिट है। यदि आप वैल में कोड के चारों ओर एक ब्लॉक बनाते हैं तो यह ठीक होना चाहिए।


0

सी ++ मानक है: यह एक ब्लॉक में स्थानांतरित करना संभव है, लेकिन एक तरह से नहीं है कि प्रारंभिककरण के साथ घोषणाओं को बायपास करता है। एक प्रोग्राम जो उस बिंदु से कूदता है जहां स्वचालित भंडारण अवधि के साथ एक स्थानीय चर एक बिंदु के दायरे में नहीं होता है जहां यह गुंजाइश में बीमार है, जब तक कि चर में POD प्रकार (3.9) न हो और एक इनिशलाइज़र (8.5) के बिना घोषित हो।

इस नियम को स्पष्ट करने के लिए कोड:

#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}

कोड को इनिलाइज़र प्रभाव दिखाने के लिए:

#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}

0

ऐसा प्रतीत होता है कि अनाम वस्तुओं को स्विच केस स्टेटमेंट में घोषित किया जा सकता है या बनाया जा सकता है, इस कारण कि उन्हें संदर्भित नहीं किया जा सकता है और जैसे कि अगले मामले में नहीं गिर सकता है। इस उदाहरण पर विचार करें GCC 4.5.3 और विजुअल स्टूडियो 2008 पर संकलित किया जा सकता है (यह एक अनुपालन मुद्दा हो सकता है, इसलिए विशेषज्ञ कृपया इसमें वजन करें)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}

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

1
DV नहीं, लेकिन: संपूर्ण प्रश्न नामित चरों की घोषणा / दायरे के बारे में है। एक अस्थायी ("अनाम वस्तु" कोई शब्द नहीं है) एक नामित चर नहीं है, न ही यह एक घोषणा है, न ही यह गुंजाइश के अधीन है (जब तक कि constअपने स्वयं के दायरे के साथ संदर्भ के लिए बाध्य न हो )। यह एक अभिव्यक्ति है जो अपने बयान के भीतर रहता है और मर जाता है (जहां भी हो सकता है)। इसलिए, यह पूरी तरह अप्रासंगिक है।
अंडरस्कोर_ड

Foo();एक घोषणा नहीं है; सवाल घोषणाओं के बारे में है।
MM
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.