Esta es una excelente práctica.
Al crear variables dentro de los bucles, se asegura de que su alcance esté restringido al interior del bucle. No se puede hacer referencia ni llamar fuera del bucle.
De esta manera:
Si el nombre de la variable es un poco "genérico" (como "i"), no hay riesgo de mezclarlo con otra variable del mismo nombre más adelante en su código (también puede mitigarse usando las -Wshadow
instrucciones de advertencia en GCC)
El compilador sabe que el alcance de la variable está limitado al interior del bucle y, por lo tanto, emitirá un mensaje de error adecuado si la variable se menciona por error en otro lugar.
Por último, pero no menos importante, el compilador puede realizar una optimización dedicada de manera más eficiente (lo más importante es la asignación de registros), ya que sabe que la variable no se puede usar fuera del ciclo. Por ejemplo, no es necesario almacenar el resultado para su reutilización posterior.
En resumen, tienes razón en hacerlo.
Sin embargo, tenga en cuenta que no se supone que la variable retenga su valor entre cada ciclo. En tal caso, es posible que deba inicializarlo cada vez. También puede crear un bloque más grande, que abarque el ciclo, cuyo único propósito es declarar variables que deben retener su valor de un ciclo a otro. Esto generalmente incluye el contador de bucle en sí.
{
int i, retainValue;
for (i=0; i<N; i++)
{
int tmpValue;
/* tmpValue is uninitialized */
/* retainValue still has its previous value from previous loop */
/* Do some stuff here */
}
/* Here, retainValue is still valid; tmpValue no longer */
}
Para la pregunta # 2: la variable se asigna una vez, cuando se llama a la función. De hecho, desde una perspectiva de asignación, es (casi) lo mismo que declarar la variable al comienzo de la función. La única diferencia es el alcance: la variable no se puede usar fuera del bucle. Incluso puede ser posible que la variable no esté asignada, simplemente reutilizando algún espacio libre (de otra variable cuyo alcance ha finalizado).
Con un alcance restringido y más preciso vienen optimizaciones más precisas. Pero lo que es más importante, hace que su código sea más seguro, con menos estados (es decir, variables) de los que preocuparse al leer otras partes del código.
Esto es cierto incluso fuera de un if(){...}
bloque. Por lo general, en lugar de:
int result;
(...)
result = f1();
if (result) then { (...) }
(...)
result = f2();
if (result) then { (...) }
es más seguro escribir:
(...)
{
int const result = f1();
if (result) then { (...) }
}
(...)
{
int const result = f2();
if (result) then { (...) }
}
La diferencia puede parecer menor, especialmente en un ejemplo tan pequeño. Pero en una base de código más grande, ayudará: ahora no hay riesgo de transportar algún result
valor desde f1()
un f2()
bloque. Cada uno result
está estrictamente limitado a su propio alcance, lo que hace que su función sea más precisa. Desde la perspectiva del crítico, es mucho mejor, ya que tiene menos variables de estado de largo alcance de las que preocuparse y rastrear.
Incluso el compilador ayudará mejor: suponiendo que, en el futuro, después de algún cambio erróneo de código, result
no se inicialice correctamente f2()
. La segunda versión simplemente se negará a funcionar, indicando un mensaje de error claro en tiempo de compilación (mucho mejor que el tiempo de ejecución). La primera versión no detectará nada, el resultado f1()
simplemente se probará por segunda vez, confundiéndose con el resultado de f2()
.
Información complementaria
La herramienta de código abierto CppCheck (una herramienta de análisis estático para código C / C ++) proporciona algunos consejos excelentes sobre el alcance óptimo de las variables.
En respuesta al comentario sobre la asignación: la regla anterior es verdadera en C, pero podría no serlo para algunas clases de C ++.
Para los tipos y estructuras estándar, el tamaño de la variable se conoce en el momento de la compilación. No hay tal cosa como "construcción" en C, por lo que el espacio para la variable simplemente se asignará a la pila (sin ninguna inicialización), cuando se llama a la función. Es por eso que hay un costo "cero" al declarar la variable dentro de un bucle.
Sin embargo, para las clases de C ++, existe esta cosa de constructor de la que sé mucho menos. Supongo que la asignación probablemente no será el problema, ya que el compilador será lo suficientemente inteligente como para reutilizar el mismo espacio, pero es probable que la inicialización tenga lugar en cada iteración del bucle.