11

多年来,我养成了for在循环退出后不使用循环迭代器值的习惯。我本可以发誓我这样做了,因为它曾经产生编译器警告,但是在我最近的一次代码审查中受到挑战后,我被证明是错误的。

例如,我总是这样做(注意:我们的代码标准禁止使用“break”关键字):

int i, result;
bool done = false;
for (i=0; i<10 && !done; i++) {
    if (some_condition) {
        result = i;
        done = true;
    }
}
// Value of i may be undefined here

现在,result如果我可以依赖 i 的值,显然可以删除该变量。我认为由于编译器优化,您不能依赖循环迭代器的值。我只是记得一个幻影教学吗?或者这是标准(特别是关于 GNU C)?

4

5 回答 5

12

C89C99C11中访问for语句后的迭代变量没有任何问题。

 int i;

 for (i = 0; i < 10; i++) {
     /* Some code */
 }

 printf("%d\n", i);  // No magic, the value is 10

从 C99 开始,您还可以使用声明作为for语句的第一个子句,在这种情况下,声明的变量当然不能在for语句之后使用。

于 2012-05-01T15:13:06.247 回答
4

不同的语言有不同的规则。在 Pascal 中,允许编译器在最终递增后优化存储循环索引,因此它可能是第一个循环终止值,也可能是最后一个有效值。

于 2014-04-24T17:39:04.117 回答
2

有很多用例,其中 for 循环仅用于推进迭代器。这可以在 strlen 的某些实现中看到(尽管无可否认,还有其他方法可以做 strlen),以及其他类型的函数,其目标是找到某个限制:

/*find the index of the first element which is odd*/
for (ii = 0; ii < nelem && arry[ii] % 2 == 0; ii++);

如前所述,混淆点可能来自迭代器本身在 for 语句中定义的结构。

一般来说,for 语句非常强大,不幸的是它们通常从未充分发挥其潜力。

例如,同一个循环的不同版本可以写成如下(尽管它不能证明使用迭代器的安全性):

#include <stdio.h>
int main(void)
{
    int cur, ii = 0, nelem, arry [] = { 1, 2, 4, 6, 8, 8, 3, 42, 45, 67 };
    int sum = 0;

    nelem = sizeof(arry) / sizeof(int);
    /* Look mom! no curly braces! */

    for (
            ii = 0;
            ii < nelem && ((cur = arry[ii]) %2 == 0 ||
                                ((printf("Found odd number: %d\n", cur)||1)));
            ii++, sum += cur
        );
    printf("Sum of all numbers is %d\n", sum);
    return 0;
}

在这种特殊情况下,对于这个特定问题似乎需要做很多工作,但对于某些事情来说它可能非常方便。

于 2012-05-01T20:30:45.010 回答
0

即使该for循环的控制变量的值已明确定义,您可能已被告知避免for在 for 循环之后使用循环的控制变量,因为该变量的范围是处理方式,特别是因为历史的处理已经改变的 C++ (我知道这个问题被标记为“C”,但我认为避免在循环之后使用 for 循环控制变量的理由可能起源于这个 C++ 历史)。

例如,考虑以下代码:

int more_work_to_do(void) 
{
    return 1;
}

int some_condition(void)
{
    return 1;
}

int foo()
{
    int i = 100;

    while (more_work_to_do()) {
        int done = 0;

        for (int i = 0; i < 10 && !done; i++) {
            if (some_condition()) {
                done = 1;
            }
        }

        if (done) return i;   // which `i`?
    }

    return 1;
}

i在循环中声明的一些旧的作用域规则下,for在带有注释“which i”的语句上返回的值将由for循环确定(VC++ 6 使用这些规则)。根据用于确定该变量范围的较新的标准规则,返回的值将i在函数开始时声明。

于 2012-05-01T15:25:48.940 回答
0

虽然我不可能知道你的习惯是如何形成的,但我可以告诉你我的习惯是如何做到的。这是通过看到这样的代码:

for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
    if (someCondition) found = TRUE;
}
foundIndex = i-1;

基本上,这样的代码是在某些编码规则(例如基于 MISRA)不允许使用 break 关键字时编写的。但是,如果您不跳出循环,则循环通常会给您留下一个“i”,它与您关心的内容相差一个。

有时,您甚至可以找到以下内容:

for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
    if (someCondition) found = TRUE;
}
foundIndex = i;

这只是语义上的错误,并且可以在将“禁止中断关键字规则”引入到单元测试未充分覆盖的现有代码库中时发现。可能听起来令人惊讶,但一切都在那里......

于 2015-08-19T08:52:55.433 回答