1

完整、调整后的代码在帖子末尾

如果我要让用户输入并使用 scanf() 获取它,这个过程是如何工作的(关于缓冲区等的细节)?

当人们提到“刷新”缓冲区时是什么意思?我还听说刷新输入(或者更确切地说,未定义?)是不好的做法,但是刷新输出流是创建 fflush() 之类的函数的目的。-- 上面上下文中的“流”是什么意思?

所以,从程序上讲,如果我要求输入如下:

printf("Enter a string: ");
    scanf("%s", string);

输入会发生什么;字符串在哪里被 scanf 函数“抓取”?

输入缓冲区中的“空白”是什么?(空格字符、NULL 等?)


在我看来,虽然我读过的文献从未专门解决过这些问题,但我想来自用户或文件的输入将存储在某个临时 char 数组中,该数组由访问,并使用适当的打印/存储指针。

我提出这个问题的原因是因为我对以下代码有一个相关问题:

int main(){
    char string[20];
    char string2[20];

//strlen test
    printf("Enter a string: ");
        scanf("%s", string);
    printf("\t length: %d\n", strlen(string));

//strcat test
    printf("Enter two strings to concatentate: ");
        scanf("%s %s", string, string2);
    strcat(string2, string);
    printf("\nConcatenated: %s\n\n\n", string);

return 0;

}

顺便说一句:上面的 strlen() 和 strcat() 函数是在本地定义的,因此参数可能与您熟悉的 C 库中的不匹配。

产生以下输出:

图像

我假设,一旦 scanf 函数遇到空格,它就会假定字符串的结尾。虽然,剩余的输入仍然存在于缓冲区中。然后,当我要求更多输入时,输入的数据被放置在缓冲区的末尾。因此,当这次调用 scanf() 时,将“andothernonsensehere”作为下一个输入,因为它较早地存在于缓冲区中。

虽然,如果我上面所说的是真的,第二个“andothernonsensehere”字符串和第二次调用时输入的第一个字符串“sherrell”不应该连接起来吗?


tl; dr verison 如何刷新缓冲区以确保 scanf() 捕获下一个输入事件?


完整,编辑,代码:

#include <stdio.h>

int main(){
    char string[20];
    char string2[20];

//strlen test
    printf("Enter a string: ");
        scanf("%s", string);
    printf("\t length: %d\n", strlen(string));
    clear(); //make sure buffer is empty

//strcat test
    printf("Enter two strings to concatentate: ");
        scanf("%s %s", string, string2);

    strcat(string2, string);
    printf("\nConcatenated: %s\n\n\n", string);

    return 0;
}

void strcat(char *toCopy, char *org){
    while(*org != NULL) org++; //find end of characters
    while( (*org++ = *toCopy++) != NULL); //copy
}

int strlen(char *a){
    char *b = a;
    while(*b++ != NULL);
    return b-a;
}


void clear(){
    while(getchar() != '\n');
}
4

2 回答 2

1

你基本上做对了,尽管缓冲可能不是你认为的那样。

scanf不维护缓冲区。从概念上讲,它一次读取一个字符,直到格式或输入用尽。

但是,终端输入通常由终端驱动程序缓冲。或者,更准确地说,如果没有等待读取的字符,则终端输入请求在Enter按下键之前不会返回任何内容,即使程序只读取一个字符。未读字符保留在内核中,它们将根据要求提供给用户程序。(终端驱动程序还处理输入字符时的回显,以及处理退格和许多其他事情。)但是,所有这些行为都可以更改。请参阅man sttyman termios(可能还有man tty_ioctl)了解更多详细信息。很多。

所以scanf完全不知道会发生什么。它只是消耗字符直到它满意为止,如果有必要,它会调用ungetc返回它读取但不需要的最后一个字符。

现在,你的问题:

不应该将第二个“andothernonsensehere”字符串和在第二次调用时输入的第一个字符串“sherrell”连接起来吗?

答:是的,他们应该这样做。它们将与标准库函数一起使用strcat,前提是您将其参数按正确的顺序放置并确保string有足够的空间来容纳连接和终止NUL字符。

正如你所说,我不能对你的 做任何假设strcat,所以我不知道它的参数与标准库版本的顺序相同。但是,如果确实如此,那么您看到的行为是可以预期的:strcat将附加string到末尾string2(覆盖随机内存,因为string2不足以容纳串联),但这不会改变string(除非string是随机内存被覆盖)。所以当你打印出来时string,你会看到它原来的样子,即一个 20 个字符的字符串,它的终止NUL字节也被其他一些随机内存位置覆盖。

随着所有这些未定义的行为的发生,您的程序几乎可以产生任何输出,或者它可能会产生段错误或触发鼻恶魔。我认为您的 C 编译器可能在双字边界上对齐字符串,因为我本来希望string2立即跟随string,因此NUL在您放入 20 个字符之后的尾随string将是 的第一个字节,然后在填充string2时将被覆盖。在这一点上附加的后果将是,嗯,很有趣。scanfstring2stringstring2

于 2013-07-16T04:43:49.117 回答
0
    String2

未声明,其输入为“”空字符串。因此,您输入第一个并与空字符串连接会产生与原始输入相同的字符串 %s 是“输入”,其中 s 代表字符串,并将其存储在字符串中。这是您从中获取输入的地方。

于 2013-07-16T03:21:45.663 回答