¿Puede un bucle 'for' dentro de un bucle 'for' usar el mismo nombre de variable de contador?

107

¿Puedo usar la misma variable de contador para un forbucle dentro de un forbucle?

¿O las variables se afectarán entre sí? ¿Debería el siguiente código usar una variable diferente para el segundo ciclo, como j, o está ibien?

for(int i = 0; i < 10; i++)
{
  for(int i = 0; i < 10; i++)
  {
  }
}
Uclydde
fuente
72
Es confuso, no me pasaría en una revisión de código. Pero es legítimo. Hay dos variables diferentes, ambas llamadas i, con diferentes alcances. Úselo -Wshadowcon GCC para que estos problemas se informen automáticamente.
Jonathan Leffler
15
Me sorprende que -Wshadowno esté incluido en -Wall.
izquierda rotonda alrededor del
5
@leftaroundabout también -Wshadowadvierte sobre el sombreado de variables globales, lo que fácilmente podría resultar molesto en proyectos más grandes.
Cubic
9
@leftaroundabout aún más sorprendentemente, incluso -Wextrano incluye -Wshadow. Supongo que es bastante común en algunos proyectos, o algunos desarrolladores de gcc adoran el sombreado como estilo de codificación, para justificar que se les deje de lado de esta manera.
hyde
5
@leftaroundabout Haciéndose eco de lo que dijo Cubic, -Wshadowtiene una horrenda tasa de falsos positivos, lo que lo vuelve completamente inútil. El alcance existe por una razón, y el sombreado no es a priori problemático. Ahora -Wshadow-local(nota: no -Wshadow=local ) es muy diferente. Pero desafortunadamente GCC se ha negado hasta ahora a incluirlo en el tronco (aunque parece haber bifurcaciones de GCC que lo incluyen).
Konrad Rudolph

Respuestas:

140

Puede utilizar el mismo nombre (identificador). Será un objeto diferente. No se afectarán entre sí. Dentro del bucle interno, no hay forma de hacer referencia al objeto utilizado en el bucle externo (a menos que haga disposiciones especiales para eso, como al proporcionarle un puntero).

Por lo general, este es un mal estilo, es propenso a la confusión y debe evitarse.

Los objetos son diferentes solo si el interior se define por separado, como con el int ique ha mostrado. Si se usa el mismo nombre sin definir un nuevo objeto, los bucles usarán el mismo objeto e interferirán entre sí.

Eric Postpischil
fuente
3
usando for (i) y for (j) anidado, y dentro de i ++, aumentará la variable de ciclo externo. Sin embargo, lo que dice es correcto si usa el mismo identificador en ambos bucles, porque son variables de ámbito diferente.
KYL3R
3
@BloodGain: "Objeto" es un término técnico utilizado en el estándar C. Lo usé deliberadamente aquí.
Eric Postpischil
1
@EricPostpischil: Ah, ya veo, sí. No estaba al tanto de esa definición en el estándar, y temía que pudiera inducir a error a los programadores nuevos (ya que esta es claramente una pregunta para principiantes), ya que C no tiene "objetos" en el sentido en que generalmente usamos el término. Lo veo en el estándar C11, y ahora tengo curiosidad por saber si se definió de esa manera antes de C11.
Bloodgain
1
Era. Es 3.14 en el estándar C99, en lugar de 3.15. Así que no hay excusa de mi parte. Eso me enseñará a cuestionarte <: - |
Bloodgain
1
De manera más general: no hay nada que le impida reutilizar un nombre de variable en cualquier ámbito anidado. Excepto, por supuesto, el miedo al castigo de Dios por escribir un código confuso.
Isaac Rabinovitch
56

Primero, esto es absolutamente legal: el código se compilará y ejecutará, repitiendo el cuerpo del bucle anidado 10 × 10 = 100 veces. El contador de bucle identro del bucle anidado ocultará el contador del bucle exterior, por lo que los dos contadores se incrementarán independientemente uno del otro.

