การใช้ {} ในคำสั่งกรณี ทำไม?


101

ประเด็นในการใช้{และ}ในcaseคำสั่งคืออะไร? โดยปกติไม่ว่าจะมีกี่บรรทัดในcaseคำสั่งบรรทัดทั้งหมดจะถูกดำเนินการ นี่เป็นเพียงกฎเกี่ยวกับคอมไพเลอร์รุ่นเก่า / ใหม่กว่าหรือมีบางอย่างอยู่เบื้องหลัง?

int a = 0;
switch (a) {
  case 0:{
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
  }
}

และ

int a = 0;
switch (a) {
  case 0:
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
}

57
การใช้งานครั้งเดียวสามารถ จำกัด ขอบเขตของตัวแปรที่ประกาศในคำสั่ง case ได้
Abhishek Bansal


1
การเยื้องมากเกินไปเช่นกัน กรณีนี้เป็นเพียงเลเบลภายในบล็อกของคำสั่งสวิตช์: พวกเขาไม่แนะนำการซ้อนเพิ่มเติมดังนั้นจึงควรสอดคล้องกับswitchคีย์เวิร์ดและในตัวอย่างที่สองคำสั่งที่แนบมาจะเยื้องเพียงครั้งเดียว สังเกตว่าคุณมีการเว้นวรรคสี่ช่องที่น่าอึดอัดใจหลังbreak;.
Kaz

หมายเหตุคำตอบที่ยอมรับนั้นถูกต้องเพียงบางส่วนเนื่องจากความคิดเห็นของแจ็คชี้ให้เห็นและพลาดรายละเอียดปลีกย่อยบางอย่างซึ่งฉันกล่าวไว้ในคำตอบของฉัน
Shafik Yaghmour

เช่นเดียวกับ FYI: ใน C (แม้แต่ C11) แทนที่จะเป็น C ++ คุณไม่สามารถติดป้ายประกาศได้ statementพวกเขาไม่ได้อยู่ในหมวดหมู่ประโยค ใน C ++ คุณสามารถ (องค์ประกอบหนึ่งของหมวดไวยากรณ์statementคือdeclaration statement)
Jonathan Leffler

คำตอบ:


195

{}หมายถึงบล็อกใหม่ของขอบเขต

ลองพิจารณาตัวอย่างที่สร้างขึ้นดังต่อไปนี้:

switch (a)
{
    case 42:
        int x = GetSomeValue();
        return a * x;
    case 1337:
        int x = GetSomeOtherValue(); //ERROR
        return a * x;
}

คุณจะได้รับข้อผิดพลาดของคอมไพเลอร์เนื่องจากxถูกกำหนดไว้แล้วในขอบเขต

การแยกสิ่งเหล่านี้ออกเป็นขอบเขตย่อยของตนเองจะทำให้ไม่จำเป็นต้องประกาศxนอกคำสั่งสวิตช์

switch (a)
{
    case 42: {
        int x = GetSomeValue();
        return a * x; 
    }
    case 1337: {
        int x = GetSomeOtherValue(); //OK
        return a * x; 
    }
}

11
จริงๆแล้ว IMO คุณจะได้รับข้อผิดพลาดของคอมไพเลอร์แม้ว่าคุณจะข้ามการประกาศตัวแปร x ครั้งที่สอง
Abhishek Bansal

1
แม้ว่าการใช้สไตล์นี้มากเกินไปและการวางบล็อกขนาดใหญ่ไว้ในคำสั่งสวิตช์จะทำให้ไม่สามารถอ่านได้ตามกรณี ฉันชอบที่จะให้งบน้อย
masoud

2
@MatthieuM. ฉันทราบข้อเท็จจริงว่า MS Visual Studio 2010 จะมีพฤติกรรมที่ Abhishek ระบุ: จะไม่รวบรวมการประกาศตัวแปรใด ๆ ภายในเคส (เว้นแต่คุณจะใช้วงเล็บปีกกาเพื่อแสดงขอบเขตใหม่ภายในกรณีนั้น) ว่าตรงกับมาตรฐานหรือเปล่าผมไม่ทราบ
KRyan

1
@KRyan: มันไม่ได้ แต่มันเป็นทางเลือกที่ปลอดภัยกว่ามากจนฉันแทบจะไม่สามารถตำหนิพวกเขาได้สำหรับการบังคับใช้สิ่งนี้
Matthieu M.

6
มาตรา 6.7 (3) ของมาตรฐาน (การกำหนดหมายเลขสำหรับแบบร่างปี 2005) ระบุว่าคุณไม่สามารถข้ามการเริ่มต้นได้ดังนั้นคุณจึงไม่สามารถเริ่มต้นในบล็อกเคสได้
Jack Aidley

23

TL; ดร

วิธีเดียวที่คุณสามารถประกาศตัวแปรที่มี intializer หรือวัตถุบางอย่างที่ไม่น่ารำคาญภายในเป็นกรณีคือการแนะนำขอบเขตบล็อกใช้{}หรือโครงสร้างการควบคุมอื่น ๆ ที่มีขอบเขตของตัวเองเหมือนห่วงหรือถ้ามีคำสั่ง

รายละเอียดที่เต็มไปด้วยเลือด

เราจะเห็นว่ากรณีต่างๆเป็นเพียงข้อความที่มีป้ายกำกับเช่นป้ายกำกับที่ใช้กับคำสั่งgoto ( ครอบคลุมอยู่ในร่างมาตรฐาน C ++ส่วน 6.1 คำสั่งที่มีป้ายกำกับ ) และเราสามารถดูได้จากส่วน6.7ย่อหน้าที่3ว่าการกระโดดผ่านการประกาศไม่ได้รับอนุญาตในหลาย ๆ กรณี รวมถึงผู้ที่มีการเริ่มต้น:

เป็นไปได้ที่จะถ่ายโอนไปยังบล็อก แต่ไม่ใช่ในลักษณะที่ข้ามการประกาศด้วยการเริ่มต้น โปรแกรมที่กระโดด87จากจุดที่ตัวแปรที่มีระยะเวลาการจัดเก็บอัตโนมัติไม่อยู่ในขอบเขตไปยังจุดที่อยู่ในขอบเขตนั้นมีรูปแบบที่ไม่ถูกต้องเว้นแต่ตัวแปรจะมีประเภทสเกลาร์ประเภทคลาสที่มีตัวสร้างเริ่มต้นเล็กน้อยและตัวทำลายเล็กน้อย เวอร์ชันที่ผ่านการรับรอง CV ของประเภทใดประเภทหนึ่งเหล่านี้หรืออาร์เรย์ของประเภทก่อนหน้านี้และประกาศโดยไม่มีตัวเริ่มต้น (8.5)

และให้ตัวอย่างนี้:

void f() {
 // ...
 goto lx; // ill-formed: jump into scope of a

 ly:
  X a = 1;
 // ...
 lx:
  goto ly; // OK, jump implies destructor
          // call for a followed by construction
          // again immediately following label ly
}

หมายเหตุมีรายละเอียดปลีกย่อยบางอย่างที่นี่คุณได้รับอนุญาตให้ข้ามผ่านการประกาศสเกลาร์ที่ไม่มีการกำหนดค่าเริ่มต้นตัวอย่างเช่น:

switch( n ) 
{
    int x ;
    //int x  = 10 ; 
    case 0:
      x = 0 ;
      break;
    case 1:
      x = 1 ;
      break;
    default:
      x = 100 ;
      break ;
}

ถูกต้องสมบูรณ์ ( ตัวอย่างสด ) แน่นอนว่าหากคุณต้องการประกาศตัวแปรเดียวกันในแต่ละกรณีพวกเขาแต่ละคนจะต้องมีขอบเขตของตัวเอง แต่มันก็ทำงานในลักษณะเดียวกันนอกเหนือจากคำสั่งสวิตช์เช่นกันดังนั้นจึงไม่ควรแปลกใจมาก

สำหรับเหตุผลในการไม่อนุญาตให้ข้ามการเริ่มต้นที่ผ่านมารายงานข้อบกพร่อง 467แม้ว่าจะครอบคลุมประเด็นที่แตกต่างกันเล็กน้อยก็มีกรณีที่สมเหตุสมผลสำหรับตัวแปรอัตโนมัติ :

[... ] ตัวแปรอัตโนมัติหากไม่ได้กำหนดค่าเริ่มต้นอย่างชัดเจนอาจมีค่าที่ไม่แน่นอน ("ขยะ") รวมถึงการแทนกับดัก [... ]

อาจเป็นเรื่องที่น่าสนใจกว่าที่จะดูกรณีที่คุณขยายขอบเขตภายในการสลับไปมาหลาย ๆกรณีตัวอย่างที่มีชื่อเสียงที่สุดของสิ่งนี้น่าจะเป็นอุปกรณ์ของ Duffซึ่งจะมีลักษณะดังนี้:

void send( int *to, const int *from, int  count)
{
        int n = (count + 7) / 8;
        switch(count % 8) 
        {
            case 0: do {    *to = *from++;   // <- Scope start
            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);    // <- Scope end
        }
}

6

เป็นนิสัยที่ช่วยให้คุณฉีดการประกาศตัวแปรด้วยตัวทำลายผลลัพธ์ (หรือความขัดแย้งของขอบเขต) ลงในcaseประโยค อีกวิธีหนึ่งในการดูก็คือพวกเขากำลังเขียนภาษาที่พวกเขาต้องการซึ่งการควบคุมการไหลทั้งหมดประกอบด้วยบล็อกไม่ใช่ลำดับของคำสั่ง


4

ตรวจสอบข้อ จำกัด ของคอมไพเลอร์พื้นฐานแล้วคุณจะเริ่มสงสัยว่าเกิดอะไรขึ้น:

int c;
c=1;

switch(c)
{
    case 1:
    //{
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    //}

    default : cout<<"def";
}

สิ่งนี้จะทำให้คุณมีข้อผิดพลาด:

error: jump to case label [-fpermissive]
error:   crosses initialization of int* i

แม้ว่าสิ่งนี้จะไม่:

int c;
c=1;

switch(c)
{
    case 1:
    {
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    }

    default : cout<<"def";
}

1

การใช้วงเล็บในสวิตช์หมายถึงบล็อกขอบเขตใหม่ตามที่ Rotem กล่าว

แต่อาจเป็นเช่นกันเพื่อความชัดเจนเมื่อคุณอ่าน หากต้องการทราบว่ากรณีใดหยุดลงเนื่องจากคุณอาจมีภาวะหยุดพักในนั้น


0

สาเหตุอาจเป็น:

  1. ความสามารถในการอ่านช่วยเพิ่มความสามารถในการมองเห็นแต่ละกรณีเป็นส่วนที่กำหนดขอบเขต
  2. การประกาศตัวแปรอื่นที่มีชื่อเดียวกันสำหรับกรณีสวิตช์หลายตัว
  3. การเพิ่มประสิทธิภาพแบบไมโคร - ขอบเขตสำหรับตัวแปรที่จัดสรรทรัพยากรที่มีราคาแพงมากซึ่งคุณต้องการทำลายทันทีที่คุณออกจากขอบเขตของเคสหรือแม้กระทั่งสถานการณ์สปาเก็ตตี้ของการใช้คำสั่ง "GOTO"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.