Esta é uma excelente prática.
Ao criar variáveis dentro de loops, você garante que seu escopo seja restrito a dentro do loop. Não pode ser referenciado nem chamado fora do loop.
Deste jeito:
Se o nome da variável for um pouco "genérico" (como "i"), não há risco de misturá-la com outra variável com o mesmo nome em algum lugar posteriormente no seu código (também pode ser mitigado usando a -Wshadowinstrução de aviso no GCC)
O compilador sabe que o escopo da variável está limitado ao interior do loop e, portanto, emitirá uma mensagem de erro adequada se a variável for referenciada por engano em outro local.
Por último, mas não menos importante, alguma otimização dedicada pode ser executada com mais eficiência pelo compilador (o mais importante é alocar o registro), pois sabe que a variável não pode ser usada fora do loop. Por exemplo, não há necessidade de armazenar o resultado para reutilização posterior.
Em suma, você está certo em fazê-lo.
Observe, no entanto, que a variável é não deve manter seu valor entre cada loop. Nesse caso, pode ser necessário inicializá-lo sempre. Você também pode criar um bloco maior, abrangendo o loop, cujo único objetivo é declarar variáveis que devem manter seu valor de um loop para outro. Isso normalmente inclui o próprio contador de loop.
{
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 a pergunta 2: A variável é alocada uma vez, quando a função é chamada. De fato, de uma perspectiva de alocação, é (quase) o mesmo que declarar a variável no início da função. A única diferença é o escopo: a variável não pode ser usada fora do loop. Pode até ser possível que a variável não esteja alocada, apenas reutilizando algum espaço livre (de outra variável cujo escopo terminou).
Com escopo restrito e preciso, otimizações mais precisas. Mais importante, porém, isso torna seu código mais seguro, com menos estados (ou seja, variáveis) com que se preocupar ao ler outras partes do código.
Isso é verdade mesmo fora de um if(){...}bloco. Normalmente, em vez de:
int result;
(...)
result = f1();
if (result) then { (...) }
(...)
result = f2();
if (result) then { (...) }
é mais seguro escrever:
(...)
{
int const result = f1();
if (result) then { (...) }
}
(...)
{
int const result = f2();
if (result) then { (...) }
}
A diferença pode parecer pequena, especialmente em um exemplo tão pequeno. Mas em uma base de código maior, ajudará: agora não há risco de transportar algum resultvalor de f1()para f2()bloquear. Cada um resulté estritamente limitado ao seu próprio escopo, tornando seu papel mais preciso. Do ponto de vista do revisor, é muito melhor, pois ele tem menos variáveis de estado de longo alcance para se preocupar e acompanhar.
Até o compilador ajudará melhor: supondo que, no futuro, após alguma alteração incorreta de código, resultnão seja adequadamente inicializado f2(). A segunda versão simplesmente se recusará a trabalhar, declarando uma mensagem de erro clara em tempo de compilação (muito melhor que o tempo de execução). A primeira versão não localizará nada, o resultado de f1()simplesmente será testado uma segunda vez, sendo confundido pelo resultado de f2().
Informação complementar
A ferramenta de código aberto CppCheck (uma ferramenta de análise estática para código C / C ++) fornece algumas dicas excelentes sobre o escopo ideal das variáveis.
Em resposta ao comentário sobre alocação: A regra acima é verdadeira em C, mas pode não ser para algumas classes C ++.
Para tipos e estruturas padrão, o tamanho da variável é conhecido no momento da compilação. Não existe "construção" em C; portanto, o espaço para a variável será simplesmente alocado na pilha (sem nenhuma inicialização), quando a função é chamada. É por isso que existe um custo "zero" ao declarar a variável dentro de um loop.
No entanto, para as classes C ++, existe esse construtor sobre o qual sei muito menos. Acho que a alocação provavelmente não será o problema, pois o compilador deve ser inteligente o suficiente para reutilizar o mesmo espaço, mas é provável que a inicialização ocorra a cada iteração do loop.