Um loop 'for' dentro de um loop 'for' pode usar o mesmo nome de variável de contador?

107

Posso usar a mesma variável de contador para um for loop dentro de um forloop?

Ou as variáveis ​​afetarão umas às outras? O código a seguir deve usar uma variável diferente para o segundo loop, como j, ou está ibem?

for(int i = 0; i < 10; i++)
{
  for(int i = 0; i < 10; i++)
  {
  }
}
Uclydde
fonte
72
É confuso - não passaria por mim em uma revisão de código. Mas é legítimo. Existem duas variáveis ​​diferentes, ambas chamadas i, com escopos diferentes. Use -Wshadowcom o GCC para obter esses problemas relatados automaticamente.
Jonathan Leffler
15
Estou surpreso que -Wshadownão esteja incluído em -Wall.
esquerda por volta de
5
@leftaroundabout -Wshadowavisa sobre o sombreamento de variáveis ​​globais também, o que pode facilmente se tornar irritante em projetos maiores.
Cubic
9
@leftaroundabout é ainda mais surpreendente, mesmo -Wextranão inclui -Wshadow. Eu acho que é comum o suficiente em alguns projetos, ou algum desenvolvedor gcc adora sombreamento como estilo de codificação, para garantir ser deixado de fora assim.
hyde
5
@leftaroundabout Ecoando o que Cubic disse, -Wshadowtem uma taxa de falsos positivos horrenda, tornando-o completamente inútil. O escopo existe por uma razão, e o sombreamento não é problemático a priori . Agora -Wshadow-local(nota: não -Wshadow=local ) é muito diferente. Mas, infelizmente, o GCC até agora se recusou a incluí-lo no tronco (embora pareça haver forks do GCC que o incluem).
Konrad Rudolph

Respostas:

140

Você pode usar o mesmo nome (identificador). Será um objeto diferente. Eles não afetarão um ao outro. Dentro do loop interno, não há como se referir ao objeto usado no loop externo (a menos que você faça provisões especiais para isso, fornecendo um ponteiro para ele).

Este é geralmente um estilo ruim, é propenso a confusão e deve ser evitado.

Os objetos são diferentes apenas se o interno for definido separadamente, como com o que int ivocê mostrou. Se o mesmo nome for usado sem definir um novo objeto, os loops usarão o mesmo objeto e irão interferir uns nos outros.

Eric Postpischil
fonte
3
usar for (i) e for (j) aninhado e dentro de i ++ aumentará a variável de loop externo. No entanto, o que você diz está correto se usar o mesmo identificador em ambos os loops, porque são variáveis ​​com escopos diferentes.
KYL3R
3
@BloodGain: “Objeto” é um termo técnico usado no padrão C. Usei deliberadamente aqui.
Eric Postpischil
1
@EricPostpischil: Ah, entendo, sim. Eu não estava ciente dessa definição no padrão e temia que fosse enganosa para novos programadores (já que esta é uma questão muito clara para iniciantes), uma vez que C não tem "objetos" no sentido que geralmente usamos o termo. Eu vejo isso no padrão C11, e agora estou curioso para saber se era definido dessa forma antes do C11.
Bloodgain
1
Isso foi. É 3,14 no padrão C99, em vez de 3,15. Portanto, não há desculpa da minha parte. Isso vai me ensinar a questionar você <: - |
Bloodgain
1
Mais genericamente: não há nada que o impeça de reutilizar um nome de variável em qualquer escopo aninhado. Exceto, é claro, o medo da punição de Deus por escrever códigos confusos.
Isaac Rabinovitch
56

Em primeiro lugar, isso é absolutamente legal: o código será compilado e executado, repetindo o corpo do loop aninhado 10 × 10 = 100 vezes. O contador de loop identro do loop aninhado ocultará o contador do loop externo, de modo que os dois contadores seriam incrementados independentemente um do outro.

Visto que o externo iestá oculto, o código dentro do corpo do loop aninhado teria acesso apenas ao valor de ido loop aninhado, não ido loop externo. Em situações em que o loop aninhado não precisa de acesso ao icódigo externo, esse código pode ser perfeitamente justificável. No entanto, isso provavelmente criará mais confusão em seus leitores, portanto, é uma boa ideia evitar escrever esse código para evitar "responsabilidades de manutenção".

Nota: Mesmo que as variáveis ​​de contador de ambos os loops tenham o mesmo identificador i, elas permanecem como duas variáveis ​​independentes, ou seja, você não está usando a mesma variável em ambos os loops. Usar a mesma variável em ambos os loops também é possível, mas o código seria difícil de ler. Aqui está um exemplo:

for (int i = 1 ; i < 100 ; i++) {
    for ( ; i % 10 != 0 ; i++) {
        printf("%02d ", i);
    }
    printf("%d\n", i);
}

