C++-回车符作为 scanf 的结束为什么会影响到下一次输入的获取

发布于 2017-01-07 16:44:35 字数 297 浏览 1404 评论 4

看下面这段代码:

#include <stdio.h>

void main()
{
int a;
char c = '';

printf("inputn");

scanf("%d",&a);
scanf("%c",&c);

printf("n");

printf("%d,%x",a,c);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

偏爱自由 2017-10-19 05:01:43

可用 while((ch = getchar()) != 'n' && ch != EOF) 这种通用的方法清空缓冲区里的回车符!

想挽留 2017-09-23 03:17:17

回车也是字符来的,所以用 %c scanf 的时候,一般都放在其他类型的前面
放在最后的话,回车会被认为输入 rn 或者 n 字符来处理,
显然 rn 或者 n 都是字符,被填充到 c 变量中

更新:
为了更好的验证些问题,我做了如下的操作:
编辑一个 todo.c 文件

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
char ch;
scanf("%c", &ch);
printf("ch = %dn", ch);
return 0;
}

编译链接后生成 todo
然后再写一个 input.c 文件,内容为

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
printf("rn");
return 0;
}

编译链接后生成 input

在 shell 里面使用命令

./input | ./todo

结果为: ch = 13

当我修改 input.c 的 printf 里面的内容为 "n" 后,编译出来的 input 再次调用
结果为: ch = 10

然后再次修改 input.c 的 printf 为 "r",重新编译链接 input
结果为: ch = 13

结论:当 scanf 用 %c 结尾接收数据时,碰到 r 或者 n 都会作为结束
进来的是什么字符,就会把什么字符赋值给对应的变量,这一点与 qingyunww 的观点不同

更新
将 todo.c 的程序修改为:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
char a, b, c;
scanf("%c%c%c", &a, &b, &c);
printf("char: %d %d %dn", a, b, c);
return 0;
}

然后调用 input 填充数据时分别测试:
rrr - 13 13 13
rnr - 13 10 13
nnn - 10 10 10

而修改为 rn 后测试结果比较诡异,每次的结果都不会一样:

以上结果来自:Arch 3.6.4 gcc 4.7.2

祝好,
斑驳敬上

浮生未歇 2017-05-21 01:05:01

在用"%c"输入时,空格和“转义字符”均作为有效字符。例:
scanf("%c%c%c",&c1,&c2,&c3);
输入:a□b□c↙
结果:a→c1,□→c2,b→c3 (其余被丢弃)
scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数,scanf函数

仅在每一个数据域均有数据,并按回车后结束)。
① 遇空格、“回车”、“跳格”键。
② 遇宽度结束。
③ 遇非法输入。
这里对lz的程序作了下修改:

#include <stdio.h>

void main()
{
int a;
char c = '';
char d = '';
printf("%d,%d n",'r','n');

printf("inputn");

scanf("%d",&a);
scanf("%c",&c);
scanf("%c",&d);

printf("%d,%d,%d",a,c,d);
printf("-------------------------n");
}

如果输入:12↙↙
结果为:12,10,10
如果输入:12□□↙
结果为:12,32,32
所以,程序中的scanf()在输入12后,再连续两次按下Enter键时,发现a(12)、c(10)、d(10),从这里可以看出Enter输入的rn被处理成n,并且被c、d接受了;同时,需要输入字符时,无论输入的是什么都会被接受。

虐人心 2017-03-15 08:10:09

跟踪了下VS中scanf的源码,找到了一些线索,但并不是如楼上两位所说的跟r有关系。
scanf底层调用流程:scanf->vscanf->inputfn。
附上inputfn函数关键代码:

comchr = *format | (_T('a') - _T('A'));

if (_T('n') != comchr)
if (_T('c') != comchr && LEFT_BRACKET != comchr)
ch = EAT_WHITE();
else
ch = INC();

...
...

case _T('d') :
while (!done_flag) {

if (_T('x') == comchr || _T('p') == comchr)

if (_ISXDIGIT(ch)) {
number = (number << 4);
ch = _hextodec(ch);
}
else
++done_flag;

else if (_ISDIGIT(ch))

if (_T('o') == comchr)
if (_T('8') > ch)
number = (number << 3);
else {
++done_flag;
}
else /* _T('d') == comchr */
number = MUL10(number);

else
++done_flag;

if (!done_flag) {
++started;
number += ch - _T('0');

if (widthset && !--width)
++done_flag;
else
ch = INC();
} else
UN_INC(ch);

comchr在解析scanf("%d")后,等于'd',INC的代码如下:

static _TINT __cdecl _inc(FILE* fileptr)
{
return (_gettc_nolock(fileptr));
}

#define _gettc_nolock _getc_nolock

#define _getc_nolock(_stream) _fgetc_nolock(_stream)

#define _fgetc_nolock(_stream) (--(_stream)->_cnt >= 0 ? 0xff & *(_stream)->_ptr++ : _filbuf(_stream))

可以看到这里INC最后面会根据(_stream)->_cnt的值决定是否调用_filbuf(_stream),_stream其实就是我们说的标准I/O输入,其结构体如下:

struct _iobuf {
char *_ptr; // 缓冲区第一个未读字节
int _cnt; // 缓冲区剩余未读字节数
char *_base; // 缓冲区基址指针
int _flag;
int _file;
int _charbuf;
int _bufsiz; // 若采用系统标准I/O,该值为4096
char *_tmpfname;
};
typedef struct _iobuf FILE;

函数_filbuf会最终调用windows的系统调用ReadFile用来读入用户输入,这里不再深入讨论。

假设我们输入如下所示:

32<Enter>

此时缓冲区存的内容为:

32n

跟踪INC()函数可以看到变量的变化情况:

第1次INC()后:
_cnt=2 ptr='2' ch='3'
第2次INC()后:
_cnt=1 ptr=0x0a ch='2'
第3次INC()后:
_cnt=0 ptr=0x0a ch=0x0a

注意第3次INC后,ch=0x0a,程序执行:

++done_flag;
接着继续执行:
UN_INC(ch);

再看看UN_INC(ch)最终会调用ungetc(_c,_stm),它会修改_stream的_cnt域,也就是说在执行了UN_INC(ch)后,
_cnt=1, ptr=0x0a

之后,程序继续执行

scanf("%c",&c);

继续往下:

comchr = *format | (_T('a') - _T('A'));

if (_T('n') != comchr)
if (_T('c') != comchr && LEFT_BRACKET != comchr)
ch = EAT_WHITE();
else
ch = INC();

可以看到程序会继续执行ch = INC()语句,而这个时候,因为(_stream)->_cnt == 1,所以_fgetc_nolock会执行:

0xff & *(_stream)->_ptr++;

而不会调用我们期望的:

_filbuf(_stream);

此时,c就等于0x0a了。

综上所述,问题确实是出在一次

scanf("%d",&a);

最后会调用:UN_INC(ch);改变了stream._cnt的值,从而导致下一次

scanf("%c",&c);

并没有达到我们预期的效果。

至于为什么匹配一个int类型成功后会调用UN_INC(ch)往前移动缓冲区的指针,这个也是可以理解的。做过词法分析的同学都知道,在匹配一个关键字时,我们需要往后看一个字符,是继续往后匹配(非空格时)呢?还是截断(遇到空格)?都需要往后看看才能确定。

补充:
既然前面已经找到了问题所在,只需要在两个scanf之间调用一个fflush(stdin)刷新下输入缓冲区即可,如下代码:

scanf("%d",&a);
fflush(stdin);
scanf("%c",&c);

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文