输入( Getchar/Scanf ) 输出( Putchar/Printf ) , 与缓冲区

输入( Getchar/Scanf ) 输出( Putchar/Printf ) , 与缓冲区

在大多数情况下,程序中的输入和输出操作都是通过缓冲区来实现的。它位于内存中,用来暂存数据直到满足某些条件(如达到一定大小或遇到特定字符)后再进行实际的I/O操作。使用缓冲区可以提高效率,减少对硬件设备的直接访问次数。

1. 输入缓冲

操作系统会维护一个输入缓冲区。当你从键盘输入时,这些数据首先被放入这个缓冲区,然后由程序通过例如 getchar()函数 ,scanf()函数 等其他输入函数,来进行读取。

这样做的好处是可以让用户在一次性输入多个字符或整行文本后,再由程序逐步处理这些数据,而不是每输入一个字符就立即触发一次读取操作。

  1. 当你调用 getchar(),它会从输入缓冲区中取出一个字符。
  2. 当你调用 scanf("%d", &num)scanf() 会等待用户输入数字,并且在按下回车键后将整个数值从缓冲区读出并解析成整数。

2.输出缓冲

对于标准输出(通常是屏幕),同样存在缓冲机制。当你调用 printf() 或其他输出函数时,数据可能不会立即显示在屏幕上,而是先写入到输出缓冲区中。当缓冲区满、遇到换行符或显式地刷新缓冲区时,数据才会真正输出到终端。

缓冲区的刷新方式:

  1. 无缓冲,即一进行写入就对缓冲区进行刷新,一个一个字符写入
  2. 行缓冲,遇到\n才对缓冲区进行刷新,一行一行写入
  3. 全缓冲,缓冲区满了,或者调用 fflush() 函数时,才进行刷新,缓冲区内容全部发送出去

Scanf() 函数 与 Getchar() 函数

scanf() 函数的行为与 getchar() 有所不同,但它们同样,都依赖于输入缓冲区。

当你进行标准输入操作时:

  1. **如果你输入一个字符,或多个字符,并且没有按下回车键,**那么缓冲区里只有一个或多个字符,没有换行符\n

    但值得注意的是,大部分情况下,你输入的字符显示在屏幕上,但它们还在终端的缓冲中,还没有被送到程序的输入缓冲区。当你按下回车后,所有的字符包括\n才会被送入缓冲区,等待输入函数进行读取操作

  2. **无论你输入了一个字符,或多个字符,只要按下了回车键,**那么缓冲区里就会存放所有的字符,包括按下回车键所输入的换行符 \n。因此,即使你只输入了一个字符,比如 ‘a’,然后按下了回车键,实际进入缓冲区的数据将是a\n

Getchar()

从输入缓冲区中读取,并返回下一个字符。如果输入缓冲区为空,getchar() 将会等待用户输入。

如果你在键盘上输入多个字符并按下回车键,所有这些字符会被送入输入缓冲区。但是,getchar() 每次只从缓冲区中取出一个字符处理。

也就是说,即使你一次性输入了多个字符,getchar() 也只会一次读取一个字符,并且后续的 getchar() 调用将继续从缓冲区中读取剩余的字符,直到缓冲区被清空或遇到文件结束符(EOF)。

例如,如果你输入了 “abc” 并按下了回车键,那么:

  • 第一次调用 getchar() 会返回 ‘a’。
  • 第二次调用 getchar() 会返回 ‘b’。
  • 第三次调用 getchar() 会返回 ‘c’。
  • 第四次调用 getchar() 可能会返回换行符 ‘\n’,这取决于系统或编译器如何处理输入。

每次调用 getchar() 都会从输入缓冲区中移除一个字符。

值得注意的是,getchar()函数读取到的字符的,将返回其字符对应的ASCII码,以int 类型存储 ,不是用char类型存储。

int main()
{
	int ch =getchar();
	putchar(ch);
	
	return 0;
}

