Eu sempre me perguntei isso - por que você não pode declarar variáveis após um rótulo de caso em uma instrução switch? Em C ++, você pode declarar variáveis praticamente em qualquer lugar (e declará-las próximas ao primeiro uso é obviamente uma coisa boa), mas o seguinte ainda não funcionará:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
O texto acima fornece o seguinte erro (MSC):
a inicialização de 'newVal' é ignorada pelo rótulo 'case'
Isso também parece ser uma limitação em outros idiomas. Por que isso é um problema?
c++
switch-statement
Roubar
fonte
fonte
Respostas:
Case
declarações são apenas rótulos . Isso significa que o compilador interpretará isso como um salto diretamente para o rótulo. Em C ++, o problema aqui é de escopo. Seus colchetes definem o escopo como tudo dentro daswitch
instrução. Isso significa que você terá um escopo no qual um salto será executado ainda mais no código, ignorando a inicialização.A maneira correta de lidar com isso é definir um escopo específico para essa
case
instrução e definir sua variável nela:fonte
Esta questão
éoriginalmente foi etiquetado como [C] e [C ++], ao mesmo tempo. O código original é realmente inválido em C e C ++, mas por razões não relacionadas completamente diferentes.No C ++, esse código é inválido porque o
case ANOTHER_VAL:
rótulo salta para o escopo da variávelnewVal
ignorando sua inicialização. Saltos que ignoram a inicialização de objetos automáticos são ilegais no C ++. Este lado do problema é abordado corretamente pela maioria das respostas.No entanto, na linguagem C, ignorar a inicialização de variáveis não é um erro. Saltar para o escopo de uma variável sobre sua inicialização é legal em C. Isso significa simplesmente que a variável é deixada não inicializada. O código original não é compilado em C por um motivo completamente diferente. A etiqueta
case VAL:
no código original é anexada à declaração da variávelnewVal
. Na linguagem C, as declarações não são declarações. Eles não podem ser rotulados. E é isso que causa o erro quando esse código é interpretado como código C.A adição de um
{}
bloco extra corrige os problemas de C ++ e C, mesmo que esses problemas sejam muito diferentes. No lado do C ++, ele restringe o escopo denewVal
, certificando-se de quecase ANOTHER_VAL:
não entra mais nesse escopo, o que elimina o problema do C ++. No lado C, extra{}
introduz uma declaração composta, fazendo com que ocase VAL:
rótulo se aplique a uma declaração, o que elimina a emissão de C.No caso C, o problema pode ser facilmente resolvido sem o
{}
. Basta adicionar uma declaração vazia após ocase VAL:
rótulo e o código se tornará válidoObserve que, embora agora seja válido do ponto de vista de C, ele permanece inválido do ponto de vista de C ++.
Simetricamente, no caso de C ++, o problema pode ser facilmente resolvido sem o
{}
. Apenas remova o inicializador da declaração da variável e o código se tornará válidoObserve que, embora agora seja válido do ponto de vista do C ++, ele permanece inválido do ponto de vista do C.
fonte
newVal
quando saltaANOTHER_VAL
?case ANOTHER_VAL:
ponto, a variávelnewVal
é visível, mas com valor indeterminado.§A9.3: Compound Statement
K&R C (segunda edição). A entrada mencionou a definição técnica de uma declaração composta que é{declaration-list[opt] statement-list[opt]}
. Confuso, porque pensei que uma declaração ERA uma declaração, procurei-a e imediatamente encontrei esta pergunta, um exemplo em que a disparidade dita se torna aparente e realmente quebra um programa. Acredito que outra solução (para C) seria colocar outra declaração (possivelmente uma declaração nula?) Antes da declaração para que a declaração rotulada seja satisfeita.Está bem. Só para esclarecer isso, nada tem a ver com a declaração. Diz respeito apenas a "pular a inicialização" (ISO C ++ '03 6.7 / 3)
Muitas postagens aqui mencionaram que pular a declaração pode resultar na variável "não sendo declarada". Isso não é verdade. Um objeto POD pode ser declarado sem um inicializador, mas terá um valor indeterminado. Por exemplo:
Onde o objeto é um não-POD ou agregado, o compilador adiciona implicitamente um inicializador e, portanto, não é possível pular essa declaração:
Essa limitação não está limitada à instrução switch. Também é um erro usar 'goto' para pular uma inicialização:
Um pouco de trivialidade é que essa é uma diferença entre C ++ e C. Em C, não é um erro pular a inicialização.
Como outros já mencionaram, a solução é adicionar um bloco aninhado para que o tempo de vida da variável seja limitado ao rótulo de caso individual.
fonte
Toda a instrução switch está no mesmo escopo. Para contornar isso, faça o seguinte:
Observe os colchetes.
fonte
Depois de ler todas as respostas e mais algumas pesquisas, recebo algumas coisas.
Em C, de acordo com a especificação,
§6.8.1 Declarações rotuladas:
Em C, não há nenhuma cláusula que permita uma "declaração rotulada". Simplesmente não faz parte do idioma.
assim
Isso não será compilado , consulte http://codepad.org/YiyLQTYw . O GCC está dando um erro:
Até
isso também não está sendo compilado , consulte http://codepad.org/BXnRD3bu . Aqui também estou recebendo o mesmo erro.
Em C ++, de acordo com a especificação,
A declaração etiquetada é permitida, mas a inicialização rotulada não é permitida.
Veja http://codepad.org/ZmQ0IyDG .
A solução para essa condição é dois
Use o novo escopo usando {}
Ou use uma declaração fictícia com etiqueta
Declare a variável antes de switch () e inicialize-a com valores diferentes na instrução case, se ela atender aos seus requisitos
Mais algumas coisas com a instrução switch
Nunca escreva nenhuma declaração no comutador que não faça parte de nenhum rótulo, porque elas nunca serão executadas:
Veja http://codepad.org/PA1quYX3 .
fonte
a
no escopo da variávela
. Portanto, do ponto de vista de C, o problema está nocase VAL:
rótulo e você o descreveu corretamente. Mas do ponto de vista do C ++, o problema está nocase ANOTHER_VAL:
rótulo.Você não pode fazer isso, porque os
case
rótulos são na verdade apenas pontos de entrada no bloco que o contém.Isso é mais claramente ilustrado pelo dispositivo de Duff . Aqui está um código da Wikipedia:
Observe como os
case
rótulos ignoram totalmente os limites do bloco. Sim, isso é mau. Mas é por isso que seu exemplo de código não funciona. Saltar para umcase
rótulo é o mesmo que usargoto
, portanto, você não pode saltar sobre uma variável local com um construtor.Como vários outros pôsteres indicaram, você precisa colocar um bloco de sua preferência:
fonte
SAR
em x86, versus oSHR
que é para turnos não assinados).A maioria das respostas até agora estão erradas em um aspecto: você pode declarar variáveis após a instrução case, mas não pode inicializá-las:
Como mencionado anteriormente, uma boa maneira de contornar isso é usar chaves para criar um escopo para o seu caso.
fonte
Meu truque favorito de troca de mal é usar um if (0) para pular um rótulo de caixa indesejado.
Mas muito mal.
fonte
goto
semTente o seguinte:
fonte
Você pode declarar variáveis em uma instrução switch se iniciar um novo bloco:
O motivo está relacionado à alocação (e recuperação) de espaço na pilha para armazenamento das variáveis locais.
fonte
Considerar:
Na ausência de instruções de interrupção, às vezes o newVal é declarado duas vezes e você não sabe se o faz até o tempo de execução. Meu palpite é que a limitação se deve a esse tipo de confusão. Qual seria o escopo do newVal? A Convenção ditaria que seria o bloco inteiro do interruptor (entre os aparelhos).
Eu não sou programador C ++, mas em C:
Funciona bem. Declarar uma variável dentro de um bloco de chave é bom. Declarar após uma guarda de caso não é.
fonte
A seção inteira do switch é um único contexto de declaração. Você não pode declarar uma variável em uma declaração de caso como essa. Tente isso:
fonte
Se o seu código diz "int newVal = 42", você esperaria razoavelmente que o newVal nunca seja não inicializado. Mas se você passar por cima dessa declaração (que é o que você está fazendo), é exatamente isso que acontece - newVal está no escopo, mas não foi atribuído.
Se é isso que você realmente queria que acontecesse, o idioma exige que seja explicitado dizendo "int newVal; newVal = 42;". Caso contrário, você pode limitar o escopo de newVal ao caso único, o que é mais provável do que você queria.
Pode esclarecer as coisas se você considerar o mesmo exemplo, mas com "const int newVal = 42;"
fonte
Eu só queria enfatizar o ponto de Slim . Uma construção de switch cria um escopo completo para o cidadão de primeira classe. Portanto, é possível declarar (e inicializar) uma variável em uma instrução switch antes do primeiro rótulo do caso, sem um par de colchetes adicional:
fonte
int newVal
será executada, mas não a= 42
atribuição.Até agora, as respostas foram para C ++.
Para C ++, você não pode pular uma inicialização. Você pode em C. No entanto, em C, uma declaração não é uma declaração e os rótulos de caso devem ser seguidos por declarações.
Então, C válido (mas feio), C ++ inválido
Por outro lado, em C ++, uma declaração é uma declaração, portanto, o seguinte é C ++ válido, C inválido
fonte
Interessante que isso seja bom:
... mas isso não é:
Entendo que uma correção é bastante simples, mas ainda não estou entendendo por que o primeiro exemplo não incomoda o compilador. Como foi mencionado anteriormente (2 anos antes hehe), a declaração não é o que causa o erro, apesar da lógica. Inicialização é o problema. Se a variável for inicializada e declarada nas diferentes linhas, ela será compilada.
fonte
Eu escrevi esta resposta original para esta pergunta . No entanto, quando terminei, descobri que a resposta estava fechada. Então, eu publiquei aqui, talvez alguém que goste de referências ao padrão ache isso útil.
Código original em questão:
Na verdade, existem 2 perguntas:
1. Por que posso declarar uma variável após o
case
rótulo?É porque no rótulo C ++ deve estar no formato:
N3337 6.1 / 1
E na
C++
declaração, a declaração também é considerada como declaração (em oposição aC
):N3337 6/1:
2. Por que posso pular a declaração de variável e depois usá-la?
Porque: N3337 6,7 / 3
Como
k
é do tipo escalar e não é inicializado no ponto da declaração, é possível saltar sobre sua declaração. Isso é semanticamente equivalente:No entanto, isso não seria possível, se
x
foi inicializado no ponto da declaração:fonte
Novas variáveis podem ser decalcadas apenas no escopo do bloco. Você precisa escrever algo como isto:
Obviamente, o newVal só tem escopo dentro do aparelho ...
Cheers, Ralph
fonte
Um
switch
bloco não é o mesmo que uma sucessão deif/else if
blocos.Estou surpreso que nenhuma outra resposta explique claramente.Considere esta
switch
declaração:Pode ser surpreendente, mas o compilador não o verá como simples
if/else if
. Ele produzirá o seguinte código:As
case
instruções são convertidas em rótulos e depois chamadas comgoto
. Os colchetes criam um novo escopo e é fácil ver agora por que você não pode declarar duas variáveis com o mesmo nome em umswitch
bloco.Pode parecer estranho, mas é necessário oferecer suporte a falhas (ou seja, não usar
break
para permitir que a execução continue na próximacase
).fonte
Acredito que o problema em questão é que a declaração foi ignorada e você tentou usar o var em outro lugar, não seria declarado.
fonte
newVal existe em todo o escopo do comutador, mas só é inicializado se o membro VAL for atingido. Se você criar um bloco ao redor do código em VAL, deverá estar OK.
fonte
O padrão C ++ possui: É possível transferir para um bloco, mas não de maneira a ignorar as declarações com a inicialização. Um programa que salta de um ponto em que uma variável local 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 o tipo POD (3.9) e seja declarada sem um inicializador (8.5).
O código para ilustrar esta regra:
O código para mostrar o efeito inicializador:
fonte
Parece que objetos anônimos podem ser declarados ou criados em uma instrução de caso de opção pelo motivo de não poderem ser referenciados e, como tal, não passarem para o próximo caso. Considere este exemplo de compilação no GCC 4.5.3 e no Visual Studio 2008 (pode ser um problema de conformidade, por isso os especialistas devem avaliar)
fonte
const
referência com escopo próprio). É uma expressão que vive e morre dentro de sua declaração (onde quer que esteja). Portanto, é totalmente irrelevante.Foo();
não é uma declaração; a questão é sobre declarações.