Dado que el exterior iestá oculto, el código dentro del cuerpo del ciclo anidado tendría acceso solo al valor del iciclo anidado, no idesde el ciclo exterior. En situaciones en las que el bucle anidado no necesita acceso al exteriori dicho código podría ser perfectamente justificable. Sin embargo, es probable que esto cree más confusión en sus lectores, por lo que es una buena idea evitar escribir ese código para evitar "responsabilidades de mantenimiento".

Nota: A pesar de que las variables de control de ambos bucles tienen el mismo identificador i, siguen siendo dos variables independientes, es decir, que está no utiliza la misma variable en ambos bucles. También es posible usar la misma variable en ambos bucles, pero el código sería difícil de leer. Aquí hay un ejemplo:

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

Ahora ambos bucles usan la misma variable. Sin embargo, lleva un tiempo averiguar qué hace este código sin compilarlo ( demostración );

dasblinkenlight
fuente
4
Dado que la pregunta está redactada como "utilizando la misma variable de contador", también me gustaría señalar que el sombreado solo tiene lugar cuando se produce la redefinición. Omitir el intbucle for interno, es decir, usar la misma variable de contador, hará que el bucle externo solo se ejecute una vez, ya que el bucle interno se irá i == 10. Esto es trivial, pero
creo
@EastonBornemeier Tiene razón, pensé que debería abordar el problema de "la misma variable" en el cuerpo de la respuesta. ¡Gracias!
dasblinkenlight
@EricPostpischil "Sombreado variable" es un término oficial, completo con su propia página en wikipedia . Sin embargo, he actualizado la respuesta para que sea coherente con la redacción del estándar. ¡Gracias!
dasblinkenlight
2
@dasblinkenlight: En realidad, tuve un espasmo cerebral sobre la dirección, y el nombre interno ensombrece el nombre externo. Mi comentario anterior estaba equivocado en ese sentido. Mis disculpas. (Sin embargo, eso es en un sentido inglés, no en un sentido oficial; Wikipedia no es una publicación oficial para C o programación en general, y no tengo conocimiento de ninguna oficina u organismo autorizado que defina el término). El estándar C usa "Esconderse", por lo que es preferible.
Eric Postpischil
Agradable, especialmente con el ejemplo de la "misma variable". Sin embargo, creo que " el código se compilará y se ejecutará como se esperaba " sería mejor ya que "el código se compilará y se ejecutará como alguien que lo ha leído detenidamente y comprende todas las ramificaciones esperadas" ... como usted dice, un código como este " es probable que cree más confusión en sus lectores "y el problema es que un lector confundido puede esperar algo diferente de lo que hace.
TripeHound
26

Usted puede. Pero debe tener en cuenta el alcance de la is. si llamamos al exterior icon i_1y al interior icon i_2, el alcance de la is es el siguiente:

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
}

Debe notar que no se afectan entre sí, y solo su alcance de definición es diferente.

Dios mio
fuente
17

Eso es completamente posible, pero tenga en cuenta que no podrá abordar la primera i declarada

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

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

    }
}

en el segundo ciclo dentro del segundo ciclo secundario

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

si necesita ajustar u obtener el valor de la primera i, use j en el segundo ciclo

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

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

    }
}

y si eres lo suficientemente creativo, puedes hacer ambos en un ciclo

for(int i ,j= 0; i < 10; (j>9) ? (i++,j=0) : 0 ,j++)
{
    printf("%d %d\n",i,j);
}
Vejestorio
fuente
6
Si capté variables i sombreadas en bucles anidados durante una revisión de código, lo vería como una oportunidad de entrenamiento. Si atrapo a alguien ofuscando el bucle interno como su último ejemplo (que NO es un bucle), podría arrojarlo por la ventana.
Bloodgain
es un bucle, solo tiene un bucle for , si fuera 2 tendría dos para palabras clave o dos palabras clave while o palabras clave for y while
Dodo
3
Por eso dije que ofuscaste el bucle. Todavía estás en bucle, lo has ocultado con una sintaxis menos obvia. Y es peor en todos los sentidos.
Bloodgain
12

