ข้อผิดพลาด: ข้ามไปที่ป้ายกำกับกรณีและปัญหา


229

ฉันเขียนโปรแกรมที่เกี่ยวข้องกับการใช้คำสั่ง switch ... อย่างไรก็ตามในการรวบรวมมันแสดง:

ข้อผิดพลาด: ข้ามไปที่ป้ายกำกับกรณีและปัญหา

ทำไมถึงทำเช่นนั้น?

#include <iostream>
#include <cstdlib>
#include <fstream>
#include <string>

using namespace std;

class contact
{
public:
    string name;
    int phonenumber;
    string address;
    contact() {
        name= "Noname";
        phonenumber= 0;
        address= "Noaddress";
    }
};

int main() {
    contact *d;
    d = new contact[200];
    string name,add;
    int choice,modchoice,t;//Variable for switch statement
    int phno,phno1;
    int i=0;
    int initsize=0, i1=0;//i is declared as a static int variable
    bool flag=false,flag_no_blank=false;

    //TAKE DATA FROM FILES.....
    //We create 3 files names, phone numbers, Address and then abstract the data from these files first!
    fstream f1;
    fstream f2;
    fstream f3;
    string file_input_name;
    string file_input_address;
    int file_input_number;

    f1.open("./names");
    while(f1>>file_input_name){
        d[i].name=file_input_name;
        i++;
    }
    initsize=i;

    f2.open("./numbers");
    while(f2>>file_input_number){
        d[i1].phonenumber=file_input_number;
        i1++;
    }
    i1=0;

    f3.open("./address");
    while(f3>>file_input_address){
        d[i1].address=file_input_address;
        i1++;
    }

    cout<<"\tWelcome to the phone Directory\n";//Welcome Message
    do{
        //do-While Loop Starts
        cout<<"Select :\n1.Add New Contact\n2.Update Existing Contact\n3.Display All Contacts\n4.Search for a Contact\n5.Delete a  Contact\n6.Exit PhoneBook\n\n\n";//Display all options
        cin>>choice;//Input Choice from user

        switch(choice){//Switch Loop Starts
        case 1:
            i++;//increment i so that values are now taken from the program and stored as different variables
            i1++;
            do{
                cout<<"\nEnter The Name\n";
                cin>>name;
                if(name==" "){cout<<"Blank Entries are not allowed";
                flag_no_blank=true;
                }
            }while(flag_no_blank==true);
            flag_no_blank=false;
            d[i].name=name;
            cout<<"\nEnter the Phone Number\n";
            cin>>phno;
            d[i1].phonenumber=phno;
            cout<<"\nEnter the address\n";
            cin>>add;
            d[i1].address=add;
            i1++;
            i++;
            break;//Exit Case 1 to the main menu
        case 2:
            cout<<"\nEnter the name\n";//Here it is assumed that no two contacts can have same contact number or address but may have the same name.
            cin>>name;
            int k=0,val;
            cout<<"\n\nSearching.........\n\n";
            for(int j=0;j<=i;j++){
                if(d[j].name==name){
                    k++;
                    cout<<k<<".\t"<<d[j].name<<"\t"<<d[j].phonenumber<<"\t"<<d[j].address<<"\n\n";
                    val=j;
                }
            }
            char ch;
            cout<<"\nTotal of "<<k<<" Entries were found....Do you wish to edit?\n";
            string staticname;
            staticname=d[val].name;
            cin>>ch;
            if(ch=='y'|| ch=='Y'){
                cout<<"Which entry do you wish to modify ?(enter the old telephone number)\n";
                cin>>phno;
                for(int j=0;j<=i;j++){
                    if(d[j].phonenumber==phno && staticname==d[j].name){
                        cout<<"Do you wish to change the name?\n";
                        cin>>ch;
                        if(ch=='y'||ch=='Y'){
                            cout<<"Enter new name\n";
                            cin>>name;
                            d[j].name=name;
                        }
                        cout<<"Do you wish to change the number?\n";
                        cin>>ch;
                        if(ch=='y'||ch=='Y'){
                            cout<<"Enter the new number\n";
                            cin>>phno1;
                            d[j].phonenumber=phno1;
                        }
                        cout<<"Do you wish to change the address?\n";
                        cin>>ch;
                        if(ch=='y'||ch=='Y'){
                            cout<<"Enter the new address\n";
                            cin>>add;
                            d[j].address=add;
                        }
                    }
                }
            }
            break;
        case 3 : {
            cout<<"\n\tContents of PhoneBook:\n\n\tNames\tPhone-Numbers\tAddresses";
            for(int t=0;t<=i;t++){
                cout<<t+1<<".\t"<<d[t].name<<"\t"<<d[t].phonenumber<<"\t"<<d[t].address;
            }
            break;
                 }
        }
    }
    while(flag==false);
    return 0;
}

