Erro: pule para o rótulo do caso


229

Eu escrevi um programa que envolve o uso de instruções switch ... No entanto, na compilação, mostra:

Erro: pule para o rótulo do caso.

Por que ele faz isso?

#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
Qual código você está tentando compilar? Qual compilador você está usando? Você incluiu cada casebloco entre chaves?
Cody Gray

2
essa é uma mensagem de erro impressionante rotunda
jozxyqk 28/08

Respostas:


437

O problema é que as variáveis ​​declaradas em uma caseainda são visíveis nos cases subseqüentes, a menos que um { }bloco explícito seja usado, mas elas não serão inicializadas porque o código de inicialização pertence a outra case.

No código a seguir, se for fooigual a 1, tudo está ok, mas se for igual a 2, acidentalmente usaremos a ivariável que existe, mas provavelmente contém lixo.

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!
}

O agrupamento do caso em um bloco explícito resolve o problema:

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
}

Editar

Para elaborar ainda mais, as switchdeclarações são apenas um tipo particularmente sofisticado de a goto. Aqui está um trecho de código análogo que exibe o mesmo problema, mas usando um em gotovez de um 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
Veja este relatório de bug corrigido do LLVM para outras explicações: llvm.org/bugs/show_bug.cgi?id=7789
Francesco

70

A declaração de novas variáveis ​​nas instruções de caso é o que está causando problemas. A inclusão de todas as caseinstruções em {}limitará o escopo das variáveis ​​recém-declaradas ao caso atualmente em execução que resolve o problema.

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

instrução de correção de limpeza
yc_yuy 15/01

Haverá algum problema se eu colocar a declaração de quebra dentro do aparelho?
Vishal Sharma 15/06

10

Padrão C ++ 11 sobre pular algumas inicializações

JohannesD deu uma explicação, agora para os padrões.

O rascunho padrão do C ++ 11 N3337 6.7 "Declaração de declaração" diz:

3 É possível transferir para um bloco, mas não de uma maneira que ignore as declarações com a inicialização. Um programa que pula (87) de um ponto em que uma variável com duração de armazenamento automática não está no escopo para um ponto em que está no escopo está mal formada, a menos que a variável tenha tipo escalar, tipo de classe com um construtor padrão trivial e um trivial destruidor, uma versão qualificada para cv de um desses tipos ou uma matriz de um dos tipos anteriores e é declarada sem um inicializador (8.5).

87) A transferência da condição de uma instrução switch para um rótulo de caso é considerada um salto a esse respeito.

[Exemplo:

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
}

- exemplo final]

A partir do GCC 5.2, a mensagem de erro agora diz:

cruza a inicialização de

C

C permite: c99 goto passado inicialização

O rascunho padrão do Anexo I C99 N1256 "Advertências comuns" diz:

2 Um bloco com inicialização de um objeto com duração automática de armazenamento é pulado para dentro


6

A resposta de JohannesD está correta, mas acho que não está totalmente claro sobre um aspecto do problema.

O exemplo que ele dá declara e inicializa a variável ino caso 1 e, em seguida, tenta usá-la no caso 2. Seu argumento é que, se o comutador for direto para o caso 2, iseria usado sem ser inicializado, e é por isso que há uma compilação erro. Nesse ponto, pode-se pensar que não haveria problema se variáveis ​​declaradas em um caso nunca fossem usadas em outros casos. Por exemplo:

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;
}

Pode-se esperar que esse programa seja compilado, já que ambos ie jsão usados ​​apenas dentro dos casos que os declaram. Infelizmente, em C ++, ele não é compilado: como Ciro Santilli explicou , simplesmente não podemos pular para case 2:, porque isso ignoraria a declaração com a inicialização de i, e mesmo que case 2não use i, isso ainda é proibido em C ++.

Curiosamente, com alguns ajustes (uma #ifdefpara #includeo cabeçalho apropriado e um ponto e vírgula após os rótulos, porque rótulos só podem ser seguidas por declarações e declarações não contam como declarações em C ), este programa faz compilação como 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;
    }
}

Graças a um compilador online como o http://rextester.com, você pode tentar compilá-lo rapidamente como C ou C ++, usando MSVC, GCC ou Clang. Como C, ele sempre funciona (lembre-se de definir STDIN!), Como C ++, nenhum compilador aceita.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.