




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
第八章輸入輸出流(input/output
stream)的基本概念及原理
8.1預(yù)定義數(shù)據(jù)類型的輸入/輸出
8.1.1基本情況及其優(yōu)點
C++輸入輸出流的優(yōu)點:
(-)重載運算符“<<”和“>>”能以函數(shù)重載的形式極大地擴大用途,在輸入輸
出流中充分體現(xiàn)多態(tài)性。
C語言的輸入/輸出系統(tǒng)本來就靈活性大、功能比較完善。但它有一個較大缺
點:無法處理眾多的用戶自定義數(shù)據(jù)類型(主要是類及其對象)。例如,有一個結(jié)
構(gòu)類型exampl如下:
structexampl{
intj;
charstr[80];
}str_ex;
如欲輸出此結(jié)構(gòu)對象str_ex的兩個成員的內(nèi)容,因而籠統(tǒng)地使用如下輸出語句
printf,
printf("%exampl”,str_ex);
則將會出現(xiàn)編譯錯誤。而C++的輸出解入系統(tǒng)則能很好地解決這個問題。
(二)類型安全(type-safe)
[例1]C語言輸出語句中的類型錯誤(第一章已看過)
#include<stdio.h>
voidmain()
(
inti=3;
doubled=4.4;
printf(4<%d\t%f\n?,,i,d);
)
運行結(jié)果:
34.400000對!
但如寫錯為:
printf("%d\t%d\n”,i,d);
則編譯時不出錯,但運行結(jié)果錯,為:
3-26214
但在C++中只須寫cout?i?"”《d?endl;而不必寫出變量類型!就能得出
34.4,對!
始終不會出錯!
(三)通過緩存增加功能。
(四)附帶優(yōu)點是書寫方便以及顯示中沒有冗余字符,能自動略去浮點數(shù)尾數(shù)中的零
(但如用戶希望顯示多余的零,也可以做到)。
C++的輸入/輸出系統(tǒng)是對流的操作,也即操作數(shù)據(jù)使其流向?qū)ο?,或從對象?/p>
出。
什么是流?流是從源頭到目的的數(shù)據(jù)流動。當(dāng)鍵入字符時,字符從鍵盤流入程序
中;當(dāng)將數(shù)據(jù)寫入磁盤文件中時,數(shù)據(jù)自程序流動至磁盤上。
C++輸入/輸出流庫是使用繼承方法建立起來的一個輸入/輸出類庫,它具有兩
個平行的基類,即streambuf類和iOS類。所有其它流類都是從它們直接或間接地派
生出來的。
-streambuf類用于提供物理設(shè)備的接口,它提供緩沖或處理流的通用方法。它作
為一個虛基類,具有類層次如下:
streambuf
filebufstrstreambufconbuf
圖8.1
iOS類及其派生類用于為用戶提供使用流類的接口。它使用streambuf完成檢查錯
誤的格式化輸入/輸出操作,并支持對streambuf的緩沖區(qū)進(jìn)行輸入/輸出時的格式
化或非格式化轉(zhuǎn)換。
ios類作為流庫中的一個虛基類,派生出許多派生類,其主要層次如下:
istreamfstreambaseostream
fstream
istream-withassignostream-withassign
iostream
iostream-withassign
圖8.2
8.1.2預(yù)定義流(標(biāo)準(zhǔn)流)的基本原理
預(yù)定義輸出輸入流涉及較多的頭文件有四個:ios.h,istream.h,ostream.h和
iostream.ho下面分別介紹。
8?1?2.1輸出流的基本概念
流輸出運算符“vv”是在頭文件ostream.h的classostream中定義的。從圖8.2可
以看出,classostream是從classios中派生出來的。因此下面先看一下用于定義class
ios的頭文件ios.ho
先看ios.h:
*ios.h-deHnitions/declarationsfortheiosclass.
***/
classios{
public:
enumio_state{goodbit=0x00,
eofbit=0x01,
failbit=0x02,
badbit=0x04};
enumopen_mode{;in=0x01,
out=0x02,
ate=0x04,
app=0x08,
trunc=0x10,
nocreate=0x20,
noreplace=0x40,
binary=0x80};
enumseek_dir{beg=0,cur=l,end=2};
enum{skipws=0x0001,
left=0x0002,
right=0x0004,
internal=0x0008,
dec=0x0010,
oct=0x0020,
hex=0x0040,
showbase=0x0080,
showpoint=0x0100,
uppercase=0x0200,
showpos=0x0400,
scientific=0x0800,
fixed=0x1000,
unitbuf=0x2000,
stdio=0x4000
staticconstlongbasefield;//dec|oct|hex
staticconstlongadjustfield;//left|right|internal
staticconstlongfloatfield;//scientific|fixed
ios(streambuf*);//differsfromANSI
virtual-ios();
inlinelongflags()const;
inlinelongflags(long_1);
inlinelongsetf(long_fJong_m);
inlinelongsetf(long_I);
inlinelongunsetf(long_I);
inlineintwidth()const;
inlineintwidth(int_i);
inlineostream*tie(ostream*_os);
inlineostream*tie()const;
inlinecharfill()const;
inlinecharfill(char_c);
inlineintprecision(int_i);
inlineintprecision()const;
//inlineoperatorvoid*()const;
operatorvoid*()const{if(state&(badbit|failbit))return0;return(void*)this;}
inlineintoperator!()const;
protected:
ios();
ios(constios&);//treatasprivate
intstate;
longx_flags;
intx_precision;
char
intx_width;
};
以后討論到有關(guān)classios的問題時,可參照以上內(nèi)容。
再看ostream.h:
/***
ostream.h-definitions/declarationsfortheostreamclass
classostream:virtualpublicios{
public:
ostream(streambuf*);
virtual?ostream。;
ostream&flush(........);
ostream&endl(........);
inlineostream&operator?(ostream&(_cdecl*_f)(ostream&));
inlineostream&operator?(ios&(_cdecl*_f)(ios&));
ostream&operator?(constchar*);
inlineostream&operator?(constunsignedchar*);
inlineostream&operator?(constsignedchar*);
inlineostream&operator?(char);
ostream&operator?(unsignedchar);
inlineostream&operator?(signedchar);
ostream&operator?(short);
ostream&operator?(unsignedshort);
ostream&operator?(int);
ostream&operator?(unsignedint);
ostream&operator?(long);
ostream&operator?(unsignedlong);
inlineostream&operator?(float);
ostream&operator?(doubie);
ostream&operator?(longdouble);
ostream&operator?(constvoid*);
ostream&operator?(streambuf*);
inlineostream&put(char);
ostream&put(unsignedchar);
inlineostream&put(signedchar);
ostream&write(constchar
inlineostream&write(constunsignedchar
inlineostream&write(constsignedchar
ostream&seekp(streampos);
ostream&seekp(streamoff,ios::seek_dir);
streampostellp();
protected:
ostream();
ostream(constostream&);//treatasprivate
ostream&operator=(streambuf*);//treatasprivate
ostream&operator=(constostream&_os){returnoperator=(_os.rdbuf());}
intdo_opfx(int);//notused
voiddo_osfx();//notused
private:
ostream(ios&);
ostream&writepad(constchar*,constchar*);
intx_floatused;
);
附注:
在C++的老版本中,_Cded是預(yù)定內(nèi)部宏,供編譯系統(tǒng)使用。當(dāng)它有定義(例如
等于1)時,標(biāo)示只選擇C++語言而不選擇Pascal語言。而另一個宏.Pascal有定義
時,則標(biāo)示同時選擇Pascal語言。
classostream_vvithassign:publicostream{
public:
ostream_withassign();
ostream_withassign(streambuf*Js);
-ostream_withassign();
ostream&operator=(constostream&_os){return
ostream::operator=(_os.rdbuf());}
ostream&operator=(streambu產(chǎn)_sb){returnostream::operator=(_sb);}
);
externostream_withassigncout;
externostream_withassigncerr;
externostream_withassignclog;
從以上文件看出,classostream中所定義的各個流輸出運算符是相對于各
個預(yù)定義數(shù)據(jù)類型進(jìn)行重載的。也即,他們可用于各種預(yù)定義數(shù)據(jù)類型。試看以下我
們很熟悉的例子:
[例1]用于三種最常用預(yù)定義數(shù)據(jù)類型的輸出運算符
//out_l.cpp
//copiedfromp.341ofWang*sbook
#include<iostream.h>
voidmain()
(
inti=10,j=45;
doublex=12,34,y=56.78;
char*str=nWmdowsn;
cout?ni=M?i?Mj=M?j?endI;
cout?nx=n?x?n,y=n?y?endl;
cout?Hstr=H?str?endl;
/*Results:
i=10J=45
x=12.34,y=56.78
str=Windows
刃
在以上程序中,輸出流對象cout順序地調(diào)用流輸出運算符“<<",將一條語句中的
多個不同類型的數(shù)據(jù)依次輸出。
輸出流對象cout還可用于輸出各類函數(shù)的運行結(jié)果,如下例:
[例2]輸出各類函數(shù)的運行結(jié)果
//out_2.cpp
#include<iostream.h>
intfunl()
intj=lll;
returnj;
)
doublefun2()
(
doublej=13.57;
returnj;
)
voidmain()
(
cout?HThefirstfunhasH?funl()?M;n
?nthesecondfunhasn?fun2()?endl;
)
/*Results:
Thefirstfunhas111;thesecondfunhas13.57
從圖8.2和ostream.h中可看出,從classostream中派生出class
ostream-withassign,而ostream-withassign建立了三個對象:cout、clog和ceir。這
就是標(biāo)準(zhǔn)庫中定義的三個輸出流對象。其中cout是標(biāo)準(zhǔn)的輸出流對象,而cerr和clog
則與標(biāo)準(zhǔn)錯誤流有關(guān)。其中,cout和clog是緩沖輸出流(burredoutputstream)
對象,發(fā)送給它們的數(shù)據(jù)暫存在緩存內(nèi),只當(dāng)滿足§8.123中所述條件之一時才將數(shù)
據(jù)輸出至標(biāo)準(zhǔn)設(shè)備;而cerr是非緩沖輸出流對象,發(fā)送給它的任何數(shù)據(jù)在執(zhí)行完一條
語句后立即輸出。
現(xiàn)在簡要地看一下輸出流對象cout、clog和cerr級聯(lián)地運行的過程。從以上圖8.2
和ostream.h中可看出:cout、clog和cerr都是classostream-withassign的對象,而
classostream-withassign是從classostream中派生出來的,因此classostream是這些
輸出流對象的基類。以coutvv為例,這是對象cout調(diào)用函數(shù)operatorvv(),也即
coutoperator?()o按照支配規(guī)則,也即對象cout調(diào)用了基類的成員函數(shù)operator
?()o函數(shù)operatorvv()原型的返回值是classostream對象的引用,而實際上則返
回classostream的派生類classostream-withassign的對象cout的引用。此返回的對
象可繼續(xù)調(diào)用下一個函數(shù)operator?()o直至整條語句末尾。
如只從表面看,有些現(xiàn)象看不清楚,見以下兩例:
[例1]變量值出錯
//out_10.cpp
//trapofcout,clogandcerrstatements
#include<iostream.h>
voidmain()
inti=10;
cout?nfirsti=M?i?n,secondi=n?i++?endl;
/*Results:
firsti=ll,secondi=10
*/
以上程序中,原來指望所顯示的兩個i都等于10,結(jié)果不然。為何?
[例2]第七章§7.1"程序運行錯誤及其處理”中[例1]程序sqrt_negative.Lcppo
該程序中主函數(shù)第二語句^cout?HSqrtof-4isn?sqroot(-4)?endl;w出錯,但并不
顯示字符串“Sqrtof-4is”。
[例3]有些輸出語句并不立即輸出數(shù)據(jù)。
//out_4.cpp
//Touseendl()functiontodisplayoutputcontents
#include<iostream.h>
#include<conio.h>//forgetch()
voidmain()
(
char*cl2=H12345H;
cout?cl2?HABCDH?cl2;
getch();
cout?MABCDEFGH?endl;
/"Resultsz
只在擊鍵二次之后,才能顯示以下內(nèi)容:
12345ABCD12345ABCDEFG
*/
以上程序中,主函數(shù)的第二語句運行完后,并無輸出,也即屏幕上并無顯示。何
故?這些都要求、也只能從匯編語言的更深層次來解釋。
整句cout、dog或cerr輸出語句的運行都可分為三個階段:入棧階段、緩存階段
和輸出階段。詳述如下。
第一階段…入棧階段
此階段由主函數(shù)main()完成。主函數(shù)按照逆順序?qū)⒄麠l輸出語句中需要輸出的各
項數(shù)據(jù)或數(shù)據(jù)的存放地址壓入棧區(qū)。其中:
(a)如果數(shù)據(jù)是單個數(shù)據(jù)(無論預(yù)定義類型或是用戶自定義類型),則直接將該項數(shù)
據(jù)壓入棧區(qū)。其中整型數(shù)據(jù)或單個字符占4個字節(jié)(堆棧按照4字節(jié)邊界原則運
行,4-byteboundary),double型數(shù)據(jù)占8個字節(jié),類的對象則可能占更多空間,
(b)如果數(shù)據(jù)是數(shù)組,則系統(tǒng)將該數(shù)組放置于數(shù)據(jù)區(qū)內(nèi),并將此數(shù)組首址壓入棧區(qū)。
(C)如果數(shù)據(jù)是一個函數(shù)運行后的返回值,則系統(tǒng)執(zhí)行該函數(shù),并根據(jù)該函數(shù)返回
只的數(shù)值類型,按照上兩項規(guī)定來決定將該數(shù)據(jù)或是數(shù)據(jù)地址壓入棧區(qū)。
第二階段一緩存階段
此階段由重載運算符函數(shù)<<完成。它取出棧區(qū)內(nèi)所存數(shù)據(jù)或按數(shù)據(jù)地址找到數(shù)據(jù)
本身,并將該項完整的數(shù)據(jù)存入位于堆區(qū)內(nèi)的緩存。如緩存是空的,則該數(shù)據(jù)被放置
于緩存的首址(例如§8.122"非緩沖輸出流”);如緩存內(nèi)已存有其它數(shù)據(jù),則該
數(shù)據(jù)被放置于緩存內(nèi)已有數(shù)據(jù)之后(例如§8.123“緩沖輸出流”)0
第三階段…輸出階段
此階段由另一些重載運算符函數(shù)<<調(diào)用flush函數(shù)來完成,它們最終調(diào)用硬件接
口的函數(shù)_imp_WriteFile,直接將數(shù)據(jù)輸出至標(biāo)準(zhǔn)設(shè)備(顯示屏幕)。由于函數(shù)
_imp_WriteFile無法訪問堆區(qū),因此必須再將數(shù)據(jù)從堆區(qū)內(nèi)再轉(zhuǎn)存至棧區(qū)內(nèi),才能
供函數(shù)_imp_WriteFile調(diào)用后輸出。flush函數(shù)順序地從緩存(堆區(qū))內(nèi)取出所存
全部數(shù)據(jù),從新壓入至棧區(qū)另外的地址,以備輸出。接著函數(shù)_imp_WriteFile直接
將位于棧區(qū)新地址處的全部數(shù)據(jù)輸出至標(biāo)準(zhǔn)設(shè)備(顯示屏幕)。
下面將分別介紹緩沖輸出流對象和非緩沖輸出流對象,它們在執(zhí)行輸出語句時的
差別在于:
(1)每當(dāng)非緩沖輸出流對象cerr語句運行時,在入棧階段之后,它調(diào)用重載運
算符函數(shù)接連執(zhí)行緩存階段和輸出階段,直接將全部數(shù)據(jù)輸出至標(biāo)準(zhǔn)設(shè)備(顯
示屏幕)。
(2)每當(dāng)緩沖輸出流對象cout或clog調(diào)用重載運算符函數(shù)<<時,一般情況下只
執(zhí)行入棧階段和緩存階段,只將數(shù)據(jù)存入緩存而不輸出數(shù)據(jù)。只當(dāng)滿足§8.123中所
述條件之一時才執(zhí)行輸出階段的操作,通過運算符函數(shù)從堆區(qū)中的緩存內(nèi)取出
所存全部數(shù)據(jù),調(diào)用用于控制硬件接口的函數(shù)_imp_WriteFile,直接將全部數(shù)據(jù)輸
出至標(biāo)準(zhǔn)設(shè)備(顯示屏幕)。
非緩沖輸出流的運行機理
cerr是非緩沖輸出流對象,發(fā)送給它的所有數(shù)據(jù)在整條輸出語句(不是半條語句)
運行后立即輸出至標(biāo)準(zhǔn)設(shè)備。
[例1]非緩沖輸出流對象cerr一次將整條語句的數(shù)據(jù)順序地輸出至標(biāo)準(zhǔn)設(shè)備。
//out_3.cpp
//''cerr''outputsthecontentsofonestatementimmediately
#include<iostream.h>
intfunl()
{
intj=512;
returnj;
)
doublefun2()
(
doublej=12.34;
returnj;
)
voidmain()
(
cerr?HThefirstresultisn?funl()?*;*
?nthesecondresultisn?fun2();
/*Results:
Thefirstresultis512;thesecondresultis12.34
*/
以上程序。ut_3.cpp中只有一句cen?輸出語句,整個語句的運行分為兩大步:第
一大步就是入棧階段;第二大步包括緩存階段和輸出階段。
第一大步…入棧階段
主函數(shù)按照逆順序?qū)⒄麠l語句中需要輸出的各項數(shù)據(jù)或數(shù)據(jù)的存放地址壓入棧
區(qū)。其順序操作如下:
(a)系統(tǒng)執(zhí)行函數(shù)fun2(),其運行結(jié)果為12.34。此數(shù)據(jù)被壓入棧區(qū),占8個字節(jié)。
(b)第二項被壓入棧區(qū)的數(shù)據(jù)是字符串“thesecondresultis”在數(shù)據(jù)區(qū)內(nèi)的地址
(0042a038)o
(c)第三項被壓入棧區(qū)的數(shù)據(jù)是字符「(其ASCH碼值為3Bh),占4個字節(jié)。
(d)系統(tǒng)接著執(zhí)行函數(shù)funl(),其運行結(jié)果512被壓入棧區(qū),占4個字節(jié)。
(e)第五項也即最后一項被壓入棧區(qū)的數(shù)據(jù)是字符串“thefirstresultis”在數(shù)據(jù)區(qū)
內(nèi)的地址(0042a01c)。
Table_out_l:程序out_3.cpp入棧階段后的棧區(qū)內(nèi)容
低地址0x0042a01c
0x0042a038
高地址
請注意:以上數(shù)據(jù)壓入棧區(qū)的順序是從輸出語句的末端開始的。
第二大步:緩存階段和輸出階段
非緩沖輸出流對象cerr是逐個地輸出數(shù)據(jù)的。對于每個數(shù)據(jù),非緩沖輸出流對
象cen?都調(diào)用運算符函數(shù)并一氣呵成地完成數(shù)據(jù)緩存和輸出的操作,直接將該
項數(shù)據(jù)輸出至標(biāo)準(zhǔn)設(shè)備(顯示屏幕)。
其詳細(xì)過程請參閱附錄二十六“非緩沖輸出流對象cerr輸出數(shù)據(jù)的詳細(xì)過程”。
由以上可見,在入棧階段中要求所有函數(shù)funl()和fun2()都正常運行,并返回運
行結(jié)果,然后才能進(jìn)入第二和第三階段。如果程序運行發(fā)生意外而導(dǎo)致程序中斷,則
整條輸出語句的數(shù)據(jù)將沒有一項能夠輸出或顯示。由于這個原因,第七章的多個程序
中,當(dāng)主函數(shù)的語句運行過程中拋出異常因而導(dǎo)致程序意外中斷時,整條語句的所有
數(shù)據(jù)都不能顯示。請見下節(jié)中[例5]和[例6]。
緩沖輸出流的運行機理
在緩沖輸出流中,只當(dāng)滿足以下四個條件中之一時,系統(tǒng)才把所存數(shù)據(jù)從緩存向
標(biāo)準(zhǔn)輸出設(shè)備(缺省為顯示終端屏幕)輸出:
(1)第一個條件:調(diào)用endl或flush函數(shù):
前面看到,flush和endl函數(shù)都是classostream的成員函數(shù)。此處flush函數(shù)用于
將輸出流刷新,從而將緩存的內(nèi)容輸出到與流相關(guān)的輸出設(shè)備中。而endl函數(shù)則調(diào)
用了flush函數(shù),除將輸出流刷新外,還執(zhí)行回車操作。
[例1]調(diào)用endl函數(shù)以便刷新緩存
//out_4.cpp
//Touseendl()functiontodisplayoutputcontents
#include<iostream.h>
#include<conio.h>//forgetch()
voidmain()
(
char*cl2=H12345H;
cout?cl2?nABCDH?cl2;
getch();
cout?HABCDEFGH?endl;
Results:
只在擊鍵二次之后,才能顯示以下內(nèi)容:
12345ABCD12345ABCDEFG
(這說明在調(diào)用endl函數(shù)之前,cout語句不輸出任何數(shù)據(jù))
*/
在out_4.cpp的例子中,共有兩條cout語句。其處理流程為:第一條cout語句不
調(diào)用endl函數(shù),只執(zhí)行前兩個階段,即入棧階段和緩存階段;第二條cout語句調(diào)用
endl函數(shù),除執(zhí)行前兩個階段外,還執(zhí)行輸出階段。
(-)入棧階段和緩存階段-在out_4.cpp的例子中:
(a)先處理第一條cout語句。主函數(shù)按照逆順序?qū)⒄麠l語句中需要輸出的三項數(shù)據(jù)
的存放地址壓入棧區(qū),如下:
Table_out_2:out_4.cpp第一條cout語句入棧階段后的棧區(qū)內(nèi)容
低地址0x0042a024(字符串“12345”地址
0x0042a038(字符串“ABCD”地蝦"
高地址0x0042a024(字符串“12345”地址廠
然后由輸出流對象cout調(diào)用重載運算符函數(shù)順序地分別按照棧區(qū)內(nèi)的地址取出
數(shù)據(jù)區(qū)內(nèi)字符串“12345"、字符串“ABCD”和字符串“12345”,并按順序存入緩存內(nèi)
(位于堆區(qū)內(nèi),其首址為p=0x00441810)。
(d)接著處理第二條cout語句。除執(zhí)行入棧階段和緩存階段外,還執(zhí)行輸出階段。
首先執(zhí)行入棧階段和緩存階段,主函數(shù)繼續(xù)按照逆順序?qū)⒄麠l語句中需要輸出的各項
數(shù)據(jù)或數(shù)據(jù)的存放地址壓入棧區(qū)。
Table_out_3:out_4.cpp第二條cout語句入棧階段后的棧區(qū)內(nèi)容
低地址0x0042a01c(字符串"ABCDEFG”地址)
0x0040101e(函數(shù)endl的地址)
高地址
接著輸出流對象cout調(diào)用重載運算符函數(shù)按照棧區(qū)內(nèi)的地址取出數(shù)據(jù)區(qū)內(nèi)字
符串"ABCDEFG”,存入緩存內(nèi)第三項數(shù)據(jù)之后。
(二)輸出階段:由于棧區(qū)內(nèi)最后一個所存數(shù)據(jù)是函數(shù)endl的地址(00401010,重
載運算符函數(shù)<<按此地址調(diào)用該函數(shù)。從而執(zhí)行第三階段即輸出階段。系統(tǒng)順序地
從緩存內(nèi)(位于堆區(qū)內(nèi),其首址為p=0x00441810)取出所存全部數(shù)據(jù),并將其全部
壓入棧區(qū)一定地址(在out_4.cpp程序中其首址在lfbuf=0012f97c處),以供函數(shù)
_imp_WriteFile調(diào)用輸出。
其詳細(xì)過程請參閱附錄二十七“緩沖輸出流對象調(diào)用endl函數(shù)的詳細(xì)過程”。
上例的第二條cout語句中,如用flush函數(shù)替代endl函數(shù),或者用clog替代cout,
程序的運行結(jié)果將完全相同。
(2)第二個條件:程序結(jié)束:
[例2]在程序結(jié)束之前,clog不輸出任何數(shù)據(jù)
//out_5.cpp
//Todisplayoutputcontentsattheendoftheprogram
#include<iostream.h>
#include<conio.h>//forgetch()
voidmain()
(
char*cl2=H12345
c!og?nABCDn?cl2?nABCDEFG
clog?cl2;
getchQ;
/*Resultsz
只在擊鍵二次之后,才能顯示以下內(nèi)容:
ABCD12345ABCDEFG12345
(這說明在程序結(jié)束之前,clog語句不輸出任何數(shù)據(jù))
*/
在以上程序。ut_5.cpp的數(shù)據(jù)輸出階段中,函數(shù)main()結(jié)束時,程序返回系統(tǒng)函
數(shù)mainCRTStartup()(系統(tǒng)是由此進(jìn)入函數(shù)main()的),并在此函數(shù)中調(diào)用函數(shù)exit。
然后幾經(jīng)周折,調(diào)用函數(shù)—write。系統(tǒng)取出堆區(qū)內(nèi)所存全部數(shù)據(jù)(out_5.cpp例中首
址在p=0x00441810),并將其全部壓入棧區(qū)一定地址(out_5.cpp例中其首址在
lfbuf=0x0012fadc處),以供函數(shù)_imp_WriteFile輸出,直接將所有數(shù)據(jù)輸出至標(biāo)
準(zhǔn)設(shè)備(顯示屏幕)。
(3)第三個條件:當(dāng)出現(xiàn)cerr語句或dn語句時:
[例3]在執(zhí)行非緩沖輸出流對象cerr語句前,cout不輸出任何數(shù)據(jù)
//out_6.cpp
//Nodisplayof"cout"until"cerr"statement
#include<iostream.h>
#include<conio.h>
voidmain()
(
char*cl2="12345
cerr?"ABCDEFG
cout?cl2?"ABCD
getch();
cerr?cl2;//No"endl"
)
/*Results:
ABCDEFG(擊鍵一次后繼續(xù)顯示)12345ABCD12345(但不回車)
*/
整個。ut_6.cpp程序包括三條輸出語句:兩條cerr語句(非緩沖輸出流對象)和
一條cout語句(緩沖輸出流對象)。
程序第一條輸出語句是cerr語句。整句cerr輸出語句的運行如同§8.122“非緩
沖輸出流"[例1]中程序out_3.cpp一樣。
程序第二條輸出語句是cout語句。整句cout輸出語句的運行只是執(zhí)行入棧階段
和緩存階段,其結(jié)果分別將字符串”12345“和“ABCD”存入緩存(首址在
0x00431570)內(nèi)。
程序第三條輸出語句是cerr語句。整句cerr輸出語句的運行分為三個階段:
在入棧階段中,主函數(shù)先將數(shù)據(jù)(字符串”12345")的地址壓入棧區(qū);在進(jìn)入緩
存階段之前,非緩沖輸出流對象cerr通過重載運算符函數(shù)<<調(diào)用flush函數(shù),將緩存
清空,因而將緩存內(nèi)全部數(shù)據(jù)(字符串“12345ABCD")轉(zhuǎn)存至棧區(qū),通過函數(shù)
_imp_WriteFile將數(shù)據(jù)輸出至標(biāo)準(zhǔn)設(shè)備(顯示屏幕),其結(jié)果是顯示字符串”12345
ABCD”。接著進(jìn)入正常的緩存和輸出階段,其操作和本程序中第一條cen?語句相同:
顯示字符串”12345"0
Tableout4:out6.cpp第2句即cerr語句入棧階段后的棧區(qū)內(nèi)容
低地址0x00426024(字符串"ABCDEFG”地址)
高地址--------------------------------------
Table...out.5out6.cpp第3句即cout語句入棧階段后的棧區(qū)內(nèi)容
低地址0x00426030(字符串”12345”地址)
-0x0042601c(字符串“ABCD”地址)
Tableout6:out6.cpp第5句即ceir語句入棧階段后的棧區(qū)內(nèi)容
低地址0x00426030(字符串”12345”地址)
高地址
其詳細(xì)過程請參閱附錄二十八”在執(zhí)行非緩沖輸出流對象cerr語句前,緩沖輸出
流對象cout不輸出任何數(shù)據(jù)”。
(4)第四個條件:緩存滿(一般為512個字節(jié))后自動將數(shù)據(jù)輸出至標(biāo)準(zhǔn)設(shè)備,并
清除緩存,準(zhǔn)備接收其它數(shù)據(jù):
[例4]512個字節(jié)的緩存滿后自動將數(shù)據(jù)輸出至顯示屏,并清除緩存
//out_7.cpp
//textsaredisplayedwhenthedataoverflowthebuffer
#include<iostream.h>
#include<conio.h>//forgetch()
voidmain()
(
for(intc=0;c<8;c++)
cout?HABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLM
NOPQRSTUVWXYZZYXWVUH;
//totally64*8=512characters
getch();
cout?endl;
getch();
cout?n12345H;
/*Results:
(第一條cout循環(huán)語句共有512個字符,正好是一個緩存的長度,但緩存雖滿,
尚未溢出,故不刷新,也不輸出數(shù)據(jù)。必須繼續(xù)存入數(shù)據(jù)(此處為回車符),
使緩存溢出方能刷新,因此必須擊鍵一次后方能顯示以下512個字符)
ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRS
TUVWXYZZYXWVUABCDEFGHIJKLMNOP
QRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUAB
CDEFGHIJKLMNOPQRSTUVWXYZZYXWVU
ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRS
TUVWXYZZYXWVUABCDEFGHIJKLMNOP
QRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUAB
CDEFGHIJKLMNOPQRSTUVWXYZZYXWVU
ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRS
TUVWXYZZYXWVUABCDEFGHIJKLMNOP
QRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUAB
CDEFGHIJKLMNOPQRSTUVWXYZZYXWVU
ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVU(又擊鍵一次后再次顯示:)
12345
*/
在以上程序out_7.cpp中,當(dāng)緩存剛好充滿時,運算符函數(shù)并不將緩存(首址為
0x00441880)刷新。只當(dāng)繼續(xù)存入數(shù)據(jù)(此處為回車符),使緩存溢出時,運算符函
數(shù)<<才將緩存(首址為0x00441880)刷新。也即將堆區(qū)內(nèi)地址0x00441880處所存全
部數(shù)據(jù)(共512個字節(jié))復(fù)制至棧區(qū)(lfbuf=0x0012f920)處,然后即輸出至標(biāo)準(zhǔn)設(shè)
備(顯示屏幕),顯示512個字符。最后一條cout語句只是將字符串“12345”壓入棧
區(qū),并接著將它們轉(zhuǎn)存入堆區(qū)(首址在0x00441880)。當(dāng)此cout語句調(diào)用endl函數(shù)
時,即調(diào)用flush函數(shù),刷新該堆區(qū),將字符串”12345”存入棧區(qū)(首址在0x0012fadc),
供函數(shù)_imp_WriteFile調(diào)用,將數(shù)據(jù)輸出至標(biāo)準(zhǔn)設(shè)備(顯示屏幕),加以顯示。
§8.122"非緩沖輸出流”末尾處提到:如果程序運行發(fā)生意外而導(dǎo)致中斷,則整
條輸出語句的數(shù)據(jù)將沒有一項能夠輸出或顯示。請見以下兩例。
[例5]程序未發(fā)生意外,cout正常顯示
//out_8.cpp
//ncoutndisplaysresultofoperationoffun()normally
#include<iostream.h>
intfun()
(
intj=45678;
returnj;
)
voidmain()
(
cout?Hfun()hasaresultofH?fun()?endl;
/*Results:
fun()hasaresultof45678
*/
[例6]程序發(fā)生意外,cout無任何顯示
//out_9.cpp
//''cout''doesnotdisplayanythingwhenfun()isterminatedabnormally
#include<iostream.h>
#include<stdlib.h>//forexit(l)
intfun()
(
intj=1234;
if(j==1234)exit(l);
returnj;
)
voidmain()
(
cout?Hfun()hasaresultofn?fun()?endl;
/*Results:
Nothingisdisplayed
緩沖輸入流的運行機理
流輸入運算符”是在頭文件istream.h的classistream中定義的。classistream
也是從classios中派生出來的。因此下面看一下用于定義classistream的頭文件
istream.h。
又看istream.h:
/***
istream.h?definitions/declarationsfortheistreamclass
***/
classistream:virtualpublicios{
public:
istream(streambuf*);
virtual-istream();
inlineistream&operator?(istream&(_cdecl*_f)(istream&));
inlineistream&operator?(ios&(_cdecl*_f)(ios&));
istream&operator?(char*);
inlineistream&operator?(unsignedchar*);
inlineistream&operator?(signedchar*);
istream&operator?(char&);
inlineistreain&operator?(unsignedchar&);
inlineistream&operator?(signedchar&);
istream&operator?(short&);
istream&operator?(unsignedshort&);
istream&operator?(int&);
istream&operator?(unsignedint&);
istream&operator?(long&);
istream&operator?(unsignedlong&);
istream&operator?(float&);
istream&operator?(double&);
istream&operator?(longdouble&);
istream&operator?(streambuf*);
intget();
inlineistream&get(charint,char='\n');
inlineistream&get(unsignedchar*,int,char=,\n,);
inlineistream&get(signedchar*,int,char='\n');
istream&get(char&);
inlineistream&get(unsignedchar&);
inlineistream&get(signedchar&);
istream&get(streambuf&,char='\n');
inlineistream&getline(char*,int,char='\n');
inlineistream&getline(unsignedchar*,int,char='\iT);
inlineistream&getline(signedchar*,int,char='\n');
inlineistream&ignore(int=l,int=EOF);
istream&read(char
inlineistream&read(unsignedchar
inlineistream&read(signedchar
intgcount()const{returnx_gcount;}
intpeek();
istream&putback(char);
intsync();
istream&seekg(streampos);
istream&seekg(streamoff,ios::seek_dir);
streampostellg();
voideatwhite();
protected:
istream();
istream(constistream&);//treatasprivate
istream&operator=(streambuf*_isb);//treatasprivate
istream&operator=(constistream&Js){returnoperator=(_is.rdbuf());}
istream&get(char*,int,int);
intdo_ipfx(int);
private:
istream(ios&);
intgetint(char*);
intgetdouble(char*,int);
int_fGline;
intx_gcount;
);
classistream_withassign:publicistream{
public:
istream_withassign();
istream_withassign(streambuf*);
-istream_withassign();
istream&operator=(constistream&_is){returnistream::operator=(_is);}
istream&operator=(streambuf*_isb){returnistream::operator=(_isb);}
);
externistream_withassigncin;
最后J暝便看一下iostream.h:
iostream.h-definitions/declarationsforiostreamclasses
***/
typedeflongstreamoff,streampos;
#include<ios.h>//Defineios.
#include<istream.h>//Defineistream.
#include<ostream.h>//Defineostream.
class_CRTIMPiostream:publicistream,publicostream{
public:
iostream(streambuf*);
virtual-iostream();
protected:
iostream();
iostream(constiostream&);
inlineiostream&operator=(streambuf*);
inlineiostream&operator=(iostream&);
private:
iostream(ios&);
iostream(istream&);
iostream(ostream&);
};
從圖8.2和istream.h中可看出,從classistream中派生出classistream-withassign,
而istream-withassign建立的一"對象就是"cin"。
cin是緩沖輸入流對象。
運算符“>>”在讀取數(shù)值時,被輸入的數(shù)值之間的缺省隔離符是空格符一或換行
符。當(dāng)輸入非有效字符(例如當(dāng)輸入整數(shù)值時,輸入的小數(shù)點即為非有效字符)時,
此非有效字符也被認(rèn)為是一個隔離符。
此時,上一個數(shù)值的輸入操作結(jié)束。如果此非有效字符正好是下一個輸入數(shù)值的
有效字符(例如下一次正好讀取浮點數(shù))則此非有效字符被存入緩存內(nèi),作為下一次
輸入操作的有效字符(見以下[例1]至[例4])0
但如果這個非有效字符并不是下一個輸入數(shù)值的有效字符(例如下一次仍然讀取
整數(shù)),則輸入操作失敗,將0輸入下一個變量,同時結(jié)束整個輸入操作(見以下[例
5])o
真正將數(shù)值輸入至變量中的操作必須在鍵入換行符后才開始。
空格符和回車符的使用:
空格符是數(shù)據(jù)隔離符(delimiter),只用于將數(shù)據(jù)輸入至緩存內(nèi);而回車符才用于
將數(shù)據(jù)從緩存中輸入至程序的數(shù)據(jù)變量中。
[例1]前一個數(shù)值的非有效字符是后一個數(shù)值的有效字符
//in_l.cpp
//Thenoneffectivecharacteroftheformervalue
//isaneffectivecharacterofthelattervalue
#include<iostream.h>
voidmain()
(
inti,j;
doubled;
cout?"Pleaseinputthreevalues:";
cin?i?j?d;
cout?i?""?j?""?d?endl;
/*Results:
Pleaseinputthreevalues:1234.5678
12340.56
*/
[例2]小數(shù)點在輸入浮點數(shù)時的作用
//in_2.cpp
//Theroleofadigitpointwhenreading
//infloatingvalues
#include<iostream.h>
voidmain()
(
doubled,e,f;
cout?nPleaseinput:";
cin?d?e?f;
cout?d?Mn?e?f?endl;
)
/*Results:
Pleaseinput:.5.6.7
1.20.30.4
[例3」]兩個連續(xù)的詞的輸入
〃in_6?cpp
IIfig04_12.cppofDeitels*bookwithmistakecorrected
//Treatingcharacterarraysasstrings
#include<iostream.h>
voidmain()
(
charstring[20];
cout?MEnterastring:n;
cin?string;
cout?”stringis:n?string?endl;
cout?HEnterastring:n;
cin?string;//readsagain
cout?u\nstringis:''?string?endl;
)
/*Results:
Enterastring:Hellogoodfriend
stringis:Hello
Enterastring:
stringis:good
切
[例3_2]輸入多串字符串
//in_3?cpp
//Readincharacters
#include<iostream.h>
voidmain()
charc[10],ch[10],cha[10];
cout?HPleaseinput:";
//spacekeyisadelimiter(隔離符)
cin?c?ch?cha;
cout?c?MM?ch?MH?cha?endl;
)
/*Results:
Pleaseinput:IamaChinese
Iama*/
[例3_3]輸入更多詞
//in_4.cpp
//Readincharacters
#include<iostream.h>
voidmain()
(
charc[10],ch[10],cha[10];
cout?nPleaseinput:'';//spacekeyisthedelimiter
cin?c?ch?cha;
cout?c?''''vvch?Mn?cha?endl;
cin?c?ch;
cout?c?''''?ch?endl;
)
/*Results:
Pleaseinput:!amaChinesestudent
Iama
Chinesestudent*/
[例4]空格和回車的不同功能
//in_5.cpp
//DifferentfunctionsofspaceandCR
#include<iostream.h>
voidmain()
(
inti9a[3];
cout?nPleaseinputintegers:
for(i=0;i<3;i++)cin?a[i];
for(i=0;i<3;i++)cout?a[i]?endl;
cout?HPleaseinputagain:
for(i=0;i<3;i++)cin?a[i];
for(i=0;i<3;i++)cout?a[i]?endl;
/*Twokindsofresults:
(l)Pleaseinputintegers:1234(CR)(CR即carriagereturn)
1
2
3
Pleaseinputagain:5678(CR)
4
5
6
(2)Pleaseinputintegers:1(CR)
2(CR)
3(CR)
1
2
3
Pleaseinputagain:縱CR)
5(CR)
6(CR)
4
5
6*/
與以上程序不同,當(dāng)上一個非有效字符不是下一個輸入數(shù)值的有效字符時,輸
入操作結(jié)束。
[例5]應(yīng)輸入整數(shù)值時,如誤輸入小數(shù)點或字符類型,則將0輸入至下一個變量,同
時結(jié)束整個輸入操作
//in_7.cpp
//Toshowthatwronginputtedvaluetypewillterminateinputoperation
#include<iostream.h>
voidmain()
(
inta=l,b=2,c=3,d=4;
cout?HOriginalvalues:n?a?*/?b?,/?c?,/?d?endl;
cout?Hinputfourintegers:n;
cin?a?b?c?d;
if(!cin)cout?nWrongtypeofinputteddatum!Valuesbecome:
elsecout?nInputtedvalues:n;
cout?a?,/?b?,/?c?,/?d?endl;
)
/*Twokindsofresults:
(l)correctoperation:
Originalvalues:1,2,3,4
inputfourintegers:11223344
Inputtedvalues:11,22,33,44
(2)incorrectoperation:
Originalvalues:1,2,3,4
inputfourintegers:12.345
Wrongtypeofinputteddatum!Valuesbecome:1,2,0,4
或者
Originalvalues:1,2,3,4
inputfourintegers:1p23
Wrongtypeofinputteddatum!Valuesbecome:1,0,3,4
*/
以上程序中,使用運算符函數(shù)“ios::operator!”檢驗輸入流對象“cin”的輸入操
作是否成功。cin的內(nèi)存存儲空間中有一個單元state,用于存儲與classios的公有數(shù)
據(jù)成員io_state相對應(yīng)的各位的數(shù)值。如輸入操作成功,則此state單元中設(shè)置goodbit
=0x00;如輸入操作失敗,則此state單元中設(shè)置failbit=0x02,顯示輸入操作出錯。
這使用classios的成員函數(shù)operator!(),如下:
inlineintios::operator!()const{returnstate&(badbit|failbit);}
當(dāng)輸入操作成功,state域內(nèi)badbit和failbit都為零,因此此重載運算符函數(shù)返回
零值。否則返回非零值,表示出錯。
應(yīng)該指出,cin用于讀取字符串時,用于存放所讀字符串的緩存空間必須是在棧
區(qū)或堆區(qū)內(nèi),而不應(yīng)在數(shù)據(jù)區(qū)內(nèi)。因數(shù)據(jù)區(qū)內(nèi)的字符串或字符數(shù)組都是常量數(shù)組,
不能更改,無法用于存放所讀字符串。請見以下程序:
[例6]//str_ptr_&_array_5.cpp
//Toseedifferentchar-pointershavingdifferentbehaviors
#include<iostr
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國糧油交易行業(yè)市場深度調(diào)研及競爭格局與投資策略研究報告
- 2025-2030年中國電阻帶和電阻管行業(yè)市場現(xiàn)狀供需分析及投資評估規(guī)劃分析研究報告
- 鄭州師范學(xué)院《城市更新與場所營造》2023-2024學(xué)年第二學(xué)期期末試卷
- 蘭州外語職業(yè)學(xué)院《婦幼保健學(xué)》2023-2024學(xué)年第二學(xué)期期末試卷
- 華東師范大學(xué)《地圖制圖學(xué)基礎(chǔ)》2023-2024學(xué)年第二學(xué)期期末試卷
- 吉利學(xué)院《軟件設(shè)計模式與體系結(jié)構(gòu)》2023-2024學(xué)年第二學(xué)期期末試卷
- 伊春職業(yè)學(xué)院《建筑會計》2023-2024學(xué)年第二學(xué)期期末試卷
- 七臺河職業(yè)學(xué)院《民事訴訟與解實務(wù)》2023-2024學(xué)年第二學(xué)期期末試卷
- 湖北孝感美珈職業(yè)學(xué)院《UI設(shè)計》2023-2024學(xué)年第二學(xué)期期末試卷
- 鐵嶺師范高等??茖W(xué)?!毒G色高分子材料》2023-2024學(xué)年第二學(xué)期期末試卷
- 單層鋼結(jié)構(gòu)廠房施工組織設(shè)計方案
- 項目盡職調(diào)查清單模板
- 唯物主義和經(jīng)驗批判主義研讀課件
- 環(huán)境保護和水土保持保證體系框圖
- 【審計工作底稿模板】FH應(yīng)付利息
- 眼部健康檢測與分析課件
- 專業(yè)碩士學(xué)位論文修改報告(二)
- 蘇州市建設(shè)工程造價計價解釋
- 煤礦機電設(shè)備春季預(yù)防性檢修計劃
- 2017年山東、臨沂爆炸事故案例分析
- 工貿(mào)企業(yè)安全管理臺賬資料
評論
0/150
提交評論