getchar()函数读取到 文件结束符(EOF),或者输入错误时,则会返回EOF

#define EOF (-1)

EOF即是常量 (-1)

getchar() 会在以下几种情况下返回 EOF

  1. 用户输入文件结束标志:在某些操作系统和终端模拟器中,用户可以通过特定的组合键来发送文件结束标志。例如,在Unix/Linux系统中,可以通过按下 Ctrl+D 来发送EOF;在Windows系统中,可以通过按下 Ctrl+Z 然后按回车来发送EOF。

  2. 到达文件尾部:如果你重定向了标准输入到一个文件,并且已经读取到了文件的末尾,那么 getchar() 将返回 EOF

  3. 输入错误:如果发生输入错误或读取失败,getchar() 也可能会返回 EOF。这种情况下,你可以使用 ferror() 函数来检查是否发生了错误。


getchar()函数使用中,也一定要记得清除缓冲区

当使用 getchar() 从标准输入读取一个字符,并且输入了一个字符然后按下回车键(Enter键)时,实际上会有两个字符进入输入缓冲区:一个是用户输入的那个字符,另一个是由回车键产生的换行符 \n

具体来说,如果你输入了字符 ‘a’ 并按下了回车键,那么输入缓冲区中的内容将是:

a\n
  • 第一次调用 getchar() 会读取并返回字符 ‘a’。
  • 如果你紧接着再调用 getchar(),它将读取并返回换行符 \n

如果你想在读取一个字符后立即处理后续的输入,而不想让换行符影响到下一个输入操作,则需要清除换行符。例如:

#include <stdio.h>

int main() {
    int ch;
    
    printf("请输入一个字符: ");
    ch = getchar(); 
    printf("你输入的字符是: %c\n", ch);
    
    消耗掉换行符
    while ((ch = getchar()) != '\n' && ch != EOF) {
       不做任何事情,只是消耗掉字符
    }
    
    现在可以继续进行其他输入操作,不会受到之前换行符的影响
    return 0;
}

Scanf()

Scanf ()同理,也是从缓冲区里读取数据,不同的是,他会根据格式字符串来匹配和解析这些数据 ,是一种用的格式化输入函数。

格式化指的是按照提供的格式来解析处理,例如你需要告诉他输入的字符格式说明符(告诉他如何处理)

scanf("%d %s",&num,str)

例如,处理单个字符,我们可以使用 %c格式说明符:

char ch;
scanf("%c",&ch);

这将会从缓冲区中读取下一个字符(无论是什么字符),并将其存储在变量 ch 中。如果缓冲区中有多个字符,scanf() 只会读取第一个字符。

当然,scanf()支持多字段输入 :可以一次读取多个字段,只要提供相应的格式说明符和变量。例如:

int a,b;
scanf("%d %d",&a,&b);

但是值得注意的是,按下回车后,\n依旧会存入缓冲区,这个时候,再次使用scanf()或者getchar(),则会导致读取失败。

int a,b;
scanf("%d %d",&a,&b);
int c=getchar(); //读取失败,不会提醒用户输入,将直接把数字10,对于其ascii字符 \n ,存入变量 c 中
char str[100];
int num;

用户输入 "hello" 并按回车
scanf("%s", str); // 读取 "hello",但 "\n" 仍留在缓冲区

如果紧接着再调用 scanf() 读取整数
scanf("%d", &num); // 这里会立即遇到 "\n",导致读取失败

所以我们在输入时,一定要清除缓冲区中的换行符

char str[100];
int num;

scanf("%s", str);
while (getchar() != '\n');//消耗换行符

scanf("%d", &num);//现在就可以继续存入了

scanf()在处理字符串时,对空格的处理方式有所不同。

使用 scanf("%s", str) 读取一个字符串,scanf() 会在遇到第一个空白字符之前停止读取,并且不会消耗掉这个空白字符(如换行符),它会留在缓冲区中。

