scanf("%s", &str)和scanf("%s\n", &str)有什么区别?

3

输入

输入由多行组成,以包含单个*的行结尾。最后一行不应处理。每行包含HajjUmrah

输出

对于输入的每一行,在单独的行中输出Hajj-e-AkbarHajj-e-Asghar,不包含引号。有关确切格式,请参见示例。

这是解决此问题的代码。

#include <stdio.h>

int main()
{
    char str[100];
    int i = 1;

    while (scanf("%s", &str))
    {
        if (str[0] == '*')
            break;
        else if (str[0] == 'H')
            printf("Case %d: Hajj-e-Akbar\n", i);
        else
            printf("Case %d: Hajj-e-Asghar\n", i);
        i++;
    }
}

对于输入

Hajj
Umrah
*

当我一次性输入此内容时,程序通过打印数据提供了预期的输出。
Hajj
Case 1: Hajj-e-Akbar
Umrah
Case 2: Hajj-e-Asghar
*

在得到 * 作为输入后,程序会等待用户按下 Enter 键。按下 Enter 后,程序终止。但是我希望我的程序在得到 * 作为输入时立即终止,而不是通过按下 Enter 键。请帮助我解决这个问题。但这不是我的问题。我的问题是针对同样的输入-

Hajj
Umrah
*

当我使用 scanf("%s\n", &str) 输入时,程序不会在第一个输入 Hajj 后打印输出 Case 1: Hajj-e-Akbar,但它会在第二个输入 Umrah 后打印第一个输入的输出。然后程序会等待一个 Enter 键来输入 *

Hajj
Umrah
Case 1: Hajj-e-Akbar
*

然后我按下Enter,它会打印出第二个输入Umrah的输出Case 2:Hajj-e-Asghar,然后等待另一个输入。按下Enter后,输出看起来像这样。

Hajj
Umrah
Case 1: Hajj-e-Akbar
*
Case 2: Hajj-e-Asghar

我不理解\nscanf中的作用。谢谢。
如果我的问题表达得不正确,我很抱歉。我是编程的新手。

1
在数组上不应使用&运算符。它们已经衰变为指针。尽管值相同,但您实际上提供了错误的类型,这一点您可能没有注意到。 - Gerhardh
2个回答

2
"scanf"读取格式化输入,因此当您使用“%s\n”,&str时,字符串将被消耗并存储在str中,换行符也将存在于缓冲区中,当您按Enter键时,字符串将被存储,而换行符将被丢弃。
请注意,正确的用法是“%s\n”,str,str已经是指针,不应使用&。
当您使用“%s”,&str时,换行符将留在缓冲区中,并将在下一次循环中被消耗,因此str将在第一次迭代中存储,“\n”将在下一次迭代中存储在str中,然后才会再次要求输入,在第三次迭代中。
为了完整起见,正如下面的评论所述,根据scanf的定义:
"...格式字符串中的任何单个空格字符都会从输入中消耗所有可用的连续空格字符(就像通过调用isspace一样确定)。请注意,格式字符串中的"\n"、" "、"\t\t"或其他空格字符没有区别。 我还建议您限制scanf所期望的字符串大小,以避免容器溢出,例如对于您拥有的100个字符容器,使用%99s,并检查scanf返回值,在这种情况下,必须为每个循环设置= 1。要做你想做的事情,我猜是从stdin获取字符而不需要按回车键,你需要一个特定于SO的方法,这里是一个使用<conio.h>库和getch()的Windows小例子:"
#include <stdio.h>
#include <conio.h>

int main()
{
    int i = 1, c;

    while (1)
    {
        if ((c = getch()) == '*')
            return 0;
        else if (c == 'H')
            printf("Case %d: Hajj-e-Akbar\n", i);
        else
            printf("Case %d: Hajj-e-Asghar\n", i);
        i++;
    }
}

对于Linux来说,一个选项是还可以使用<ncurses.h>库中的getch()函数,这可能需要你进行安装
PS:不用担心,你的问题很好,特别是作为该网站中仅有的第二个问题。

其实,我不太了解指针,所以我使用了 &。当我使用 scanf("%s", str) 并在同一时间给出以下输入时,可能会发生这种情况。 Hajj Umrah *首先,它获取字符串 Hajj 并显示其输出。然后它获取第二个输入 Umrah 并显示其输出。这两个输出都是在没有按下任何 Enter 的情况下显示的,因为每个输入后有两个换行符。但是,当程序得到 * 作为输入时,如果没有按下 Enter,它就不会执行。这就是为什么我必须在最后一个输入处按下 Enter 的原因。我的解释正确吗? - Ehsanul Karim Pappu
@EhsanulKarimPappu 如果你想要做你想做的事情,那么没有可移植的解决方案。如果你使用的是Windows系统,你可以尝试使用<conio.h>库中的getch()函数;如果你使用的是Linux系统,则可以使用<ncurses.h>库从stdin读取而无需按下回车键。你所描述的行为可能是由于错误的类型赋值导致的未定义行为。 - anastaciu
@EhsanulKarimPappu,我编辑了答案,为您提供了一些我认为可能满足您需求的选项。 - anastaciu

1
在使用scanf时,结尾的\n是一个坏主意:格式字符串中的任何空格字符都会导致输入流中的任何空格字符序列被丢弃。如果从文件中读取,您不会有问题,但如果从终端读取,则scanf()将不会返回,直到您输入下一个项目,这将创建混乱,因为程序的输出将对应于先前的项目,而不是刚刚输入的项目...永远不要在scanf()格式字符串中添加尾随空格或换行符。
您应该测试scanf()是否返回1,以避免在文件末尾返回EOF时出现无限循环。
还要注意,传递&str是不正确的:str是一个数组,将其作为str传递将有效地传递指向其第一个元素的指针,这是正确的。
此外,您应该告诉scanf()目标数组可以存储的最大字符数,以避免在输入过长时出现未定义的行为。由于该数组的大小为100,因此格式字符串应为"%99s"以留出空间放置空终止符。
最后,在最后一个*后需要按Enter键,原因有三个:
  • 终端设备驱动程序默认为行缓冲,因此在键入Enter之前,输入不会提供给程序。
  • 标准输入流(stdin)默认为行缓冲,因此它将从系统句柄读取数据,直到获得换行符,然后scanf()才有机会看到输入行的第一个字符。
  • scanf("%s", str)将保持其输入流,直到获取单词的末尾,空格字符或文件结束,因此当您仅键入*时,它不会返回。
下面是修改后的版本:
#include <stdio.h>

int main() {
    char str[100];
    int i = 1;

    while (scanf("%99s", str) == 1 && str[0] != '*') {
        if (str[0] == 'H') {
            printf("Case %d: Hajj-e-Akbar\n", i);
        } else {
            printf("Case %d: Hajj-e-Asghar\n", i);
        }
        i++;
    }
    return 0;
}

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