詳細(xì)聊聊c語言中的緩沖區(qū)問題
發(fā)現(xiàn)問題
你是不是總會出現(xiàn)當(dāng)你輸入的時候(你想的是只輸出一個內(nèi)容),但是最后卻輸入兩個。
比如下面這個例子

?那這到底是是哪出了問題呢?
沒錯這就是關(guān)于緩沖區(qū)的問題。
我們先仔細(xì)了解這個題目
例題
判斷字母是否為元音字母包括大小寫。
看代碼實現(xià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)
{
for (i = 0; i < 10; i++)
{
if (ch == yyzm[i])
{
printf("元音字母\n");
break;
}
}
if (i == 10)
{
printf("輔音字母\n");
}
}
return 0;
}
問題原因
我們一般怎么輸入呢?
我們先輸入元音字母o然后在按一下回車,一般輸入都是這樣輸入的到底是哪出了問題呢?
沒錯就是那個回車惹的禍。每當(dāng)我們輸入一個字母的時候,scanf讀取字母之后,就會放入緩沖區(qū)中,回車一下當(dāng)然也會放個'\n'字符也就是空格,當(dāng)計算機(jī)拿取字符的時候先拿走一個字符,接著看里面還有沒有字符,如果有字符就會繼續(xù)讀取,如果沒有則進(jìn)行下面的內(nèi)容。
在我們這個代中由于是多次輸入數(shù)據(jù),就會讀入字符后第一個if語句結(jié)束,如果還有字符的話,計算機(jī)就會繼續(xù)拿字符,這時就拿了一個'\n','\n'不是元音字母就會進(jìn)入下一個if語句輸出。
那我們?nèi)绾谓鉀Q呢?
解決方法一:
在后面加入getchar(),它的作用就是清理緩存區(qū),由于輸入字符,計算機(jī)是一個一個字符讀取的,又因為我們多次輸入,所以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前面加個空格,目的是清理緩沖區(qū)
{
for (i = 0; i < 10; i++)
{
if (ch == yyzm[i])
{
printf("元音字母\n");
break;
}
}
if (i == 10)
{
printf("輔音字母\n");
}
}
//getchar();清理緩沖區(qū)
return 0;
}
出錯二
當(dāng)我們用scanf輸入字符串的時候,如果遇到空格也會出現(xiàn)問題,這時我們就可以引入另外一個函數(shù)那就是gets函數(shù)
gets函數(shù)引入
gets函數(shù)的優(yōu)點與scanf對比:
gets() 函數(shù)不僅比 scanf 簡潔,而且,就算輸入的字符串中有空格也可以直接輸入,不用像 scanf 那樣要定義多個字符數(shù)組。
關(guān)于使用 gets() 函數(shù)需要注意:使用 gets() 時,系統(tǒng)會將最后“敲”的換行符從緩沖區(qū)中取出來,然后丟棄,所以緩沖區(qū)中不會遺留換行符。這就意味著,如果前面使用過 gets(),而后面又要從鍵盤給字符變量賦值的話就不需要吸收回車清空緩沖區(qū)了,因為緩沖區(qū)的回車已經(jīng)被 gets() 取出來扔掉了。(此段話是在網(wǎng)上查到的,整理為復(fù)習(xí)準(zhǔn)備,請見諒)。
我們來做個題吧
逆序字符串
#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()
{
//逆序字符串的內(nèi)容
char str[100];
int i = 0;
gets(str);
swap(str);
return 0;
}

為什么要引入緩沖區(qū)
比如我們從磁盤里取信息,我們先把讀出的數(shù)據(jù)放在緩沖區(qū),計算機(jī)再直接從緩沖區(qū)中取數(shù)據(jù),等緩沖區(qū)的數(shù)據(jù)取完后再去磁盤中讀取,這樣就可以減少磁盤的讀寫次數(shù),再加上計算機(jī)對緩沖區(qū)的操作大大快于對磁盤的操作,故應(yīng)用緩沖區(qū)可大大提高計算機(jī)的運(yùn)行速度。
又比如,我們使用打印機(jī)打印文檔,由于打印機(jī)的打印速度相對較慢,我們先把文檔輸出到打印機(jī)相應(yīng)的緩沖區(qū),打印機(jī)再自行逐步打印,這時我們的CPU可以處理別的事情。
現(xiàn)在您基本明白了吧,緩沖區(qū)就是一塊內(nèi)存區(qū),它用在輸入輸出設(shè)備和CPU之間,用來緩存數(shù)據(jù)。它使得低速的輸入輸出設(shè)備和高速的CPU能夠協(xié)調(diào)工作,避免低速的輸入輸出設(shè)備占用CPU,解放出CPU,使其能夠高效率工作。?
緩沖區(qū)的類型
緩沖區(qū) 分為三種類型:全緩沖、行緩沖和不帶緩沖。
1) 全緩沖
在這種情況下,當(dāng)填滿標(biāo)準(zhǔn)I/O緩存后才進(jìn)行實際I/O操作。全緩沖的典型代表是對磁盤文件的讀寫。
2) 行緩沖
在這種情況下,當(dāng)在輸入和輸出中遇到換行符時,執(zhí)行真正的I/O操作。這時,我們輸入的字符先存放在緩沖區(qū),等按下回車鍵換行時才進(jìn)行實際的I/O操作。典型代表是標(biāo)準(zhǔn)輸入(stdin)和標(biāo)準(zhǔn)輸出(stdout)。
3) 不帶緩沖
也就是不進(jìn)行緩沖,標(biāo)準(zhǔn)出錯情況stderr是典型代表,這使得出錯信息可以直接盡快地顯示出來。
總結(jié)
相關(guān)文章
C++實現(xiàn)LeetCode(103.二叉樹的之字形層序遍歷)
這篇文章主要介紹了C++實現(xiàn)LeetCode(103.二叉樹的之字形層序遍歷),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C++利用std::forward_list查找插入數(shù)據(jù)方法示例
這篇文章主要給大家介紹了關(guān)于C++利用std::forward_list查找插入數(shù)據(jù)的相關(guān)資料,文中先對std::forward_list進(jìn)行了詳細(xì)的介紹,而后通過示例代碼給大家介紹了查找的方法,需要的朋友可以參考借鑒,下面話不多說了,來一起看看吧。2017-08-08
詳解C++中賦值,關(guān)系,函數(shù)調(diào)用運(yùn)算符重載的實現(xiàn)
本文主要為大家講解一下三個C++中的運(yùn)算符重載,分別是賦值運(yùn)算符重載、關(guān)系運(yùn)算符重載和函數(shù)調(diào)用運(yùn)算符重載,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-06-06