1
คุณพยายามรวบรวมรหัสอะไร คุณใช้คอมไพเลอร์อะไร? คุณใส่แต่ละcaseวงเล็บไว้ในวงเล็บหรือไม่?
Cody Grey

2
นั่นเป็นข้อความแสดงข้อผิดพลาดวงเวียนที่น่าประทับใจอย่างหนึ่ง
jozxyqk

คำตอบ:


437

ปัญหาคือว่าตัวแปรที่ประกาศในหนึ่งcaseยังคงมองเห็นในภายหลังcases เว้นแต่ชัดเจน{ }บล็อกจะใช้แต่พวกเขาจะไม่สามารถเตรียมcaseเนื่องจากรหัสเริ่มต้นเป็นอีก

ในรหัสต่อไปนี้ถ้าfooเท่ากับ 1 ทุกอย่างก็โอเค แต่ถ้ามันเท่ากับ 2 เราจะใช้iตัวแปรที่มีอยู่โดยไม่ตั้งใจแต่อาจมีขยะ

switch(foo) {
  case 1:
    int i = 42; // i exists all the way to the end of the switch
    dostuff(i);
    break;
  case 2:
    dostuff(i*2); // i is *also* in scope here, but is not initialized!
}

การตัดเคสในบล็อกที่ชัดเจนช่วยแก้ปัญหา:

switch(foo) {
  case 1:
    {
        int i = 42; // i only exists within the { }
        dostuff(i);
        break;
    }
  case 2:
    dostuff(123); // Now you cannot use i accidentally
}

แก้ไข

การทำอย่างละเอียดเพิ่มเติมงบเป็นเพียงชนิดแฟนซีโดยเฉพาะอย่างยิ่งของswitch gotoนี่คือโค้ดที่คล้ายคลึงกันซึ่งแสดงถึงปัญหาเดียวกัน แต่ใช้gotoแทนswitch:

int main() {
    if(rand() % 2) // Toss a coin
        goto end;

    int i = 42;

  end:
    // We either skipped the declaration of i or not,
    // but either way the variable i exists here, because
    // variable scopes are resolved at compile time.
    // Whether the *initialization* code was run, though,
    // depends on whether rand returned 0 or 1.
    std::cout << i;
}

1
ดูรายงานข้อผิดพลาด LLVM คงที่นี้สำหรับคำอธิบายอื่น ๆ : llvm.org/bugs/show_bug.cgi?id=7789
Francesco

70

การประกาศตัวแปรใหม่ในข้อความสั่ง case เป็นสิ่งที่ทำให้เกิดปัญหา การแนบcaseคำสั่งทั้งหมดใน{}จะ จำกัด ขอบเขตของตัวแปรที่ประกาศใหม่ไปยังเคสที่กำลังดำเนินการซึ่งแก้ปัญหาได้

switch(choice)
{
    case 1: {
       // .......
    }break;
    case 2: {
       // .......
    }break;
    case 3: {
       // .......
    }break;
}    

คำแนะนำการแก้ไขที่สะอาดกว่า
yc_yuy

จะมีปัญหาใด ๆ หรือไม่ถ้าฉันใส่คำสั่ง break เข้าไปในเครื่องหมายปีกกาแบบหยิก
Vishal Sharma

10

มาตรฐาน C ++ 11 ในการกระโดดข้ามการกำหนดค่าเริ่มต้นบางอย่าง

JohannesDให้คำอธิบายตอนนี้สำหรับมาตรฐาน

