关于C#:不确定为什么使用scanf时while循环的作用不同


Not sure why while loop works different when using scanf

在学习C的过程中,我是一个新手,据我所知,我知道,如果有人给出诸如while (1 == 1)之类的条件,则while循环将永远运行,这将成为永远的循环。

但是在读一本书时,我注意到了诸如

之类的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<stdio.h>

int main() {

    char a;
    int started = 0;
    puts("Scanning\
"
);
    while (scanf("%s[^\
]"
,&a) == 1)
    {
        if (a >= 65 && a <= 90)
        {
            printf("Char is capital\
"
);
        }
        else
        {
            printf("Not cap.\
"
);
        }

    }
    puts("Done\
"
);
    return 0;


}

命令行=> char.exe < file.txt

它从标准输入中获取输入,但scanf返回参数的数量,即1,因此条件应在(1 == 1)

时变为

但是为什么这不是永久循环,并且在读取文件后仍然存在?

谢谢


1
2
3
4
char a;
...
while (scanf("%s[^\
]"
,&a) == 1)

您的格式无效,因为在您只想读取char时专用于字符串,因此您将至少在char中写出结尾的空char,从而产生未定义的行为

1
2
3
char a;
...
while (scanf(" %c",&a) == 1)

并注意格式中的空格,如果要管理所有字符,请将其删除

1
 if (a >= 65 && a <= 90)

使用ASCII码是错误的,这是不可读的并且与非ASCII不兼容

你可以做

1
if (a >= 'A' && a <= 'Z')

或更好地使用isupper(<ctype.h>),因为它不保证大写字母是连续的

您的printf可以被替换为放下\\
字符串或fputs中的值对于让printf打印一个简单的字符串(没有%和arg)是没有用的

But why this is not a forever loop and exists after reading the file?

scanf在EOF上不返回1,因此循环在EOF上停止,关于scanf家族:

These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set indicate the error.


But why this is not a forever loop and exists after reading the file?

仅当scanf每次调用返回1时,它才会无限循环。循环在函数第一次返回不同结果时终止。*

scanf的返回值传达成功扫描和分配的输入字段的数量,并且在某些情况下还传达有关I / O错误的信息。特别地,如果scanf在匹配任何字段之前到达输入的末尾,它将返回宏EOF的值。 EOF不等于1。

*提供了该程序的行为的良好定义。由于注释和另一个答案中所述的原因,您的程序不是。


输入字母时,条件(1==1)成立,并且while循环的主体被迭代。

while循环迭代后,它进入下一个字母。 scanf等到您输入下一个字符。

再次满足条件(1==1)

此循环是不确定的循环,您可以继续输入字母,而永远不会到达puts语句。


&a表示您正在尝试读取堆栈变量中的整行。

But why this is not a forever loop and exists after reading the file?

尝试读取堆栈变量中的文件将导致堆栈崩溃,并且程序将崩溃。

在Internet上的C中寻找不同的存储类型和内存管理。 a是在程序堆栈上分配的字符变量,仅假定存储一个字符。如果使用它的地址并尝试放置更多数据,则它将破坏相邻的堆栈内存/变量,直到OS检测到该内存/变量并杀死程序。您的格式表示它将读取整行,即直到换行为止。
scanf仍将返回1,但它将尝试将该行存储在a的地址中。

您是否按照以下方式运行它:

1
cat <your file>| ./a.out

分配一个足够大的缓冲区,例如char a[4096];并将a作为

传递

1
2
while(scanf("%s[^\
]"
, a) == 1)

现在注意a现在是一个数组,不需要前面的&。文件完成后,循环仍将退出,但仍在EOF上。如果在4Kb之前没有出现新行,请尝试使用小于4 Kb的文件,这是最坏的情况。

用于读取较大文件/行的规定似乎超出了您的实验范围。

工作代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<stdio.h>

int main() {

char a[4096];
int started = 0;
puts("Scanning\
"
);
while (scanf("%s[^\
]"
, a) == 1)
{
    if (a >= 65 && a <= 90)
    {
        printf("Char is capital\
"
);
    }
    else
    {
        printf("Not cap.\
"
);
    }

}
puts("Done\
"
);
return 0;
}

函数scanf返回以下值:

  • >0成功转换和分配的项目数。
  • 0-未分配任何项目。

  • <0-遇到读取错误,或者在出现任何错误之前到达文件结尾(EOF) 进行了分配。