❶ 如何刷新輸入緩沖區
你可以試試用fgets代替
char sbuf[1024];
// ...
// fflush(stdin); // 用下面一句代替
fgets( sbuf, 1024, stdin ); // 將緩沖中的所有字元讀到sbuf,即達到清空緩沖的目的
// ...
❷ 這個C++程序都清空緩沖區了,為什麼錯誤輸入時還是不正常
getch();只接收一個字元,而漢字的一個字有2個字元,所以getch無法接收完畢.這也就導致了後續的錯誤.
getch()詳細介紹如下:
所在頭文件:conio.h
函數用途:從控制台讀取一個字元,但不顯示在屏幕上
函數原型:int getch(void)
返回值:讀取的字元
例如:
char ch;或int ch;
getch();或ch=getch();
用getch();會等待你按下任意鍵,再繼續執行下面的語句;
用ch=getch();會等待你按下任意鍵之後,把該鍵字元所對應的ASCII碼賦給ch,再執行下面的語句。
易錯點:
1.所在頭文件是conio.h。而不是stdio.h。
2.在使用之前要調用initscr(),結束時要調用endwin()。否則會出現不輸入字元這個函數
也會返回的情況。
3.在不同平台,輸入回車,getch()將返回不同數值,而getchar()統一返回10(即\n)
1)windows平台下ENTER鍵會產生兩個轉義字元 \r\n,因此getch返回13(\r)。
2)unix、 linux系統中ENTER鍵只產生 \n ,因此getch返回10(\n)。
3)MAC OS中ENTER鍵將產生 \r ,因此getch返回13(\r)。
getch();並非標准C中的函數,不存在C語言中。所以在使用的時候要注意程序的可移植性。國內C語言新手常常使用getch();來暫停程序且不知道此函數來源,建議使用getchar();(如果情況允許)代替此功能或更換一款編譯器。
❸ 龍書中提到的「雙緩沖區」在實際的編譯器中有運用嗎
您好,很高興能為您解決問題。
有應用的。比方說你要用GDI+畫圖片,
在圖片很大並且重畫頻率高的時候,容易造成閃爍。
這時,使用「雙緩沖區」現在內存畫板上畫好,然後一次性移到屏幕上,
閃的效果會好很多。這成為「雙緩沖畫圖」
雙緩沖區不僅僅在GDI+中有應用,在其他地方也處處可見。
❹ 開發大神門幫忙解答一下圖中這兩道編譯技術題,萬分感謝!!
實現一個時間類Time。將小時,分鍾和秒存儲為int型成員變數。要求該類中包含一個構造函數,訪問用的函數,一個推進當前時間的函數adv(),一個重新設置當前時間(即將當前時間設為00:00:00)的函數reset()和輸出結果函數print()。注意時間按數字式電子表格式顯示,即小時、分、秒分別用兩位表示,如果其中之一小於10,則前方補0,如22:01:00(中間不含空格),另外按該格式依次輸出時、分、秒後,以endl結尾。當輸入時間超出合法范圍(提示:注意上下界)時,請自動按照24小時制進行轉換,如輸入25:00:61,則輸出應為01:01:01。
#include
using namespace std;
class Time{
public:
Time(int hour,int minute,int second);
Time(){
hour=0;
minute=0;
second=0;
}
void showTime()
{if(second>=60){
minute++;second=second%60;
if(minute>=60){
hour++;minute=minute%60;
❺ 什麼叫「緩沖區益出保護」啊我的機器這幾天總出現這個問題
緩沖區溢出是當前一些軟體存在的最常見的安全隱患之一,通過提供一個惡意的輸入黑客可以改變進程的執行流程,緩沖區溢出能夠威脅到整個進程,機器,甚至相關的系統領域。如果運行的進程是在許可權比較高的用戶下面,比如administrator或者本地的系統帳戶(Local System Account),那麼黑客破壞所導致的損失將會很嚴重而且將會面臨更廣泛的潛在危脅。最近時期爆發的一些眾所周知的病毒像,紅色代碼病毒和震盪波蠕蟲病毒,都是C/C++代碼里存在著緩沖區溢出的結果。
1.什麼是緩沖區溢出?
~~~~~~~~~~~~~~~~~~~
buffer overflow,buffer overrun,smash the stack,trash the stack,
scribble the stack, mangle the stack,spam,alias bug,fandango on core,
memory leak,precedence lossage,overrun screw...
指的是一種系統攻擊的手段,通過往程序的緩沖區寫超出其長度的內容,造成緩沖區的溢出,從而破壞程序的堆棧,使程序轉而執行其它指令,以達到攻擊的目的。據統計,通過緩沖區溢出進行的攻擊占所有系統攻擊總數的80%以上。 造成緩沖區溢出的原因是程序中沒有仔細檢查用戶輸入的參數。
2.製造緩沖區溢出
~~~~~~~~~~~~~~~~
一個程序在內存中通常分為程序段,數據端和堆棧三部分。程序段里放著程序的機器碼和只讀數據。數據段放的是程序中的靜態數據。動態數據則通過堆棧來存放。在內存中,它們的位置是:
+------------------+ 內存低端
| 程序段 |
|------------------|
| 數據段 |
|------------------|
| 堆棧 |
+------------------+ 內存高端
當程序中發生函數調用時,計算機做如下操作:首先把參數壓入堆棧;然後保存指令寄存器(IP)中的內容做為返回地址(RET);第三個放入堆棧的是基址寄存器(FP);然後把當前的棧指針(SP)拷貝到FP,做為新的基地址;最後為本地變數留出一定空間,把SP減去適當的數值。
3.通過緩沖區溢出獲得用戶SHELL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果在溢出的緩沖區中寫入我們想執行的代碼,再覆蓋返回地址(ret)的內 容,使它指向緩沖區的開頭,就可以達到運行其它指令的目的。
低內存端 buffer sfp ret *str 高內存端
<------ [ ][ ][ ][ ]
棧頂 ^ | 棧底
|________________________|
通常,我們想運行的是一個用戶shell。
4.利用緩沖區溢出進行的系統攻擊
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果已知某個程序有緩沖區溢出的缺陷,如何知道緩沖區的地址,在那兒放入shell代碼呢?由於每個程序的堆棧起始地址是固定的,所以理論上可以通過反復重試緩沖區相對於堆棧起始位置的距離來得到。但這樣的盲目猜測可能要進行數百上千次,實際上是不現實的。解決的辦法是利用空指令NOP。在shell代碼前面放一長串的NOP,返回地址可以指向這一串NOP中任一位置,執行完NOP指令後程序將激活shell進程。這樣就大大增加了猜中的可能性。
三. 緩沖區溢出的保護方法
目前有四種基本的方法保護緩沖區免受緩沖區溢出的攻擊和影響。在3.1中介紹了強制寫正確的代碼的方法。在3.2中介紹了通過操作系統使得緩沖區不可執行,從而阻止攻擊者殖入攻擊代碼。這種方法有效地阻止了很多緩沖區溢出的攻擊,但是攻擊者並不一定要殖入攻擊代碼來實現緩沖區溢出的攻擊(參見 2.1節),所以這種方法還是存在很弱點的。在3.3中,我們介紹了利用編譯器的邊界檢查來實現緩沖區的保護。這個方法使得緩沖區溢出不可能出現,從而完全消除了緩沖區溢出的威脅,但是相對而言代價比較大。在3.4中我們介紹一種間接的方法,這個方法在程序指針失效前進行完整性檢查。這樣雖然這種方法不能使得所有的緩沖區溢出失效,但它的的確確阻止了絕大多數的緩沖區溢出攻擊,而能夠逃脫這種方法保護的緩沖區溢出也很難實現。然後在3.5,我們要分析這種保護方法的兼容性和性能優勢(與數組邊界檢查)。
3.1 編寫正確的代碼
編寫正確的代碼是一件非常有意義但耗時的工作,特別象編寫C語言那種具有容易出錯傾向的程序(如:字元串的零結尾),這種風格是由於追求性能而忽視正確性的傳統引起的。盡管花了很長的時間使得人們知道了如何編寫安全的程序,具有安全漏洞的程序依舊出現。因此人們開發了一些工具和技術來幫助經驗不足的程序員編寫安全正確的程序。
最簡單的方法就是用grep來搜索源代碼中容易產生漏洞的庫的調用,比如對strcpy和sprintf的調用,這兩個函數都沒有檢查輸入參數的長度。事實上,各個版本C的標准庫均有這樣的問題存在。
為了尋找一些常見的諸如緩沖區溢出和操作系統競爭條件等漏洞,代碼檢查小組檢查了很多的代碼。然而依然有漏網之魚存在。盡管採用了 strncpy和snprintf這些替代函數來防止緩沖區溢出的發生,但是由於編寫代碼的問題,仍舊會有這種情況發生。比如lprm程序就是最好的例子,雖然它通過了代碼的安全檢查,但仍然有緩沖區溢出的問題存在。
為了對付這些問題,人們開發了一些高級的查錯工具,如fault injection等。這些工具的目的在於通過人為隨機地產生一些緩沖區溢出來尋找代碼的安全漏洞。還有一些靜態分析工具用於偵測緩沖區溢出的存在。
雖然這些工具幫助程序員開發更安全的程序,但是由於C語言的特點,這些工具不可能找出所有的緩沖區溢出漏洞。所以,偵錯技術只能用來減少緩沖區溢出的可能,並不能完全地消除它的存在。除非程序員能保證他的程序萬無一失,否則還是要用到以下3.2到3.4部分的內容來保證程序的可靠性能。
3.2 非執行的緩沖區
通過使被攻擊程序的數據段地址空間不可執行,從而使得攻擊者不可能執行被殖入被攻擊程序輸入緩沖區的代碼,這種技術被稱為非執行的緩沖區技術。事實上,很多老的Unix系統都是這樣設計的,但是近來的Unix和MS Windows系統由於實現更好的性能和功能,往往在在數據段中動態地放入可執行的代碼。所以為了保持程序的兼容性不可能使得所有程序的數據段不可執行。
但是我們可以設定堆棧數據段不可執行,這樣就可以最大限度地保證了程序的兼容性。Linux和Solaris都發布了有關這方面的內核補丁。因為幾乎沒有任何合法的程序會在堆棧中存放代碼,這種做法幾乎不產生任何兼容性問題,除了在Linux中的兩個特例,這時可執行的代碼必須被放入堆棧中:
信號傳遞:
Linux通過向進程堆棧釋放代碼然後引發中斷來執行在堆棧中的代碼來實現向進程發送Unix信號。非執行緩沖區的補丁在發送信號的時候是允許緩沖區可執行的。
GCC的在線重用:
研究發現gcc在堆棧區里放置了可執行的代碼作為在線重用之用。然而,關閉這個功能並不產生任何問題,只有部分功能似乎不能使用。
非執行堆棧的保護可以有效地對付把代碼殖入自動變數的緩沖區溢出攻擊,而對於其他形式的攻擊則沒有效果(參見2.1)。通過引用一個駐留的程序的指針,就可以跳過這種保護措施。其他的攻擊可以採用把代碼殖入堆或者靜態數據段中來跳過保護。
3.3 數組邊界檢查
殖入代碼引起緩沖區溢出是一個方面,擾亂程序的執行流程是另一個方面。不象非執行緩沖區保護,數組邊界檢查完全放置了緩沖區溢出的產生和攻擊。這樣,只要數組不能被溢出,溢出攻擊也就無從談起。為了實現數組邊界檢查,則所有的對數組的讀寫操作都應當被檢查以確保對數組的操作在正確的范圍內。最直接的方法是檢查所有的數組操作,但是通常可以採用一些優化的技術來減少檢查的次數。目前有以下的幾種檢查方法:
3.3.1 Compaq C 編譯器
Compaq公司為Alpha CPU開發的C編譯器(在Tru64的Unix平台上是cc,在Alpha Linux平台上是ccc)支持有限度的邊界檢查(使用-check_bounds參數)。這些限制是:
只有顯示的數組引用才被檢查,比如「a[3]」會被檢查,而「*(a+3)」則不會。
由於所有的C數組在傳送的時候是指針傳遞的,所以傳遞給函數的的數組不會被檢查。
帶有危險性的庫函數如strcpy不會在編譯的時候進行邊界檢查,即便是指定了邊界檢查。
由於在C語言中利用指針進行數組操作和傳遞是如此的頻繁,因此這種局限性是非常嚴重的。通常這種邊界檢查用來程序的查錯,而且不能保證不發生緩沖區溢出的漏洞。
3.3.2 Jones & Kelly: C的數組邊界檢查
Richard Jones和Paul Kelly開發了一個gcc的補丁,用來實現對C程序完全的數組邊界檢查。由於沒有改變指針的含義,所以被編譯的程序和其他的gcc模塊具有很好的兼容性。更進一步的是,他們由此從沒有指針的表達式中導出了一個「基」指針,然後通過檢查這個基指針來偵測表達式的結果是否在容許的范圍之內。
當然,這樣付出的性能上的代價是巨大的:對於一個頻繁使用指針的程序如向量乘法,將由於指針的頻繁使用而使速度比本來慢30倍。
這個編譯器目前還很不成熟;一些復雜的程序(如elm)還不能在這個上面編譯,執行通過。然而在它的一個更新版本之下,它至少能編譯執行ssh軟體的加密軟體包。其實現的性能要下降12倍。
3.3.3 Purify:存儲器存取檢查
Purify是C程序調試時查看存儲器使用的工具而不是專用的安全工具。Purify使用「目標代碼插入」技術來檢查所有的存儲器存取。通過用Purify連接工具連接,可執行代碼在執行的時候數組的所有引用來保證其合法性。這樣帶來的性能上的損失要下降3-5倍。
3.3.4 類型-安全語言
所有的緩沖區溢出漏洞都源於C語言缺乏類型安全。如果只有類型-安全的操作才可以被允許執行,這樣就不可能出現對變數的強制操作。如果作為新手,可以推薦使用具有類型-安全的語言如Java和ML。
但是作為Java執行平台的Java虛擬機是C程序,因此通過攻擊JVM的一條途徑是使JVM的緩沖區溢出。因此在系統中採用緩沖區溢出防衛技術來使用強制類型-安全的語言可以收到意想不到的效果。
3.4 程序指針完整性檢查
程序指針完整性檢查和邊界檢查由略微的不同。與防止程序指針被改變不同,程序指針完整性檢查在程序指針被引用之前檢測到它的改變。因此,即便一個攻擊者成功地改變了程序的指針,由於系統事先檢測到了指針的改變,因此這個指針將不會被使用。
與數組邊界檢查相比,這種方法不能解決所有的緩沖區溢出問題;採用其他的緩沖區溢出方法就可以避免這種檢測。但是這種方法在性能上有很大的優勢,而且在兼容性也很好。
程序完整性檢查大體上有三個研究方向。在3.4.1中會介紹Snarskii為FreeBSD開發了一套定製的能通過監測cpu堆棧來確定緩沖區溢出的libc。在3.4.2中會介紹我們自己的堆棧保護方法所開發的一個編譯器,它能夠在函數調用的時候自動生成完整性檢測代碼。最後在3.4.3,我們介紹正在開發中的指針保護方法,這種方法類似於堆棧保護,它提供對所有程序指針的完整性的保護。
3.4.1 手寫的堆棧監測
Snarskii為FreeBSD開發了一套定製的能通過監測cpu堆棧來確定緩沖區溢出的libc。這個應用完全用手工匯編寫的,而且只保護 libc中的當前有效紀錄函數。這個應用達到了設計要求,對於基於libc庫函數的攻擊具有很好的防衛,但是不能防衛其它方式的攻擊。
3.4.2 堆棧保護:編譯器生成的有效紀錄完整性檢測
堆棧保護是一種提供程序指針完整性檢查的編譯器技術,通過檢查函數活動紀錄中的返回地址來實現。堆棧保護作為gcc的一個小的補丁,在每個函數中,加入了函數建立和銷毀的代碼。加入的函數建立代碼實際上在堆棧中函數返回地址後面加了一些附加的位元組。而在函數返回時,首先檢查這個附加的位元組是否被改動過。如果發生過緩沖區溢出的攻擊,那麼這種攻擊很容易在函數返回前被檢測到。
但是,如果攻擊者預見到這些附加位元組的存在,並且能在溢出過程中同樣地製造他們,那麼他就能成功地跳過堆棧保護的檢測。通常,我們有如下的兩種方案對付這種欺騙:
終止符號:
利用在C語言中的終止符號如0(null),CR,LF,-1(EOF)等不能在常用的字元串函數中使用,因為這些函數一旦遇到這些終止符號,就結束函數過程了。
隨機符號:
利用一個在函數調用時產生的一個32位的隨機數來實現保密,使得攻擊者不可能猜測到附加位元組的內容。而且,每次調用,附加位元組的內容都在改變,也無法預測。
通過檢查堆棧的完整性的堆棧保護法是從Synthetix方法演變來的。Synthetix方法通過使用准不變數來確保特定變數的正確性。這些特定的變數的改變是程序實現能預知的,而且只能在滿足一定的條件才能可以改變。這種變數我們稱為准變數。Synthetix開發了一些工具用來保護這些變數。
攻擊者通過緩沖區溢出而產生的改變可以被系統當做非法的動作。在某些極端的情況下,這些准不變數有可能被非法改變,這是就需要堆棧保護來提供更完善的保護了。
實驗的數據表明,堆棧保護對於各種系統的緩沖區溢出攻擊都有很好的保護作用,並能保持較好的兼容性和系統性能。隨後,我們用堆棧保護的方法重新構造了一個完整的Linux系統(Red Hat 5.1)。然後我們用XFree86-3.3.2-5和lsof的漏洞對此進行了攻擊,結果表明,這個系統有效地抵禦了這些攻擊。這些分析表明,堆棧保護能有效抵禦現在的和將來的基於堆棧的攻擊。
堆棧保護版本的Red Hat Linux 5.1已經在各種系統上運行了多年,包括個人的筆記本電腦和工作組文件伺服器。從我們的Web伺服器上可以得到這個版本,而且在我們的郵件列表裡已經有了 55個成員。出了僅有的一次例外,這個系統和本來的系統工作完全一樣,這表明堆棧保護並不對系統的兼容性構成很大的影響。
我們已經用各種性能測試來評測堆棧保護的性能。Mircobenchmarks的結果表明在函數的調用,堆棧保護中增加了系統的
開銷。而在網路的測試中(需要用到堆棧保護的地方),則表明這種開銷不是很大。
我們的第一個測試對象是SSH,它提供了極強的加密和認證,用來替代Berkeley的r系列指令。SSH使用了軟體加密,因此系統的佔用的帶寬不大,我們用網路間復制一個大的文件來測試帶寬:
scp bigsource localhost:bigdest
測試結果表明:堆棧保護幾乎不影響SSH的網路吞吐性能。
第二個測試使用了Apache Web伺服器。如果這種伺服器存在基於堆棧的攻擊,那麼攻擊者就可以輕易地取得Web伺服器的控制權,允許攻擊者閱讀隱秘的內容和肆意篡改主頁的內容。同時,Web伺服器也是對性能和帶寬要求較高的一個伺服器部件。
我們用WebStone對帶有和不帶堆棧保護的Apache Web伺服器進行了測試。
和SSH一樣,他們的性能幾乎沒有區別。在客戶數目較少的情況下,帶有保護的伺服器性能比不帶保護的略微好些,在客戶端數目多的時候,不帶保護的性能好些。在最壞的情況下,帶保護的伺服器比不帶保護的要差8%的連接性能,而在平均延時上保持優勢。象以前一樣,我們把這些歸結為雜訊的影響。因此,我們的結論是:堆棧保護對Web伺服器系統性能沒有重大的影響。
3.4.3 指針保護:編譯器生成程序指針完整性檢查
在堆棧保護設計的時候,沖擊堆棧構成了緩沖區溢出攻擊的常見的一種形式。有人推測存在一種模板來構成這些攻擊(在1996年的時候)。從此,很多簡單的漏洞被發現,實施和補丁了,很多攻擊者開始用在第二部分中描述的更一般的方法實施緩沖區溢出攻擊。
指針保護是堆棧保護針對這種情況的一個推廣。通過在所有的代碼指針之後放置附加位元組來檢驗指針在被調用之前的合法性。如果檢驗失敗,會發出報警信號和退出程序的執行,就如同在堆棧保護中的行為一樣。這種方案有兩點需要注意:
附加位元組的定位:
附加位元組的空間是在被保護的變數被分配的時候分配的,同時在被保護位元組初始化過程中被初始化。這樣就帶來了問題;為了保持兼容性,我們不想改變被保護變數的大小,因此我們不能簡單地在變數的結構定義中加入附加字。還有,對各種類型也有不同附加位元組數目。
檢查附加位元組:
每次程序指針被引用的時候都要檢查附加位元組的完整性。這個也存在問題;因為「從存取器讀」在編譯器中沒有語義;編譯器更關心指針的使用,而各種的優化演算法傾向於從存儲器中讀入變數。
還有隨著不同類型的變數,讀入的方法也各自不同。
我們已經開發了指針保護的一個原型(還是基於gcc的),通過附加位元組來保護靜態分配的函數指針,但不適用於結構和數組類型。這個計劃還遠沒有完成。一旦這個項目完成了,那麼用它和堆棧保護構成的可執行代碼將不會受到緩沖區溢出的攻擊了。
目前為止,只有很少一部分使用非指針變數的攻擊能逃脫指針保護的檢測。但是,可以通過在編譯器上強制對某一變數加入附加位元組來實現檢測,這時需要程序員自己手工加入相應的保護了。
3.5 兼容性和性能的考慮
程序指針完整性檢查與邊界檢查相比,並不能防止所有的緩沖區溢出問題。然而在執行的性能和兼容性上具有相當的優勢:
性能:
邊界檢查必須在每個數組元素操作時完成一次檢查。相比之下,程序指針檢查只在被引用的時候實現檢查。無論在C還是在C++中,這種花在程序指針引用上的開銷始終比數組的指針引用小。
應用效能:
邊界檢查最難實現之處在於在C語言中,很能確定數組的邊界。這是由於在C中,數組的概念和通用指針的混用造成的。由於一個指針是一個獨立的對象,沒有與特定的邊界條件關聯,只有一個系統的機器字來存儲它,而標識邊界信息的數據卻沒有存放。因此需要特殊的方法來恢復這些信息;數組的引用將不在是一個簡單的指針,而是一個對緩沖區描述的指針組。
與現有代碼的兼容性:
一些邊界檢查方法為了與現有的代碼保持兼容而在系統的性能上得到了損失。而另一些則用別的方法達到目的。這樣就打破的傳統的C的轉換規則,轉而產生了一類新的C編譯器,只能編譯C的一個子集,有的還不能使用指針或者需要別的改變。
四 有效的組合
在這里我們研究、比較在第二部分描述的各種漏洞攻擊和在第三部分描述的防衛方法,以此來確定何種組合能完全消除緩沖區溢出問題。但是我們沒有把邊界檢查計算在內,因為它能有效地防止所有的緩沖區溢出,但是所需的開銷也是驚人的。
最普通的緩沖區溢出形式是攻擊活動紀錄然後在堆棧中殖入代碼。這種類型的攻擊在1996年中有很多紀錄。而非執行堆棧和堆棧保護的方法都可以有效防衛這種攻擊。非執行堆棧可以防衛所有把代碼殖入堆棧的攻擊方法,堆棧保護可以防衛所有改變活動紀錄的方法。這兩種方法相互兼容,可以同時防衛多種可能的攻擊。
剩下的攻擊基本上可以用指針保護的方法來防衛,但是在某些特殊的場合需要用手工來實現指針保護。全自動的指針保護需要對每個變數加入附加位元組,這樣使得指針邊界檢查在某些情況下具有優勢。
最為有趣的是,第一個緩沖區溢出漏洞--Morris蠕蟲使用了現今所有方法都無法有效防衛的方法,但是卻很少有人用到,也許是這種方法過於復雜的緣故吧。
五. 結論
在本文中,我們詳細描述和分析了緩沖區溢出的攻擊和防衛方法。由於這種攻擊是目前常見的攻擊手段,所以進行這個方面的研究工作是有意義和成效的。研究的結果表明,堆棧保護方法和非執行緩沖區方法對於當前絕大多數的攻擊都能有效地防禦,指針保護的方法可以對剩下的攻擊進行有效的防禦。最後聲明的是對於Morris蠕蟲的攻擊,迄今還沒有有效的防禦手段
❻ c語言中用「空格符」來處理緩沖區是個什麼原理
這個是有關scanf的老生常談了。
① 這個原理是基於標准C裡面的whitespace的定義,空格、回車或換行符,製表符等被視為whitespace,而scanf中的格式「 %c」,已說明是按空格+字元來處理輸入的。沒有空格則格式中的空格被忽略, 如果有空格或回車換行,被當作空格對待。
② 這是技巧的出現,的確是因scanf的設計問題而應運而生的。scanf用的不好容易溢出; 一般認為應該避免使用它,用替代的方式如:while ((c = getchar()) != EOF && c != '\n');
③ 對「輸入流」作fflush()不是標準的C方式, 意味著不同的編譯器可能會有不同的結果,所以絕對應該避免。
❼ C語言scanf函數輸入時鍵盤緩沖區\n的問題
你首先要明白,從鍵盤讀入鍵盤緩沖區(buffer)的數據都是以ASCII碼存儲的(包括回車)。
程序1
#include "stdio.h"
void main()
{
char a;
char b;
scanf("%d",&a);
scanf("%d",&b);
printf("%d %d",a,b);
}
鍵盤輸入
97<回車>
第一次回車後,buffer中的ASCII:39h,37h,0AH(0A是換行的ASCII), scanf會根據格式字元串中的第一個%d對buffer按位元組順序讀取,當讀取到0A時,認為%d型的數據結束,此時把已經讀取到的39h,37h依據%d轉為整型數據97存儲在字元型變數a中。(這里是除去了掃描截止點0AH)
此時buffer中已經無任何數據了。
96<回車>
第二次回車後,按同樣的流程,scanf會根據格式字元串中的第二個%d對buffer按位元組順序讀取。最終b得到96.
此時buffer中已經無任何數據了。
輸出
97 96
程序2
#include "stdio.h"
void main()
{
char a;
char b;
scanf("%c",&a);
scanf("%c",&b);
printf("%d %d",a,b);
}
鍵盤輸入
9<回車>buffer:39H,0AH
因為scanf會按照第一個%c格式掃描buffer(只掃描一個位元組就結束),然後把掃描到的39H直接送到變數a(當以%d格式讀出來時,39H就是57)
此時,buffer中只有:0AH。
然後,scanft又遇到第二個%c,繼續掃描buffer,得到0aH並送入變數b.
此時buffer中已經無任何數據了
輸出
57 10
程序3
#include "stdio.h"
void main()
{
char a[100];
char b[100];
scanf("%s",a);
scanf("%s",b);
printf("%s %s",a,b);
}
鍵盤輸入
abc<回車>
第一次回車後,buffer:61H,62H,63H,0AH。
scanf會按照%s的格式對buffer按位元組順序掃描,當掃描到0AH時,結束掃描(按照%s的要求,空格20H也是掃描結束點)。
然後把掃描到的(除去最後一個判斷掃描截至的位元組0AH)數據直接送入以a為起始地址的字元串。
此時,buffer無任何數據了。
def<回車>
第二次回車後,buffer:65H,66H,67H,0AH.掃描的流程與上面的完全一致。
輸出
abc def
程序4
#include <stdio.h>
void main()
{
int i;
char j;
for(i=0;i<2;i++)
scanf("%c",&j);/*注意這里%前沒有空格*/
printf("%d",j);
}
鍵盤輸入
1<回車>,
這里scanf執行了兩次(i==0時,與i==1時),而且每次都是想對j賦值。
第一次scanf,按%c的要求,只掃描buffer中的一個位元組,但是buffer中並不數據,於是要求鍵盤輸入數據到buffer,此時的1<回車>代表向buffer中輸入了:31H,0AH。
然後按%c的要求,只掃描buffer中的一個位元組:31h,並將它直接送入變數j.
此時,buffer中還留下:0AH。
第二次scanf要求鍵盤輸入數據,按%c的要求,只掃描buffer中的一個位元組:0Ah,並將它直接送入變數j.
此時,buffer無數據了。
最後,你用%d格式輸出j的值(0AH換成整型就是10)
輸出
10
程序5
#include <stdio.h>
void main()
{
int i;
char j;
for(i=0;i<2;i++)
scanf(" %c",&j);/*注意這里%前有一個空格*/
printf("%d",j);
}
1<回車>2<enter>的情況:
scanf會按照格式控制字元串的要求,順序掃描buffer.
但是你其中有一個空格,這個很特殊,我也是第一次發現這個問題(一般我都不會在scanf中加入任何常量字元)
我測試了一下:我發現這個空格有吸收回車(0AH)和空格(20H)的「神奇功效」,吸收之後再要求buffer給一個位元組,直到這個位元組不是0AH或者 20H,此時把這個位元組交給下一個格式字串。
第一次循環時遇到格式字串空格,就掃描buffer中的一個位元組,但是buffer中無數據,要求從鍵盤輸入數據:1〈回車〉,buffer中有數據了——31H,0AH。再讀取到位元組31H,scanf發現這個並不是0AH/20H,就把這個位元組31H交給格式字元%c處理。
循環結束,此時buffer裡面還有:0AH.
第二次循環時遇到格式字串空格,就掃描buffer中的一個位元組——0AH,發現是0AH/20H,於是就要求buffer再來一個位元組。此時buffer裡面已經沒有數據了,要求鍵盤輸入:2<enter>.
buffer中有數據了——32H,0AH。於是再讀一個位元組31H,scanf發現這個並不是0AH/20H,就把這個位元組32H交給格式字元%c處理(j最終得到32H)。
循環結束,此時buffer裡面還有:0AH.
這里有一篇關於Printf的帖子:http://blog.csdn.net/arong1234/archive/2008/05/18/2456455.aspx
程序6
#include "stdio.h"
void main()
{
int a;
int b;
scanf("%c",&a);
scanf("%c",&b);
printf("%d %d",a,b);
}
鍵盤輸入
1<回車>
問題5:
你的編譯器VC認為%d數據應該是4個位元組,但是你採用的是%c讀數據,
scanf("%c",&a);此句讀到的是1的ascii碼:31h.然後把31H直接送入地址&a(而並沒有改寫a的三個高位元組地址)。
scanf("%c",&b);同理。
你可以用printf("a=%x,b=%x\n",a,b);來驗證我說的。它們的最低位元組肯定是31H,0AH。
PS1:
當你把 int a;int b;放在main()外進行定義時,a,b的初值就是0。此時你會得到正確的結果。
當你把 int a;int b;放在main()內進行定義時,a,b不會被初始化(它們的三個三個高位元組地址的內容是不確定的),你就會得到上面錯誤的結果。(定義的動態變數都不會被初始化,靜態變數會被初始化為0)
PS2:以下也是不正確的用法。
char c;
scanf("%d",&c);/當你用%d給c賦值時,會對從&c開始的連續4個位元組進行賦值。當從buffer得到的值是在一個位元組范圍內(-128~127),下面是可以正常輸出的。但是不管怎樣,這樣做是很危險的——越界。
printf("%d",c);
=================請你測試下這個程序========================
#include "stdio.h"
void main()
{
char c[4],i=4;
scanf("%d",c);/*請輸入258<回車>*/
while(i-->0)
printf("%02x ",c[i]);
printf("\n");
}/*如果得到的結果是00 00 00 01 02就說明我的結論是正確的(258的轉為16進制數就是00 00 01 02H,然後scanf會把這個數放入以c為起始地址的)
================以下程序也是======================
#include "stdio.h"
void main()
{
char c,i=4;
char *p=&c;
scanf("%d",&c);/*請輸入258<回車>*/
while(i-->0)
printf("%02x ",p[i]);
printf("\n");
}
❽ 設置緩沖區的原則是什麼
虛擬內存讓系統更流暢 1、虛擬內存的作用內存在計算機中的作用很大,電腦中所有運行的程序都需要經過內存來執行,如果執行的程序很大或很多,就會導致內存消耗殆盡。為了解決這個問題,Windows中運用了虛擬內存技術,即拿出一部分硬碟空間來充當內存使用,當內存佔用完時,電腦就會自動調用硬碟來充當內存,以緩解內存的緊張。舉一個例子來說,如果電腦只有128MB物理內存的話,當讀取一個容量為200MB的文件時,就必須要用到比較大的虛擬內存,文件被內存讀取之後就會先儲存到虛擬內存,等待內存把文件全部儲存到虛擬內存之後,跟著就會把虛擬內里儲存的文件釋放到原來的安裝目錄里了。下面,就讓我們一起來看看如何對虛擬內存進行設置吧。2、虛擬內存的設置對於虛擬內存主要設置兩點,即內存大小和分頁位置,內存大小就是設置虛擬內存最小為多少和最大為多少;而分頁位置則是設置虛擬內存應使用那個分區中的硬碟空間。對於內存大小的設置,如何得到最小值和最大值呢?你可以通過下面的方法獲得:選擇「開始→程序→附件→系統工具→系統監視器」(如果系統工具中沒有,可以通過「添加/刪除程序」中的Windows安裝程序進行安裝)打開系統監視器,然後選擇「編輯→添加項目」,在「類型」項中選擇「內存管理程序」,在右側的列表選擇「交換文件大小」。這樣隨著你的操作,會顯示出交換文件值的波動情況,你可以把經常要使用到的程序打開,然後對它們進行使用,這時查看一下系統監視器中的表現值,由於用戶每次使用電腦時的情況都不盡相同,因此,最好能夠通過較長時間對交換文件進行監視來找出最符合您的交換文件的數值,這樣才能保證系統性能穩定以及保持在最佳的狀態。找出最合適的范圍值後,在設置虛擬內存時,用滑鼠右鍵點擊「我的電腦」,選擇「屬性」,彈出系統屬性窗口,選擇「性能」標簽,點擊下面「虛擬內存」按鈕,彈出虛擬內存設置窗口,點擊「用戶自己指定虛擬內存設置」單選按鈕,「硬碟」選較大剩餘空間的分區,然後在「最小值」和「最大值」文本框中輸入合適的范圍值。如果您感覺使用系統監視器來獲得最大和最小值有些麻煩的話,這里完全可以選擇「讓Windows管理虛擬內存設置」。 3、調整分頁位置Windows 9x的虛擬內存分頁位置,其實就是保存在C盤根目錄下的一個虛擬內存文件(也稱為交換文件)Win386.swp,它的存放位置可以是任何一個分區,如果系統盤C容量有限,我們可以把Win386.swp調到別的分區中,方法是在記事本中打開System.ini(C:\Windows下)文件,在[386Enh]小節中,將「PagingDrive=C:WindowsWin386.swp」,改為其他分區的路徑,如將交換文件放在D:中,則改為「PagingDrive=D:Win386.swp」,如沒有上述語句可以直接鍵入即可。而對於使用Windows 2000和Windows XP的,可以選擇「控制面板→系統→高級→性能」中的「設置→高級→更改」,打開虛擬內存設置窗口,在驅動器[卷標]中默認選擇的是系統所在的分區,如果想更改到其他分區中,首先要把原先的分區設置為無分頁文件,然後再選擇其他分區。
❾ 如何加大 cout 緩沖區
cout緩沖區實現在C++標准庫內(libstdc++,不是標准類/模板庫),想加大cout 緩沖區的話修改c++標准庫源碼後重編譯替換掉原來的庫文件即可。
❿ 什麼叫做當前動態緩沖區空間不足10%,還有怎樣恢復分區數據保存分區數據
所謂的動態緩沖區空間不足,就是說你系統分區的剩餘容量不足以進行當前的操作。想繼續就得刪除一些不必要的文件騰出足夠的空間。 保存分區數據:把所選分區做個備份。 恢復分區數據:把備份中的數據恢復到分區使用。