C ++ 11 N3337 ร่างมาตรฐาน 6.7 "คำประกาศ" พูดว่า:

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

87) การถ่ายโอนจากเงื่อนไขของคำสั่งเปลี่ยนไปยังป้ายชื่อกรณีถือเป็นการกระโดดในแง่นี้

[ตัวอย่าง:

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
}

- ตัวอย่างท้าย]

ในฐานะของ GCC 5.2 ขณะนี้ข้อความแสดงข้อผิดพลาดแจ้งว่า:

ข้ามการเริ่มต้นของ

C อนุญาต: c99 goto การเริ่มต้นที่ผ่านมา

C99 N1256 ร่างมาตรฐาน Annex I "คำเตือนที่พบบ่อย" พูดว่า:

2 บล็อกที่มีการเริ่มต้นของวัตถุที่มีระยะเวลาการจัดเก็บอัตโนมัติจะถูกข้ามไป


6

คำตอบของ JohannesDนั้นถูกต้อง แต่ฉันรู้สึกว่ามันยังไม่ชัดเจนในแง่มุมของปัญหา

ตัวอย่างที่เขาให้ประกาศและเริ่มต้นตัวแปรiในกรณีที่ 1 แล้วลองใช้มันในกรณีที่ 2 ข้อโต้แย้งของเขาคือถ้าสวิตช์ตรงไปที่กรณีที่ 2 iจะถูกนำมาใช้โดยไม่ต้องเริ่มต้นและนี่คือสาเหตุที่มีการรวบรวม ความผิดพลาด ณ จุดนี้ใคร ๆ ก็คิดว่าคงไม่มีปัญหาหากตัวแปรที่ประกาศในเคสไม่เคยถูกใช้ในกรณีอื่น ตัวอย่างเช่น:

switch(choice) {
    case 1:
        int i = 10; // i is never used outside of this case
        printf("i = %d\n", i);
        break;
    case 2:
        int j = 20; // j is never used outside of this case
        printf("j = %d\n", j);
        break;
}

หนึ่งอาจคาดว่าโปรแกรมนี้จะรวบรวมเนื่องจากทั้งสองiและjจะใช้เฉพาะในกรณีที่ประกาศพวกเขา น่าเสียดายที่ใน C ++ มันไม่ได้รวบรวม: ดังที่Ciro Santilli อธิบายไว้เราไม่สามารถข้ามไปcase 2:ได้เพราะสิ่งนี้จะข้ามการประกาศด้วยการเริ่มต้นiและแม้ว่าจะcase 2ไม่ได้ใช้iเลย สิ่งนี้ยังคงเป็นสิ่งต้องห้ามใน C ++

ที่น่าสนใจคือการปรับเปลี่ยนบางส่วน ( #ifdefไป#includeยังส่วนหัวที่เหมาะสมและเครื่องหมายอัฒภาคหลังจากป้ายกำกับเนื่องจากป้ายกำกับสามารถตามด้วยข้อความสั่งเท่านั้นและการประกาศไม่นับเป็นข้อความใน C ) โปรแกรมนี้จะรวบรวมเป็น C:

// Disable warning issued by MSVC about scanf being deprecated
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

#ifdef __cplusplus
#include <cstdio>
#else
#include <stdio.h>
#endif

int main() {

    int choice;
    printf("Please enter 1 or 2: ");
    scanf("%d", &choice);

    switch(choice) {
        case 1:
            ;
            int i = 10; // i is never used outside of this case
            printf("i = %d\n", i);
            break;
        case 2:
            ;
            int j = 20; // j is never used outside of this case
            printf("j = %d\n", j);
            break;
    }
}

ขอบคุณคอมไพเลอร์ออนไลน์เช่นhttp://rextester.comคุณสามารถลองรวบรวมได้อย่างรวดเร็วไม่ว่าจะเป็น C หรือ C ++ โดยใช้ MSVC, GCC หรือ Clang เนื่องจาก C ใช้งานได้เสมอ (โปรดอย่าลืมตั้งค่า STDIN!) เนื่องจาก C ++ ไม่มีคอมไพเลอร์ยอมรับ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.