【C语言】getchar()函数和scanf()函数的使用

文章详细介绍了C语言中getchar()函数的功能,它是如何从缓冲区中读取字符的,以及为什么需要设置缓冲区以提高效率和稳定性。同时,文章讨论了缓冲区的三种类型:全缓冲、行缓冲和无缓冲。接着,文章通过示例解释了scanf()函数的使用,包括正确处理缓冲区中遗留的字符问题,并强调了使用scanf时的注意事项,如匹配数据类型和处理未初始化的变量。
摘要由CSDN通过智能技术生成

getchar()

image.png

getchar int getchar(void )
对应头文件:<stdio.h>
函数功能:从缓冲区中获取一个字符。
返回值:如果读取成功,则返回字符的ASCII值。
如果读取失败,则返回EOF,对应的整形值-1。
如果标准输入在文件结束处,该函数返回EOF并设置stdin的EOF指示符(feof)。
如果发生了其他的读取错误,函数也会返回EOF,但会设置它的错误指示符(ferror)。
EOF : end of file->文件的结束标志。
注意:为什么getchar的返回值我们不用char类型接收呢,因为字符在内存中本质上也是以对应ASCII码值存储的,并且如果getchar读取失败会返回-1,这并不能用char来接收,所以我们可以用一个整型来接收getchar的返回值。
这时候就有彦祖要问了,缓冲区是什么呢?


缓冲区

什么是缓冲区

缓冲区是指在内存中开辟出来的一块空间,用于存储输入或输出流中的数据。在C语言中,输入流和输出流都有自己的缓冲区。
当我们使用scanf()函数从标准输入流(例如键盘)读取数据时,输入的字符会先被存储在输入流的缓冲区中,直到遇到换行符或文件结束符。而当我们使用printf()函数向标准输出流(例如屏幕)写入数据时,输出的字符会先被存储在输出流的缓冲区中,直到缓冲区满或者我们手动刷新缓冲区。
getchar()函数可以帮助我们从输入流缓冲区中读取一个字符,并返回该字符的ASCII码值。如果输入流缓冲区为空,则getchar()函数会一直等待用户输入字符。

为啥要设置缓冲区

设置缓冲区的主要目的是为了提高程序的效率。
在C语言中,输入和输出操作通常都需要与外部设备进行通信,例如键盘、屏幕、磁盘等。这些设备的速度较慢,而CPU的处理速度很快,如果每次输入和输出操作都直接与外部设备进行通信,将会导致程序的执行速度非常慢。
因此,在C语言中引入了缓冲区机制,可以将多次输入和输出操作的数据先存储在内存中的缓冲区中,当缓冲区被填满或者需要将缓冲区中的数据发送到外部设备时,再将缓冲区中的数据一次性地发送出去,再加上计算机对缓冲区的操作大大快于对外部设备的操作,故应用缓冲区可大大提高计算机的运行速度。
同时,设置缓冲区还可以避免一些意外情况的发生,例如缓冲区溢出、数据丢失等。在使用缓冲区时,我们需要注意缓冲区的状态以及缓冲区的大小,以便及时刷新缓冲区或者扩展缓冲区大小。
总之,缓冲区在C语言中起着至关重要的作用,它不仅能够提高程序的效率,还可以增加程序的稳定性和可靠性。

缓冲区的类型

缓冲区分为三种类型:全缓冲、行缓冲和不带缓冲。

1、全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
2、行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
3、不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。

getchar函数的正确使用

分析以下代码的输出结果:

#include<stdio.h>
int main()
{
    char password[10] = { 0 };
    puts("请输入密码:");
    scanf("%s", password);
    puts("请确认密码:<y/n>");
    int ch = getchar();
    if (ch == 'y')
        puts("确认成功!");
    else
        puts("确认失败!");
    return 0;
}

image.png
很奇怪啊,程序并没有等待我们输入Y/N来确认密码,而是直接结束,原因就是我们缓冲区中存放的abcdef\n,而scanf函数遇到空格、换行符(\n)、Tab时结束,所以\n并没有被读取走,而是继续留在了缓冲区中,而getchar读取数据时发现缓冲区中并不为空,就不会再等待我们输入了,而是直接读取了\n,然后判断if条件,结束程序。

这时候就体现出getchar函数的作用了,可以用来清空缓冲区

