C语言中多字符输入导致递归/循环输入函数重复执行的原因
问题描述
我写了一个用printf()和scanf()处理输入的递归函数,用户输入非'y'字符时会递归调用自身。但如果输入多个字符(比如输入abc再回车),函数会多次重复输出输入提示,输出次数和输入的字符数一致。换成while循环后问题依旧,改成字符串类型解决了重复问题但引发其他无关问题。
递归函数代码:
int testDetectorWhite(){ char userConfirmation; printf("Please place a WHITE disk directly below the detector\n"); printf("Press 'y' when ready:\n"); scanf("%c", &userConfirmation); printf("\n"); if(userConfirmation != 'y'){ testDetectorWhite(); } //more code }
while循环版本:
while(userConfirmation != 'y') { printf("Please place a WHITE disk directly below the detector\n"); printf("Press 'y' when ready:\n"); scanf("%c", &userConfirmation); }
原因分析
这是因为终端输入采用行缓冲机制:你输入的所有字符(包括最后按下的回车键\n)都会先存在输入缓冲区里,直到触发换行才会把数据交给程序。
而scanf("%c")的行为是每次从缓冲区读取一个字符,不管这个字符是可见字符还是换行符。比如你输入abc\n,缓冲区里有a、b、c、\n四个字符:
- 第一次
scanf读走a,判断不是'y',进入下一次循环/递归,输出提示; - 第二次
scanf直接从缓冲区读走b,同样不满足条件,继续输出提示; - 第三次读走
c,重复上述过程; - 第四次读走
\n,还是不满足,继续输出提示;
直到缓冲区被清空,才会再次等待用户输入。
程序不会崩溃是因为缓冲区的字符都是合法的ASCII字符,scanf能正常读取;终端也不会忽略后续字符,这是标准输入的正常行为——程序会按顺序处理缓冲区里的所有数据。
解决方法
方法1:在scanf格式串前加空格,跳过空白字符
把scanf("%c", &userConfirmation);改成:
scanf(" %c", &userConfirmation);
这里的空格会让scanf自动跳过所有空白字符(包括空格、换行、制表符等),只读取第一个非空白字符,这样就能忽略缓冲区里残留的换行和多余字符。
方法2:手动清空输入缓冲区
在每次scanf之后,把缓冲区里剩下的字符全部读走:
scanf("%c", &userConfirmation); // 清空缓冲区 while ((getchar()) != '\n');
这个循环会一直调用getchar(),直到读到换行符,把缓冲区里剩余的所有字符都消耗掉。
方法3:用scanf的格式匹配跳过非目标字符
可以用scanf的格式串直接跳过所有非'y'的字符,直到读到'y':
scanf("%*[^y]%c", &userConfirmation);
%*[^y]表示读取所有不是'y'的字符并丢弃(*表示不存储),然后%c读取第一个'y'。不过这种方式如果用户一直不输入'y',会一直等待。
内容的提问来源于stack exchange,提问作者Logarythmic

