scanf()和getchar()在循环中出现的输入问题研究

本文探讨了在C语言中,scanf()和getchar()在循环中遇到的输入问题。当输入非预期字符时,scanf()可能导致循环无法正常中断,而getchar()则可能因' '字符导致提前结束输入。解决方案包括理解和利用输入流的状态以及避免内核缓冲区的残留。通过实验和源码阅读,澄清了setbuf()在无缓冲模式下的工作原理,并提出了解决这些问题的方法。
摘要由CSDN通过智能技术生成

1. scanf()在循环中出现的问题

下面这段代码,如果一开始就输入字符d这样非数字的话,那么会直接执行完剩余9次循环,
用户不再有输入的机会。

	int unused;
    for (size_t i = 0; i < 10; i++)
    {
   
        printf("please press any key to continue:\n");
        scanf("%d", &unused);
	}

我猜测是标准IO函数流缓冲区的问题(详见apue第五章),可能不合格式的字符采用类似ugetc()的方法丢回了流缓冲中,于是不断重复读取丢弃的智障行为,于是我试着关闭了流缓冲。然而问题仍然存在,情况和之前一样。所以,这和标准IO的缓冲无关,也就是用户态的缓冲无关,那只能是和内核态有关了,暂时还没想到怎么研究,只能看源码了吧。

    setbuf(stdin, NULL);
    int unused;
    for (size_t i = 0; i < 10; i++)
    {
   
        printf("please press any key to continue:\n");
        scanf("%d", &unused);
    
    }

虽然其具体机理暂时不清楚,我试着将缓冲改为自定义的缓冲,至少探究一下缓冲内部是怎么变化的吧。输出了一下读指针的偏移情况,以及缓冲中内容。暂时的结论是:

  • 在scanf()函数的开始,应该会有一种将读指针移到缓冲区开头的操作;

  • 推测scanf()内部机制可能是这样的,

    • 如果输入例如1234a这样的字符串,a会被用类似ugetc()的机制返回;

    • stdin文件中仍然有内容未被读取,所以用户没机会再次输入,

      scanf()内部的缓冲和读指针也不会进行更新。可能是这样的,

      不向内核发送read请求,并且读指针也不重置;

    • 不断尝试读取,直到循环结束;

  • 我认为应该存在相关的机制,来记录stdin在内核缓冲区中关于上一次循环输入是否已经读取完,

    因为即使关闭了scanf()内部的用户态缓冲也无济于事。


                
### 回答1: getchar是一个函数,它从标准输入读取一个字符,不会跳过空格、换行符等;gets是一个函数,它从标准输入读取一行字符,会跳过空格、换行符等;scanf是一个函数,它从标准输入读取一行字符,并将其转换为指定的数据类型。 ### 回答2: getchar、gets和scanf都是C语言用于输入的函数,但是它们有一些区别。 首先,getchar函数用于获取一个字符输入。它会从标准输入读取一个字符,并返回这个字符的ASCII码值。getchar函数是以逐字符为单位读取输入的,即每次只能读取一个字符。如果需要读取字符串,则需要通过循环调用getchar函数多次。 其次,gets函数用于读取一行字符串输入。它会从标准输入读取一个字符串,并将其存储在指定的字符数组。相比于getchar函数,gets函数可以读取整行输入,但是它存在一个严重的安全问题,即无法控制输入字符串长度,容易造成缓冲区溢出漏洞。 最后,scanf函数是一个格式化输入函数,可以根据指定的格式从标准输入读取不同类型的数据。scanf函数可以读取字符、字符串、整数、浮点数等各种类型的数据。通过指定格式字符,可以按照一定的格式读取输入数据,并将其存储到相应的变量。与gets函数相比,scanf函数可以实现一些输入验证,对输入数据进行格式化处理。 总结来说,getchar函数适用于读取单个字符;gets函数适用于读取一行字符串,但存在安全问题scanf函数适用于按照格式读取各种类型的数据,并进行简单的输入验证。在实际使用,需要根据不同的需求选择合适的输入函数。 ### 回答3: getchar()、gets()和scanf()是C语言常用的输入函数,它们的主要区别如下: 1. getchar()函数:用于从标准输入逐个读取字符。它会忽略空白字符,只返回非空白字符。该函数每次只读取一个字符,并且返回该字符的ASCII码值。 2. gets()函数:用于从标准输入读取一行字符串。它会读取输入的所有字符,直到遇到换行符为止。该函数把读取到的字符串存储在指定的字符数组,并在末尾添加一个空字符('\0')作为字符串的结束标志。 3. scanf()函数:用于从标准输入按指定格式读取数据。它可以根据格式字符串的指定规则,将输入的数据按照指定类型存储到相应的变量scanf()可以读取多个数据,不仅限于字符串,还可以读取整数、浮点数等。 这三个函数的应用场景不同: - getchar()常用于需要逐个读取字符的情况,比如输入密码、验证码等。 - gets()常用于读取一行字符串,但由于不检查输入长度,容易导致缓冲区溢出,不建议使用。 - scanf()可以按照格式化的要求进行数据输入,适用于各种类型的输入操作。 总结来说,getchar()读取单个字符,gets()读取一行字符串,而scanf()则根据格式化字符串按需读取不同类型的输入数据。在实际应用,要根据需求选择合适的函数以确保输入数据的正确性和安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值