scanf("%c", &x)和x=getchar()有何区别?

6
我正在写信,尽可能理解我尝试计算的以下程序,因为我认为它在日常生活中可能有一些应用。
这个程序是这样的(它是一个密码):
#include <stdio.h>

void cifrario(int k) {
    char b, d;
    scanf("%c", &d);
    d = getchar();
    putchar(d);
    scanf("%c", &b);
    b = getchar();
    while (b != d) {
        if (('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z')) {
            b = b + k;
            printf("%c", b);
            scanf("%c", &b);
            b = getchar();
        } else 
            printf("%c", b);
    }
}

int main() {
    int h;
    scanf("%d", &h);
    cifrario(h);
    return 0;
}

如您所见,我正在尝试根据输入的某个常量来移动字母表。
我还不太理解并且也不太清楚的是,为什么程序如果没有scanf或getchar就不能正常运行,并且两者在同一个值上都是必需的,比如这里:
scanf("%c", &d);
d = getchar(); 

我认为这两个函数完全等价;实际上在互联网上搜索,我发现关于getchar的内容如下:

该函数返回读取的字符作为unsigned char强制转换为int或在文件结束或错误时返回EOF

这就是我期望scanf()所做的。
我发现:
  • 如果我将所有字符都不带空格地输入,我得到的结果(我认为)只是同一个字符的移位而已,而不是两个字符。

  • while中的else没有按预期工作,因为一旦我输入了一个非字母字符并查看结果,输出就会失败并进入无限字符串。


2
使用 scanf() 进行换行符管理非常麻烦。建议停止使用 scanf() 获取用户输入,改用 fgets()(可能跟随着 sscanf())。 - pmg
2
getchar() 的返回类型是 int,而你正在使用 char 类型的变量来保存它的返回值。 - H.S.
@pmg 对不起,我不知道fgets()和sscanf(),它们更有用吗? - jacopoburelli
1
是的,它们更有用。fgets() 允许从错误的用户输入中恢复,并使得管理输入行(包括尾随的 '\n')变得自然。 - pmg
2
@jacopoburelli 了解一下 getchar()getchar() 的返回类型是 int,因为它在失败时返回 EOF - H.S.
显示剩余2条评论
1个回答

2
是的,这两个函数有一些不同之处。
主要区别在于读取字符失败时的行为。通常情况下可能是输入流关闭导致的,但也有其他原因导致读取失败。
scanf函数通过返回值指示失败。如果您没有检查返回值(就像您在代码中没有做的那样),则无法知道读取是否失败。在这种失败情况下,d可能会保留其旧值,尽管我不确定标准是否保证了这一点。
getchar()通过返回EOF来指示失败,它是一个负整数值(通常为-1,但不能保证)。在您的代码中,您编写了d=getchar(),这意味着您无法区分此结果与具有与EOF相同字符代码的有效字符的读取。 (常见的系统有-128到127范围内的字符)。 对于此代码,d永远不会保留其以前的值。
为了正确地处理错误,您可以使用scanf并测试返回值为1;或者将getchar()的结果存储在int中,并在将其分配给char之前检查其值是否不是EOF。
还有一个理论上的差异在成功情况下。这在现代系统上不相关。scanf版本将在d中存储成功的字符。但是,getchar()版本返回一个非负整数,该整数是转换为unsigned char的字符。代码d=getchar()然后涉及将此值转换为char。如果char是有符号的,则会调用实现定义的行为,并且它可能会导致与scanf获得的不同字符代码。然而,我不知道任何实际上存在这种差异的系统。
两个版本都从流中读取下一个可用字符。在您的问题底部,您描述了一些其他行为,但我认为您将scanf的行为与“%d”与“%c”的行为混淆了。
在您的程序中,while(b!= d)永远不会终止,如果您没有遇到另一个与d相同的字符,则需要修复此问题并确保在读取失败时终止。

这可能是由于输入流被关闭导致的:实际上并不是这样。如果流已关闭,则调用getchar()scanf()会产生未定义的行为。您可能指的是如果输入流已到达文件结尾。 - chqrlie
我不确定标准是否保证了这一点。标准提到仅为成功转换存储转换后的值,因此如果转换失败,则应将目标变量保持不变。 - chqrlie
@chqrlie 实际上,我不会依赖于没有存储空间,多年来已经出现了许多有缺陷的stdio实现。当我说“流被关闭”时,我是指由操作系统或其他因素关闭,以响应输入耗尽等条件。我猜你是在谈论用户调用fclose - M.M
“输入流已关闭”这个表达有些含糊不清:C语言确实将FILE *对象称为“流指针”,并且它们可以通过fclose()函数来关闭……而你所提到的管道和终端连接的系统句柄则可以通过外部手段关闭。通常情况下,它们不被称作“流”。关于实现细节和缺陷,我相信你的防御性方法是基于经验的。 - chqrlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接