Sí, puede usar el mismo nombre de variable de contador para un forbucle interno que para el forbucle externo .

Desde el bucle for :

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
La declaración de expresión utilizada como loop_statement establece su propio alcance de bloque, distinto del alcance de init_clause .

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

El alcance de loop_statement está anidado dentro del alcance de init_clause .

De C Standards # 6.8.5p5 Declaraciones de iteración [el énfasis es mío]

Una instrucción de iteración es un bloque cuyo alcance es un subconjunto estricto del alcance de su bloque adjunto. El cuerpo del bucle también es un bloque cuyo alcance es un subconjunto estricto del alcance de la declaración de iteración .

De C Standards # 6.2.1p4 Alcances de identificadores [énfasis mío]

.... Dentro del alcance interno, el identificador designa la entidad declarada en el alcance interno; la entidad declarada en el alcance externo está oculta (y no visible) dentro del alcance interno.

HS
fuente
10

Desde la perspectiva del código / compilador, esto sería perfectamente válido y legal. El int ideclarado en el for(int i = 0; i < 10; i++)bucle interno está en un alcance nuevo y más pequeño, de modo que la declaración ensombrece la declaración de int ien el bucle externo (o, con otras palabras: en el alcance interno todos los accesos a la variable ivan al int ideclarado en el alcance interno, dejando int iintacto el en el alcance externo).

Dicho esto, desde la perspectiva de la calidad del código, esto es absolutamente horrible. Es difícil de leer, difícil de entender y fácil de malinterpretar. No lo hagas.

CaronteX
fuente
8

Sí, puedes usarlo, pero es bastante confuso. Lo más importante es el alcance de la variable local dentro del ciclo. En cuanto a si una variable se declara dentro de una función, el alcance de esa variable es esa función.

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

De manera similar, en el caso de los bucles, la variable declarada dentro del bucle interno tiene un alcance diferente y el bucle externo declarado de la variable tiene un alcance 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
}

El mejor enfoque es utilizar diferentes variables para bucles internos y externos.

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

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

    }

}
Safwan Shaikh
fuente
8

Sí, definitivamente puedes usar la misma variable de nombre.

Las variables de programación C se pueden declarar en tres lugares:
Variables locales: -Dentro de una función o un bloque.
Variables globales: -Fuera de todas las funciones.
Parámetros formales: -En los parámetros de la función.

Pero en tu caso i scopetendrás que preocuparte debajo de cosas

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
}

Nota: sería una buena práctica utilizar diferentes variables para los bucles internos y externos

Zaynul Abadin Tuhin
fuente
6

Sí, y lo que es más interesante, puede reutilizar un nombre de variable cada vez que abre un conjunto de llaves. Esto suele ser útil al insertar un código de diagnóstico. Escriba una llave abierta '{' seguida de una declaración y uso de variables, luego cierre la llave y las variables desaparecerán. Esto garantiza que no interferirá con nada en el cuerpo principal mientras conserva la ventaja de las variables, clases y métodos declarados fuera de las llaves.

SuwaneeCreek
fuente
3

Regla de alcance: una variable declarada en una instrucción for solo se puede usar en esa instrucción y el cuerpo del bucle.

Si en su código ha definido varias instancias de i en bucles internos, cada instancia ocupará su propio espacio de memoria. Así que no hay nada de qué preocuparse por los resultados de todos modos, sería lo mismo.

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

Pero no se recomienda utilizar el mismo nombre de variable, ya que es difícil de entender y luego se convierte en código no mantenible.

Subash J
fuente
1

La parte importante es que el parámetro de bucle interno contiene int i. Debido a que ise redefine de esta manera, las dos variables no se afectan entre sí; sus alcances son diferentes. Aquí hay dos ejemplos para mostrar esto:

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

Tenga en cuenta que el código anterior incluye int ien el parámetro de bucle interno y el código siguiente solo incluye i.

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

Bueno, puedes hacer esto sin que tus scripts tengan problemas, pero debes evitar esa estructura. Suele generar confusión

Hoguera
fuente