詳細聊聊c語言中的緩沖區問題

發現問題

你是不是總會出現當你輸入的時候(你想的是隻輸出一個內容),但是最後卻輸入兩個。

比如下面這個例子

 那這到底是是哪出瞭問題呢?

沒錯這就是關於緩沖區的問題。

我們先仔細瞭解這個題目

例題

判斷字母是否為元音字母包括大小寫。

看代碼實現(錯誤的)

#include<stdio.h>
int main()
{
	int i = 0;
	char ch = 0;
	char yyzm[20] = { 'a','A','e','E','i','I','o','O','u','U' };
	while(scanf("%c", &ch)!=EOF)
	{
		for (i = 0; i < 10; i++)
		{
			if (ch == yyzm[i])
			{
				printf("元音字母\n");
				break;
			}
		}
		if (i == 10)
		{
			printf("輔音字母\n");
		}
	}
	
	return 0;
}

問題原因

我們一般怎麼輸入呢?

我們先輸入元音字母o然後在按一下回車,一般輸入都是這樣輸入的到底是哪出瞭問題呢?

沒錯就是那個回車惹的禍。每當我們輸入一個字母的時候,scanf讀取字母之後,就會放入緩沖區中,回車一下當然也會放個’\n’字符也就是空格,當計算機拿取字符的時候先拿走一個字符,接著看裡面還有沒有字符,如果有字符就會繼續讀取,如果沒有則進行下面的內容。

在我們這個代中由於是多次輸入數據,就會讀入字符後第一個if語句結束,如果還有字符的話,計算機就會繼續拿字符,這時就拿瞭一個’\n’,’\n’不是元音字母就會進入下一個if語句輸出。

那我們如何解決呢?

解決方法一:

在後面加入getchar(),它的作用就是清理緩存區,由於輸入字符,計算機是一個一個字符讀取的,又因為我們多次輸入,所以getchar總是會讀取那個’\n’;

解決方法二:

我們在scanf%c後面加個’\n’,由於是一個一個讀取字符的,如果後面有’\n’,就會把\n也拿走。

解決方案三:

在%c前面加個空格,這樣做的目的是每次讀取下一個字符時,就會把上一個字符後面的’\n’清理掉。

正確的代碼:

#include<stdio.h>
int main()
{
	int i = 0;
	char ch = 0;
	char yyzm[20] = { 'a','A','e','E','i','I','o','O','u','U' };
	while(scanf(" %c", &ch)!=EOF)//可以在%c後面加個'\n',也可以在%c前面加個空格,目的是清理緩沖區
	{
		for (i = 0; i < 10; i++)
		{
			if (ch == yyzm[i])
			{
				printf("元音字母\n");
				break;
			}
		}
		if (i == 10)
		{
			printf("輔音字母\n");
		}
	}
	//getchar();清理緩沖區
	return 0;
}

出錯二

當我們用scanf輸入字符串的時候,如果遇到空格也會出現問題,這時我們就可以引入另外一個函數那就是gets函數

gets函數引入

gets函數的優點與scanf對比:

gets() 函數不僅比 scanf 簡潔,而且,就算輸入的字符串中有空格也可以直接輸入,不用像 scanf 那樣要定義多個字符數組。

關於使用 gets() 函數需要註意:使用 gets() 時,系統會將最後“敲”的換行符從緩沖區中取出來,然後丟棄,所以緩沖區中不會遺留換行符。這就意味著,如果前面使用過 gets(),而後面又要從鍵盤給字符變量賦值的話就不需要吸收回車清空緩沖區瞭,因為緩沖區的回車已經被 gets() 取出來扔掉瞭。(此段話是在網上查到的,整理為復習準備,請見諒)。

我們來做個題吧

逆序字符串

#include<stdio.h>
#include<string.h>
void swap(char* str)
{
    int i = 0;
    int len = strlen(str);
    for (i = 0; i < len / 2; i++)
    {
        char tmp = 0;
        tmp = str[i];
        str[i] = str[len - i - 1];
        str[len - i - 1] = tmp;
    }
    printf("%s", str);
}
int main()
{
    //逆序字符串的內容
    char str[100];
    int i = 0;
    gets(str);
    swap(str);
 
    return 0;
}

為什麼要引入緩沖區

比如我們從磁盤裡取信息,我們先把讀出的數據放在緩沖區,計算機再直接從緩沖區中取數據,等緩沖區的數據取完後再去磁盤中讀取,這樣就可以減少磁盤的讀寫次數,再加上計算機對緩沖區的操作大大快於對磁盤的操作,故應用緩沖區可大大提高計算機的運行速度。

又比如,我們使用打印機打印文檔,由於打印機的打印速度相對較慢,我們先把文檔輸出到打印機相應的緩沖區,打印機再自行逐步打印,這時我們的CPU可以處理別的事情。

現在您基本明白瞭吧,緩沖區就是一塊內存區,它用在輸入輸出設備和CPU之間,用來緩存數據。它使得低速的輸入輸出設備和高速的CPU能夠協調工作,避免低速的輸入輸出設備占用CPU,解放出CPU,使其能夠高效率工作。 

緩沖區的類型

緩沖區 分為三種類型:全緩沖、行緩沖和不帶緩沖。

1) 全緩沖

在這種情況下,當填滿標準I/O緩存後才進行實際I/O操作。全緩沖的典型代表是對磁盤文件的讀寫。

2) 行緩沖

在這種情況下,當在輸入和輸出中遇到換行符時,執行真正的I/O操作。這時,我們輸入的字符先存放在緩沖區,等按下回車鍵換行時才進行實際的I/O操作。典型代表是標準輸入(stdin)和標準輸出(stdout)。

3) 不帶緩沖

也就是不進行緩沖,標準出錯情況stderr是典型代表,這使得出錯信息可以直接盡快地顯示出來。

總結

推薦閱讀: