第八章輸入輸出流(inputoutputstream)的基本概念及原理_第1頁
第八章輸入輸出流(inputoutputstream)的基本概念及原理_第2頁
第八章輸入輸出流(inputoutputstream)的基本概念及原理_第3頁
第八章輸入輸出流(inputoutputstream)的基本概念及原理_第4頁
第八章輸入輸出流(inputoutputstream)的基本概念及原理_第5頁
已閱讀5頁,還剩55頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論