Agora, os dois loops usam a mesma variável. No entanto, leva um tempo para descobrir o que esse código faz sem compilá-lo ( demo );

dasblinkenlight
fonte
4
Uma vez que a pergunta é formulada como "usando a mesma variável de contador", também gostaria de salientar que o sombreamento só ocorre quando ocorre a redefinição. Omitir o intloop for interno, ou seja, usar realmente a mesma variável de contador, fará com que o loop externo execute apenas uma vez, pois o loop interno sairá i == 10. Isso é trivial, mas
acho
@EastonBornemeier Você está certo, achei que deveria abordar a questão da "mesma variável" no corpo da resposta. Obrigado!
dasblinkenlight
@EricPostpischil "Variable shadowing" é um termo oficial, completo com sua própria página na wikipedia . Porém, eu atualizei a resposta para ser consistente com o texto da norma. Obrigado!
dasblinkenlight
2
@dasblinkenlight: Na verdade, tive um espasmo cerebral sobre a direção, e o nome interno sombreia o nome externo. Meu comentário anterior estava errado a esse respeito. Me desculpe. (No entanto, isso está no sentido inglês, não no sentido oficial - a Wikipedia não é uma publicação oficial para C ou programação em geral, e eu não tenho conhecimento de nenhum escritório ou órgão autorizado que defina o termo.) O padrão C usa “Esconder”, então isso é preferível.
Eric Postpischil
Legal, especialmente com o exemplo da "mesma variável". No entanto, acho que " o código será compilado e executado conforme o esperado " seria melhor, pois "o código será compilado e executado como alguém que o leu cuidadosamente e entende todas as ramificações esperadas" ... como você diz, um código como este " é provável que crie mais confusão em seus leitores "e o problema é que um leitor confuso pode esperar algo diferente do que ele faz.
TripeHound
26

Você pode. Mas você deve estar ciente do escopo dos is. se chamarmos o externo icom i_1e o interno icom i_2, o escopo do is é o seguinte:

for(int i = 0; i < 10; i++)
{
     // i means i_1
     for(int i = 0; i < 10; i++)
     {
        // i means i_2
     }
     // i means i_1
}

Você deve notar que eles não afetam um ao outro, e apenas seu escopo de definição é diferente.

AMD
fonte
17

Isso é completamente possível, mas tenha em mente que você não será capaz de resolver o primeiro i declarado

for(int i = 0; i < 10; i++)//I MEAN THE ONE HERE
{

  for(int i = 0; i < 10; i++)
    {

    }
}

no segundo loop dentro do segundo loop filho

for(int i = 0; i < 10; i++)
{

  for(int i = 0; i < 10; i++)//the new i
    {
        // i cant see the i thats before this new i here
    }
}

se você precisar ajustar ou obter o valor do primeiro i, use j no segundo loop

for(int i = 0; i < 10; i++)
{

  for(int j = 0; j < 10; j++)
    {

    }
}

e se for criativo o suficiente, você pode fazer os dois em um ciclo

for(int i ,j= 0; i < 10; (j>9) ? (i++,j=0) : 0 ,j++)
{
    printf("%d %d\n",i,j);
}
Dodo
fonte
6
Se eu pegasse variáveis ​​sombreadas em loops aninhados durante uma revisão de código, veria isso como uma oportunidade de treinamento. Se eu pegar alguém ofuscando o loop interno como seu último exemplo (que NÃO é um loop), posso jogá-lo pela janela.
Bloodgain
é um loop, ele tem apenas um para loop, se fosse 2 teria dois para palavras-chave ou dois, enquanto palavras-chave ou um para e enquanto palavras-chave
Dodo
3
É por isso que eu disse que você ofuscou o loop. Você ainda está em loop, acabou de ocultá-lo com uma sintaxe menos óbvia. E é pior em todos os sentidos.
Bloodgain
12

Sim, você pode usar o mesmo nome de variável de contador para um forloop interno e para o externofor .

De loop for :

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
A instrução de expressão usada como loop_statement estabelece seu próprio escopo de bloco, distinto do escopo de init_clause .

for (int i = 0; ; ) {
    long i = 1;   // valid C, invalid C++
    // ...
}  

O escopo de loop_statement está aninhado dentro do escopo de init_clause .

Dos Padrões C # 6.8.5p5 Declarações de iteração [ênfase minha]

Uma instrução de iteração é um bloco cujo escopo é um subconjunto estrito do escopo de seu bloco envolvente. O corpo do loop também é um bloco cujo escopo é um subconjunto estrito do escopo da instrução de iteração .

Dos padrões C # 6.2.1p4 Escopos de identificadores [ênfase minha]

.... No escopo interno, o identificador designa a entidade declarada no escopo interno; a entidade declarada no escopo externo está oculta (e não visível) dentro do escopo interno.

