简体   繁体   English

为什么这个C代码(for循环)永远循环?

[英]Why this C code (for-loop) is looping forever?

Learning C right now and I've made this simple for-loop . 现在学习C,我已经做了一个简单的for-loop The program is just to check if the argument given has the 'a' alphabet in it. 该程序只是检查给定的参数中是否包含“ a”字母。

int main(int argc, char *argv[])
{
    if(argc != 2) {
        printf("ERROR: Need one argument\n");
        return 1;
    }

    int i = 0;
    char letter;
    for(i = 0, letter = argv[1][i]; letter != '\0'; i++) {

        switch(letter) {
            case 'a':
                printf("%d: 'a'\n", i);
                break;

            default:
                printf("%d: '%c' is not an 'a'\n", i, letter);
        }
    }

    return 0;
}               

The result of this is a forever looping program, but if I change the line: 这样的结果是一个永远循环的程序,但是如果我改变这一行:

for(i = 0, letter = argv[1][i]; letter != '\\0'; i++) to for(i = 0, letter = argv[1][i]; letter != '\\0'; i++)

for(i = 0, letter = argv[1][i]; argv[1][i] != '\\0'; i++) , for(i = 0, letter = argv[1][i]; argv[1][i] != '\\0'; i++)

The code runs just fine. 代码运行正常。 Why is this? 为什么是这样?

letter is never updated in your loop. letter永远不会在您的循环中更新。 It always have first character of string in argv[1] . argv[1]它始终具有string的第一个字符。 Place this statement 放置此语句

letter = argv[1][i];  

after switch statement in the loop body. 在循环体中的switch语句之后。

letter is assigned value only once; letter仅被赋值一次; at the beginning of the loop. 在循环的开始。 That assignment should be inside the for loop: 该分配应在for循环内:

// Assign initial value to letter
char letter = argv[1][0];
for(i = 0; letter != '\0'; i++){
    switch(letter) {
        // ...
    }

    // Update new value to letter
    letter = argv[1][i];
}

The first part of a for loop is only initialized in the beginning. for循环的第一部分仅在开始时初始化。 You can "fix" your code by swapping the , and ; 通过交换,你可以“修复”你的代码,; there: 那里:

for (i = 0; letter = argv[1][i], letter != '\0'; i++) {

Now the condition uses the comma operator to first assign the letter (and discard this value), and then to check that the character is not '\\0'. 现在,该条件使用逗号运算符首先分配字母(并丢弃此值),然后检查字符是否不是'\\ 0'。 Actually the 2nd part is superfluous since the value of letter = argv[1][i] is the character, and it is true only if it is not '\\0', so we can write the loop as 实际上,第二部分是多余的,因为letter = argv[1][i]是字符,并且仅当它不是'\\ 0'时才为真,因此我们可以将循环写为

for (i = 0; letter = argv[1][i]; i++) {

Should you not need a loop index, seasoned C programmer, however, wouldn't use any of the constructs above - including indexing, instead using just a pointer-to-char: 如果您不需要循环索引,那么经验丰富的C程序员将不会使用上述任何结构-包括索引,而仅使用指向字符的指针:

char *pos;
for (pos = argv[1]; *pos; pos++) {
    switch(*pos) {
        case 'a':
            printf("'a'\n");
            break;

        default:
            printf("'%c' is not an 'a'\n", *pos);
    }
}

or even with an index 甚至带有索引

char letter, *pos = argv[1];
int i;
for (i = 0; letter = pos[i]; i++) {
    switch(letter) {
        case 'a':
            printf("%d: 'a'\n", i);
            break;

        default:
            printf("%d: '%c' is not an 'a'\n", i, letter);
    }
}

If you ever have trouble debugging a for loop, it often helps to think of for (INITIALISE; TEST; INCREMENT) to be equivalent to: 如果您在调试for循环时遇到麻烦,通常可以考虑将for (INITIALISE; TEST; INCREMENT)等效为:

INITIALISE;
while (TEST)
{
    // code
    INCREMENT;
}

Also you should never use the comma operator. 同样,您永远不要使用逗号运算符。 It's OK to use it if you really know what you are doing, but if you really knew what you were doing then you wouldn't have posted this question here. 如果您真的知道自己在做什么,可以使用它,但是如果您真的知道自己在做什么,那么您就不会在这里发布此问题。

So in your for loop for(i = 0, letter = argv[1][i]; letter != '\\0'; i++) and replacing the comma with a semicolon we can look at it as: 因此,在for循环中for(i = 0, letter = argv[1][i]; letter != '\\0'; i++)并用分号替换逗号,我们可以将其视为:

i = 0;
letter = argv[1][i];
while (letter != '\0')
{
    // other code (which doesn't modify letter)
    i++;
}

and from this it should be clear why the loop doesn't terminate. 从这一点应该很清楚为什么循环不会终止。

Whenever you get an accidental for-ever loop, the first thing you check is if you actually increment the loop iterator. 每当遇到意外的永远循环时,首先要检查的是是否实际增加了循环迭代器。 You don't, so there you go. 你没有,所以你去了。

The fix is trivial, just use a pointer instead, which was probably what you intended anyhow? 该修复程序很简单,只需使用一个指针即可,无论如何这可能是您想要的?

const char* cptr = argv[1]; 

for(i = 0; cptr[i] != '\0'; i++)
{
  switch(cptr[i])
  { 
    ... 
  }
}

You now have a pure, simple loop. 您现在有了一个简单的循环。 Avoid multiple iterators when possible. 尽可能避免使用多个迭代器。 As a bonus, it runs faster than the original, since there is no pointless copying at each lap of the loop. 另外,由于在循环的每一圈都没有毫无意义的复制,因此它的运行速度比原始速度快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM