使用增量运算符时的不同结果(arr[i++] vs arr[i]; i++;)

Different results when using increment operator (arr[i++] vs arr[i]; i++;)

我不明白为什么下面的代码没有按预期工作:

#include <stdio.h>

int main() {
    int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
    while (i < size && oneOrZero[i++]);
    if (i == size) printf("All ones"); else printf("Has a zero");
}
Terminal: All ones.

当在循环内递增索引时,代码 运行 符合预期:

#include <stdio.h>

int main() {
    int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
    while (i < size && oneOrZero[i]) {i++;}
    if (i == size) printf("All ones"); else printf("Has a zero");
}
Terminal: Has a zero.

谁能解释一下这两者的区别?

在第一个代码中,当 i8 时,oneOrZero[i] 将计算为 false 因为 oneOrZero[8] == 0,但是 i 无论如何都会增加到 9,增量不依赖于表达式的 truthiness,它会随着表达式的计算次数而发生。

所以当 i == size 被评估时自然是 9 == 9,这当然是 true,因此将打印 "All ones" 给你错误的输出。

在第二个代码中,i在条件表达式的主体内部递增,这意味着它只会在满足条件时递增,所以当i8 , oneOrZero[i] 将评估为 false 并且 i 不会递增,保留其 8 值。

在下一行语句中 i == size 将是 8 == 9false 并且 "Has a zero" 将被打印,为您提供正确的输出。

这是一个典型的 off-by-one 错误,当使用迭代索引 i 也进行检查时(与 size 比较)。不用担心,几乎每个人都会遇到这种情况。

问题是,即使条件失败,我们已经在 oneOrZero[i++] 中更改了结果 (i)。我们的第二个变体没有落入这个陷阱,因为条件和索引增量是解耦的。

我们可以用一个更简单的例子来复制该行为:

#include <stdio.h>

int main() {
    int i = 0, size = 1, oneOrZero[] = {0};
    while (i < size && oneOrZero[i++]);
    if (i == size) printf("All ones"); else printf("Has a zero");
}

现在,让我们手动检查条件:

  1. i < size没问题,所以我们继续评价right-hand这边。
  2. i++ 增量 i1(又名 size
  3. oneOrZero[0]0,因此条件不成立

在这一次迭代后,i == size,我们打印 All ones


将此与其他变体进行比较:

int main() {
    int i = 0, size = 1, oneOrZero[] = {0};
    while (i < size && oneOrZero[i]) {i++;}
    if (i == size) printf("All ones"); else printf("Has a zero");
}

再次检查条件:

  1. i < size 可以
  2. oneOrZero[0] == 0,所以我们停止。
  3. i 永远不会递增

因此 i < size 我们打印 Has a zero.


请注意,可以将条件更改为

int i = -1;

while(++i < size && oneOrZero[i]);

但这需要仔细记录。

   #include <stdio.h>
        
        int main() {
            int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
            while (i < size && oneOrZero[i++]);
            if (i == size) printf("All ones"); else printf("Has a zero");
        }

上面的代码一直执行到i = 8,第一个条件i < size8 < 9,第二个条件oneOrZero[8]false。无论如何,我将增加到 9。9 == 9 所以它会打印 "All ones" 将打印给你错误的输出。

#include <stdio.h>

int main() {
    int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
    while (i < size && oneOrZero[i]) {i++;}
    if (i == size) printf("All ones"); else printf("Has a zero");
}

上面的代码执行到 i = 8 并计算为 i < size8 < 9oneOrZero[i] oneOrZero[8] = 0 并计算为 false 并出现跳出循环 并且 i == size 8 == 9 和打印 Has a zero 将被打印,为您提供正确的输出。