HS
fonte
10

Do ponto de vista do código / compilador, isso seria uma coisa perfeitamente válida e legal de se fazer. O int ideclarado no for(int i = 0; i < 10; i++)loop interno está em um escopo novo e menor, de modo que a declaração obscurece a declaração de int ino loop externo (ou, com outras palavras: No escopo interno todos os acessos à variável ivão para o int ideclarado no escopo interno, deixando o int ino escopo externo intocado).

Dito isso, da perspectiva da qualidade do código, isso é totalmente horrível. É difícil de ler, difícil de entender e fácil de interpretar mal. Não faça isso.

CharonX
fonte
8

Sim, você pode usar, mas é muito confuso. O mais importante é o escopo da variável local dentro do loop. No que diz respeito a se uma variável for declarada dentro de uma função, o escopo dessa variável é essa função.

int a = 5;
// scope of a that has value 5
int func(){
    int a = 10;
   // scope of a that has value 10
}
// scope of a that has value 5

Da mesma forma, no caso dos loops, a variável declarada dentro do loop interno tem escopo diferente e a variável declarada no loop externo tem escopo diferente.

for(int i = 0; i < 10; i++){
    // In first iteration, value of i is 0

    for(int i = 1; i < 10; i++){
        // In first iteration, value of i is 1
    }
    // In first iteration, value of i is 0
}

A melhor abordagem é usar variáveis ​​diferentes para loops internos e externos.

for(int i = 0; i < 10; i++){

    for(int j = 1; j < 10; j++){

    }

}
Safwan Shaikh
fonte
8

Sim, definitivamente você pode usar a mesma variável de nome.

As variáveis ​​de programação C podem ser declaradas em três lugares:
variáveis ​​locais: -Dentro de uma função ou bloco.
Variáveis ​​globais: - Fora de todas as funções.
Parâmetros formais: -Nos parâmetros da função.

Mas no seu caso i scopeterá que se preocupar com as coisas abaixo

for(int i = 0; i < 10; i++)
{
     // i means 1st for loop variable
     for(int i = 0; i < 10; i++)
     {
        // but here i means 2nd for loop  variable
     }
     //interesting thing here i means 1st for loop variable
}

Observação: seria uma prática recomendada usar variáveis ​​diferentes para loops internos e externos

Zaynul Abadin Tuhin
fonte
6

Sim - e ainda mais interessante, você pode reutilizar um nome de variável sempre que abrir um conjunto de colchetes. Isso geralmente é útil ao inserir o código de diagnóstico. Digite uma chave aberta '{' seguido pela declaração e uso de variáveis, então feche a chave e as variáveis ​​vão embora. Isso garante que você não interferirá em nada no corpo principal, ao mesmo tempo em que retém a vantagem de quaisquer variáveis, classes e métodos declarados fora das chaves.

SuwaneeCreek
fonte
3

Regra de escopo: uma variável declarada em uma instrução for só pode ser usada nessa instrução e no corpo do loop.

Se em seu código você definiu múltiplas instâncias de i em loops internos, cada instância ocupará seu próprio espaço de memória. Portanto, não há nada para se preocupar com os resultados de qualquer maneira, seria o mesmo.

int main(void) {

    int i = 2; //defined with file global scope outside of a function and will remain 2
    if(1)
    {       //new scope, variables created here with same name are different
        int i = 5;//will remain == 5
        for(int i = 0; i < 10; i++)
        {   //new scope for "i"

            printf("i value in first loop: %d \n", i); // Will print 0 in first iteration
            for(int i = 8; i < 15; i++) 
            {   //new scope again for "i", variable with same name is not the same
                printf("i value in nested loop: %d \n", i); // Will print 8 in first iteration
            }
        }

    }

    return 0;
}

Mas não é recomendado usar o mesmo nome de variável, pois é difícil de entender e se torna um código insustentável posteriormente.

Subash J
fonte
1

A parte importante é que o parâmetro do loop interno contém int i. Por iser redefinido dessa forma, as duas variáveis ​​não se afetam; seus escopos são diferentes. Aqui estão dois exemplos para mostrar isso:

for(int i = 0; i < 10; i++) // This code will print "Test" 100 times
{
 for(int i = 0; i < 10; i++)
 {
  puts("Test");
 }
}

Observe que o código acima inclui int io parâmetro de loop interno e o código abaixo apenas inclui i.

for(int i = 0; i < 10; i++) // This code will print "Test" 10 times
{
 for(i = 0; i < 10; i++)
 {
  puts("Test");
 }
}
Uclydde
fonte
0

Bem, você pode fazer isso sem que seus scripts tenham problemas, mas você deve evitar essa estrutura. Geralmente leva a confusão

Fogueira
fonte