char str[100];
scanf("%s", str); // 假设用户输入 "hello world" 并按下回车键

缓冲区中的内容是:

hello world \n
  1. scanf("%s", str) 只会读取 “hello” 并将其存储到 str 中。

    此时str数组里的内容是:h e l l o \0

  2. 空格以及之后的所有字符(包括 “world” 和换行符 \n)都会留在缓冲区中。

    缓冲区中将剩下:

    w o r l d \n 
    

    在这种情况下,而换行符\n依旧会存入缓冲区中

如果继续scanf(),它会继续从缓冲区中读取下一个非空白字符序列,即 “world”。因此,scanf() 不会将空格存入字符串中,而是跳过它们。

所以scanf()将会把剩下的world \n直接读取到str,所以对于这种情况,我们也要对其进行清除缓冲区的操作:

char str[100];
int num;

scanf("%s", str);
while (getchar() != '\n');
{

}
// 现在可以安全地读取整数
scanf("%d", &num);
#include <stdio.h>

int main() {
    char str[100];
    scanf("%s", str);
  
  检查剩余的输入
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF)
    {
        putchar(ch); // 回显剩余的字符
    }

    然后继续剩下的操作

    return 0;
}

注意事项

  1. 宽度限制:你可以为 %s 添加宽度限制来防止缓冲区溢出。例如,%99s 将最多读取99个字符,确保不会超出数组边界。

  2. scanf() 的返回值表示成功读取的项数,例如:

    int p = scanf("%d,%d",&i,&j);
    
    其返回值是2,说明成功读取了两个字段,可便于检查是否跟预期符合
    

Putchar()函数与Printf()函数

Putchar()和Printf()同样也是通过缓冲区来显示输入数据:全缓冲,行缓冲,无缓冲

**Pirntf()函数,**根据提供的格式字符串生成输出。

如果当前是行缓冲模式,那么在遇到换行符 \n 时,缓冲区中的内容会被刷新并显示在屏幕上。

如果没有换行符,数据可能会保留在缓冲区中,直到缓冲区满或者显式调用 fflush()

int main()
{
    printf("Hello, "); 
    可能会输出,因为没有换行符,取决于终端的行为,可能会自动刷新缓冲区。
    return 0;
}
int main()
{
	printf("world!\n");
    一定会立即输出 "Hello, world!\n" 因为遇到了换行符
    return 0;
}
 使用 fflush 显式刷新缓冲区
    printf("Hello, ");
    fflush(stdout);  // 立即刷新缓冲区,确保 "Hello, " 立即显示在屏幕上。
    printf("world!\n");继续输出 "world!\n"。
  • printf() 的输出通常是在遇到换行符 \n 或者缓冲区满时才会被刷新到屏幕上。
  • 某些终端或开发环境可能会自动刷新缓冲区,使得输出看起来像是立即显示的。
  • 如果你需要确保输出立即显示,可以使用 fflush(stdout) 来显式地刷新缓冲区。

Putchar()函数,printf()函数类似,如果当前是行缓冲模式,那么在遇到换行符 \n 时,缓冲区中的内容会被刷新并显示在屏幕上。如果没有换行符,数据可能会保留在缓冲区中,直到缓冲区满或者显式调用 fflush()

#include <stdio.h>

int main() {
    putchar('H');
    putchar('e');
    putchar('l');
    putchar('l');
    putchar('o');
    putchar(',');  // 到这里可能会输出,因为没有换行符,取决于终端
    putchar('\n'); // 到这里会立即输出 "Hello,\n"
    
    return 0;
}
  1. 标准输出(stdout)通常是行缓冲的,遇到换行符 \n 时会刷新缓冲区。
  2. 可以使用 fflush(stdout) 显式地刷新缓冲区。
  3. 标准错误流(stderr)通常是无缓冲的,输出会立即显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值