#include<stdio.h>
int main()
{
	char password[10] = { 0 };
	puts("请输入密码:");
	scanf("%s", password);
	puts("请确认密码:<y/n>");
	while (getchar() != '\n') //不断读取缓冲区字符
	{
		;
	}
	int ch = getchar(); //再次读取想要的字符
	if (ch == 'y')
		puts("确认成功!");
	else
		puts("确认失败!");
	return 0;
}

scanf()

int scanf ( const char * format, … );
从标准输入中读取格式化数据,从 stdin 读取数据,并根据参数格式将它们存储到附加参数所指向的位置。

scanf(“输入控制符”,输入参数);

# include <stdio.h>
int main()
{
    int i;
    scanf("%d", &i);  //&i 表示变量 i 的地址,&是取地址符
    printf("i = %d\n", i);
    return 0;
}

操作系统在接收键盘数据时都将它当成字符来接收的,我们从键盘输入的全部都是字符。比如从键盘输入 123,它表示的并不是数字 123,而是字符 ‘1’、字符 ‘2’ 和字符 ‘3’。
这时就需要用“输入控制符”将它转化一下。%d的含义就是要将从键盘输入的这些合法的字符转化成一个十进制数字。经过 %d 转化完之后,字符 123 就是数字 123 了。
第二个要弄清楚的是:&是一个取地址运算符,&后面加变量名表示“该变量的地址”,所以&i就表示变量 i 的地址。&i又称为“取地址i”,就相当于将数据存入以变量 i 的地址为地址的变量中。
综上所述,scanf 语句的意思就是:从键盘上输入字符 123,然后%d将这三个字符转化成十进制数 123,最后通过 “取地址 i” 找到变量 i 的地址,再将数字 123 放到以变量 i 的地址为地址的变量中,即变量 i 中,所以最终的输出结果就是i=123。

scanf(“输入控制符/非输入控制符”,输入参数)

在 printf 中,所有的“非输出控制符”都要原样输出。同样,在 scanf 中,所有的“非输入控制符”都要原样输入

所以在 scanf 的使用中一定要记住:双引号内永远都不要加“非输入控制符”。除了“输入控制符”之外,什么都不要加,否则就是自找麻烦。而且对于用户而言,肯定是输入越简单越好。

scanf()使用注意事项

  1. 参数的个数一定要对应
  2. 输入的数据类型一定要与所需要的数据类型一致
#include<stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);
    printf("a = %d b = %d\n", a, b);
	return 0;
}

image.png
b = -858993460表示b未初始化
在 scanf 中,从键盘输入的一切数据,不管是数字、字母,还是空格、回车、Tab 等字符,都会被当作数据存入缓冲区。按回车键的时候 scanf 开始进入缓冲区取数据,从前往后依次取。
但 scanf 中 %d 只识别“十进制整数”。对 %d 而言,空格值(空格、回车、Tab 键)都是区分数据与数据的分隔符。当 scanf 进入缓冲区中取数据的时候,如果 %d 遇到空格、回车、Tab 键,那么它并不取用,而是跳过,继续往后取后面的数据,直到取到“十进制整数”为止。对于被跳过和取出的数据,系统会将它从缓冲区中释放掉。未被跳过或取出的数据,系统会将它一直放在缓冲区中,直到下一个 scanf 来获取。
但是如果 %d 遇到字母,就会把它当作非法字符,那么它不会跳过也不会取用,而是直接从缓冲区跳出。所以上面这个程序,虽然 scanf 进入缓冲区了,但用户输入的是字母 a,所以它什么都没取到就出来了,而变量 i 没有值,即未初始化,所以输出就是 –858993460。
但如果将 %d 换成 %c,那么任何数据都会被当作一个字符,不管是数字还是空格、回车、Tab 键它都会取回。

总结scanf()

scanf 的使用看似细节繁杂,但使用起来非常简单。就目前而言,只要掌握以下四点:

  1. 在 scanf 的“输入参数”中,变量前面的取地址符&不要忘记。
  2. scanf 中双引号内,除了“输入控制符”外什么都不要写。
  3. “输出控制符”和“输出参数”无论在“顺序上”还是在“个数上”一定要一一对应。
  4. “输入控制符”的类型和变量所定义的类型一定要一致。对于从键盘输入的数据的类型,数据是用户输入的,程序员是无法决定的,所以在写程序时要考虑容错处理。
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值