Error: Ir a la etiqueta del caso

229

Escribí un programa que implica el uso de declaraciones de cambio ... Sin embargo, en la compilación muestra:

Error: salta a la etiqueta del caso.

¿Porque hace eso?

#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;
}
Codificador frustrado
fuente
1
¿Qué código estás tratando de compilar? Qué compilador estas usando? ¿Has encerrado cada casebloque entre llaves?
Cody Gray
2
ese es un impresionante mensaje de error indirecto
jozxyqk

Respuestas:

437

El problema es que las variables declaradas en una casesiguen siendo visibles en las siguientes cases a menos que se use un { }bloque explícito , pero no se inicializarán porque el código de inicialización pertenece a otra case.

En el siguiente código, si fooes igual a 1, todo está bien, pero si es igual a 2, usaremos accidentalmente la ivariable que sí existe pero que probablemente contenga basura.

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

Ajustar el caso en un bloque explícito resuelve el 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 más detalles, las switchdeclaraciones son solo un tipo particularmente elegante de a goto. Aquí hay un código analógico que muestra el mismo problema pero que usa un en gotolugar de un 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;
}
JohannesD
fuente
1
Vea este informe de error LLVM fijo para otras explicaciones: llvm.org/bugs/show_bug.cgi?id=7789
Francesco
70

La declaración de nuevas variables en declaraciones de casos es lo que causa problemas. El encerrar todas las casedeclaraciones {}limitará el alcance de las variables recientemente declaradas al caso en ejecución que resuelve el problema.

switch(choice)
{
    case 1: {
       // .......
    }break;
    case 2: {
       // .......
    }break;
    case 3: {
       // .......
    }break;
}    
Mahesh
fuente
instrucción solución más limpia
yc_yuy
¿Habrá algún problema si pongo la declaración de ruptura dentro de las llaves?
Vishal Sharma
10

C ++ 11 estándar al saltar sobre algunas inicializaciones

JohannesD dio una explicación, ahora para los estándares.

El borrador estándar C ++ 11 N3337 6.7 "Declaración de declaración" dice:

3 Es posible transferir a un bloque, pero no de una manera que omita las declaraciones con la inicialización. Un programa que salta (87) desde un punto donde una variable con duración de almacenamiento automático no está dentro del alcance hasta un punto donde está dentro del alcance está mal formado a menos que la variable tenga un tipo escalar, un tipo de clase con un constructor trivial predeterminado y un trivial destructor, una versión calificada para cv de uno de estos tipos, o una matriz de uno de los tipos anteriores y se declara sin un inicializador (8.5).

87) La transferencia de la condición de una declaración de cambio a una etiqueta de caso se considera un salto a este respecto.

[Ejemplo:

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
}

- ejemplo final]

A partir de GCC 5.2, el mensaje de error ahora dice:

cruza la inicialización de

C

C lo permite: c99 pasa a la inicialización

El borrador estándar del C99 N1256 Anexo I "Advertencias comunes" dice:

2 Un bloque con la inicialización de un objeto que tiene una duración de almacenamiento automática se salta a

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
6

La respuesta de JohannesD es correcta, pero creo que no está del todo claro sobre un aspecto del problema.

El ejemplo que da declara e inicializa la variable ien el caso 1, y luego trata de usarla en el caso 2. Su argumento es que si el cambio pasara directamente al caso 2, ise usaría sin inicializarse, y es por eso que hay una compilación error. En este punto, uno podría pensar que no habría problema si las variables declaradas en un caso nunca se usaran en otros casos. Por ejemplo:

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

Uno podría esperar que este programa para compilar, ya que ambos i, y jsólo se utilizan dentro de los casos que se declaran. Desafortunadamente, en C ++ no se compila: como explicó Ciro Santilli 包子 露 宪 六四 事件 法轮功 , simplemente no podemos saltar case 2:, porque esto omitiría la declaración con la inicialización de i, y aunque case 2no se usa ien absoluto, Esto todavía está prohibido en C ++.

Curiosamente, con algunos ajustes (una #ifdefa #includela cabecera apropiada, y un punto y coma después de las etiquetas, ya que las etiquetas sólo pueden ser seguidas por declaraciones y declaraciones no cuentan como las declaraciones en C ), este programa hace de compilación 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;
    }
}

Gracias a un compilador en línea como http://rextester.com , puede intentar compilarlo rápidamente como C o C ++, utilizando MSVC, GCC o Clang. Como C siempre funciona (¡solo recuerda configurar STDIN!), Como C ++ ningún compilador lo acepta.

Fabio dice reinstalar a Mónica
fuente