This is 出色的实践。
通过在循环内创建变量,可以确保它们的范围仅限于循环内部。不能在循环之外引用或调用它。
这边走:
如果变量的名称有点“通用”(如“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()
to f2()
堵塞。每个result
严格限制在自己的范围内,使其作用更加精准。从评论者的角度来看,这要好得多,因为他拥有的更少长程状态变量担心和跟踪。
甚至编译器也会提供更好的帮助:假设将来在代码发生一些错误更改之后,result
没有正确初始化f2()
。第二个版本将简单地拒绝工作,在编译时声明清晰的错误消息(比运行时更好)。第一个版本不会发现任何东西,结果是f1()
将简单地进行第二次测试,对结果感到困惑f2()
.
补充信息
开源工具CppCheck http://cppcheck.sourceforge.net/(C/C++ 代码的静态分析工具)提供了有关变量最佳范围的一些极好的提示。
回应分配评论:
上述规则在 C 中适用,但可能不适用于某些 C++ 类。
对于标准类型和结构,变量的大小在编译时已知。 C 中没有“构造”这样的东西,因此当调用函数时,变量的空间将简单地分配到堆栈中(没有任何初始化)。这就是为什么在循环内声明变量时成本“为零”的原因。
然而,对于 C++ 类,有一个构造函数的东西我知之甚少。我想分配可能不会成为问题,因为编译器应该足够聪明来重用相同的空间,但初始化可能会在每次循环迭代时发生。