Це відмінна практика.
Створюючи змінні всередині циклів, ви гарантуєте, що їх сфера дії обмежена всередині циклу. На нього не можна посилатись і не викликати поза циклу.
Сюди:
Якщо назва змінної трохи "generic" (наприклад, "i"), немає ризику змішати її з іншою такою ж змінною десь пізніше у вашому коді (також можна пом'якшити, використовуючи -Wshadow
інструкцію попередження про GCC)
Компілятор знає, що область змінної обмежена внутрішнім циклом, і тому видасть правильне повідомлення про помилку, якщо змінна помилково посилається в іншому місці.
І останнє, але не менш важливе, деяку виділену оптимізацію може виконувати компілятор більш ефективно (головне, щоб розподілити регістр), оскільки він знає, що змінну не можна використовувати поза циклом. Наприклад, не потрібно зберігати результат для подальшого повторного використання.
Коротше кажучи, ви праві це зробити.
Однак зауважте, що змінна не повинна зберігати своє значення між кожним циклом. У такому випадку вам може знадобитися ініціалізувати його кожен раз. Ви також можете створити більший блок, що охоплює цикл, єдиною метою якого є оголошення змінних, які повинні зберігати своє значення з одного циклу в інший. Зазвичай це включає сам лічильник циклу.
{
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 */
}
Для питання №2: Змінна виділяється один раз, коли функція викликається. Насправді, з точки зору розподілу, це (майже) те саме, що оголошення змінної на початку функції. Єдина відмінність - область застосування: змінна не може бути використана поза циклом. Можливо навіть, що змінна не виділяється, просто повторно використовуючи якийсь вільний слот (з іншої змінної, сфера дії якої закінчилася).
З обмеженою та точнішою сферою застосування приходять більш точні оптимізації. Але що ще важливіше, це робить ваш код більш безпечним, з меншими станами (тобто змінними), щоб турбуватися, читаючи інші частини коду.
Це справедливо навіть поза if(){...}
блоком. Зазвичай замість:
int result;
(...)
result = f1();
if (result) then { (...) }
(...)
result = f2();
if (result) then { (...) }
безпечніше писати:
(...)
{
int const result = f1();
if (result) then { (...) }
}
(...)
{
int const result = f2();
if (result) then { (...) }
}
Різниця може здатися незначною, особливо на такому невеликому прикладі. Але на велику базу коду, це допоможе: в даний час не існує жодного ризику транспортувати деякий result
значення з f1()
до f2()
блоку. Кожна result
суворо обмежена власною сферою, роблячи свою роль більш точною. З точки зору рецензента, це набагато приємніше, оскільки він має менші величини змінних стану, про які слід турбуватися та відстежувати.
Навіть компілятор допоможе краще: припустивши, що в майбутньому після деякої помилкової зміни коду result
не буде ініціалізовано належним чином f2()
. Друга версія просто відмовиться працювати, заявивши чітке повідомлення про помилку під час компіляції (набагато краще, ніж час виконання). Перша версія нічого не помітить, результат f1()
просто буде перевірений другий раз, плутаючи результат f2()
.
Додаткова інформація
Інструмент із відкритим кодом CppCheck ( інструмент статичного аналізу для C / C ++ коду) дає чудові підказки щодо оптимальної області змінних.
У відповідь на коментар щодо розподілу: Наведене вище правило справедливо для C, але може бути не для деяких класів C ++.
Для стандартних типів та структур розмір змінної відомий під час компіляції. У C немає такого поняття, як "побудова", тому простір для змінної просто буде виділено в стек (без будь-якої ініціалізації), коли функція викликається. Ось чому існує "нульова" вартість при оголошенні змінної всередині циклу.
Однак для класів C ++ є ця конструкторська річ, про яку я знаю набагато менше. Я думаю, що виділення, ймовірно, не буде проблемою, оскільки компілятор повинен бути досить розумним, щоб повторно використовувати той самий простір, але ініціалізація, ймовірно, відбуватиметься при кожній ітерації циклу.